spacenavd-0.6/0000755000175000017500000000000012374256237013202 5ustar nuclearnuclearspacenavd-0.6/Makefile.in0000644000175000017500000000237312162430754015245 0ustar nuclearnuclearsrc = $(wildcard src/*.c) $(wildcard src/serial/*.c) $(wildcard src/magellan/*.c) hdr = $(wildcard src/*.h) $(wildcard src/serial/*.h) $(wildcard src/magellan/*.h) obj = $(src:.c=.o) dep = $(obj:.o=.d) bin = spacenavd ctl = spnavd_ctl CC = gcc INSTALL = install CFLAGS = -pedantic -Wall $(dbg) $(opt) -fno-strict-aliasing -I$(srcdir)/src -I/usr/local/include $(add_cflags) LDFLAGS = -L/usr/local/lib $(xlib) $(add_ldflags) $(bin): $(obj) $(CC) -o $@ $(obj) $(LDFLAGS) -include $(dep) tags: $(src) $(hdr) ctags $(src) $(hdr) %.o: $(srcdir)/%.c $(CC) $(CFLAGS) -c $< -o $@ %.d: $(srcdir)/%.c @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ .PHONY: clean clean: rm -f $(obj) $(bin) .PHONY: cleandep cleandep: rm -f $(dep) .PHONY: install install: $(bin) $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin $(INSTALL) -m 755 $(bin) $(DESTDIR)$(PREFIX)/bin/$(bin) $(INSTALL) -m 755 $(srcdir)/$(ctl) $(DESTDIR)$(PREFIX)/bin/$(ctl) cd $(srcdir) && ./setup_init --no-install # [ -d /etc/hal/fdi/policy ] && \ # $(INSTALL) -m 644 spacenav.fdi $(DESTDIR)/etc/hal/fdi/policy/spacenav.fdi .PHONY: uninstall uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/$(bin) rm -f $(DESTDIR)$(PREFIX)/bin/$(ctl) rm -f $(DESTDIR)/etc/hal/fdi/policy/spacenav.fdi # cd $(srcdir) && ./setup_init remove spacenavd-0.6/setup_init0000755000175000017500000000257611535046476015325 0ustar nuclearnuclear#!/bin/sh setup_sysv_init() { rlvl=`cat /etc/inittab | grep :initdefault: | sed 's/^id://; s/:init.*$//'` if [ $? != 0 -o -z "$rlvl" ]; then echo 'default runlevel detection failed.' rlvl=2 fi echo "selected runlevel: $rlvl" if [ "$1" = 'remove' ]; then echo 'removing sysv init script' rm -f /etc/init.d/spacenavd rm -f /etc/rc${rlvl}.d/S99spacenavd else echo 'setting up sysv init script' install -m 755 init_script /etc/init.d/spacenavd cd /etc/rc${rlvl}.d rm -f S99spacenavd ln -s ../init.d/spacenavd S99spacenavd fi } setup_bsd_init() { echo 'setting up bsd init' echo "BSD init setup not implemented yet, you'll have to do it manually." } if [ "$1" = '--no-install' ]; then echo echo --- Spacenavd installation complete --- echo To have spacenavd start automatically at bootup, you must add an appropriate echo "init script. Refer to your system's manual for details on how to do that." echo An example init script is available in the spacenavd source directory. echo If you wish to attempt and install an init script automatically, run ./setup_init echo exit 0 fi if [ -d /etc/init.d -a -d /etc/rc0.d ]; then setup_sysv_init $* exit 0 fi if [ -f /etc/rc -a -d /etc/rc.d ]; then setup_bsd_init $* exit 0 fi echo "You're either using a non-standard init or this detection failed." echo "You'll have to setup your init, to start spacenavd, manually." spacenavd-0.6/spnavd_ctl0000755000175000017500000000221511141453076015254 0ustar nuclearnuclear#!/bin/sh # this script, starts and stops the communication between spacenavd and the # local X server. (:0). if [ "$1" != 'x11' ]; then echo "the only valid control for the moment is x11 ($0 x11 start/stop)." exit 1 fi if [ -z "$2" ]; then echo 'you must specify either "start" or "stop".' exit 1 fi if [ "$2" = 'start' ]; then # check to see there is a local X server running. DISPLAY=":0" xdpyinfo >/dev/null 2>/dev/null if [ $? != 0 ]; then echo "You must have an X server running before starting up spacenavd-X11 events." exit 1 fi sig=-usr1 elif [ "$2" = "stop" ]; then sig=-usr2 else echo 'you must specify either "start" or "stop".' exit 1 fi # detect daemon's process id pid=`cat /var/run/spnavd.pid 2>/dev/null` if [ $? != 0 ]; then pid=`ps -e | grep spacenavd | awk '{ print $1 }'` if [ -z "$pid" ]; then echo 'spacenavd daemon is not running, nothing to do.' exit 1 fi fi kill $sig $pid if [ $? = 0 ]; then if [ $sig = '-usr1' ]; then echo 'signalled spacenavd, it should now start sending X events.' else echo 'signalled spacenavd to stop sending X events.' fi else echo 'sending signal to spacenavd failed.' fi spacenavd-0.6/spacenavd.sln0000644000175000017500000000156312167571175015672 0ustar nuclearnuclear Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spacenavd", "spacenavd.vcproj", "{C1F098DD-4900-4820-98D5-4601340C8F90}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C1F098DD-4900-4820-98D5-4601340C8F90}.Debug|Win32.ActiveCfg = Debug|Win32 {C1F098DD-4900-4820-98D5-4601340C8F90}.Debug|Win32.Build.0 = Debug|Win32 {C1F098DD-4900-4820-98D5-4601340C8F90}.Release|Win32.ActiveCfg = Release|Win32 {C1F098DD-4900-4820-98D5-4601340C8F90}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal spacenavd-0.6/init_script0000755000175000017500000000134211232220727015442 0ustar nuclearnuclear#!/bin/sh # # spacenavd free driver for 3Dconnexion 6dof devices # # chkconfig: 2345 99 99 # description: A free user space driver for 3Dconnexion input devices,\ # compatible with the proprietary 3dxsrv daemon. DAEMON=/usr/local/bin/spacenavd [ -x "$DAEMON" ] || exit 0 case "$1" in start) echo 'Starting spacenavd daemon' $DAEMON -v ;; stop) echo 'Stopping spacenavd daemon' # detect daemon's process id pid=`cat /var/run/spnavd.pid 2>/dev/null` if [ $? != 0 ]; then pid=`ps -e | grep spacenavd | awk '{ print $1 }'` if [ -z "$pid" ]; then echo 'spacenavd daemon is not running, nothing to do.' exit 1 fi fi kill $pid ;; reload|restart|force-reload) $0 stop && sleep 1 && $0 start ;; esac spacenavd-0.6/spacenavd.vcproj0000644000175000017500000000700212167571175016373 0ustar nuclearnuclear spacenavd-0.6/src/0000755000175000017500000000000012374256237013771 5ustar nuclearnuclearspacenavd-0.6/src/spnavd.h0000644000175000017500000000237112162430754015431 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SPNAVD_H_ #define SPNAVD_H_ #include "config.h" #include "cfgfile.h" #define SOCK_NAME "/var/run/spnav.sock" #define PIDFILE "/var/run/spnavd.pid" #define LOGFILE "/var/log/spnavd.log" /* Multiple devices support */ #ifndef MAX_DEVICES #define MAX_DEVICES 8 #endif #if defined(__cplusplus) || (__STDC_VERSION__ >= 199901L) #define INLINE inline #else /* not C++ or C99 */ #ifdef __GNUC__ #define INLINE __inline__ #else #define INLINE #endif #endif struct cfg cfg; int verbose; #endif /* SPNAVD_H_ */ spacenavd-0.6/src/cfgfile.c0000644000175000017500000003424112162430754015531 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2013 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "cfgfile.h" enum {TX, TY, TZ, RX, RY, RZ}; static const int def_axmap[] = {0, 2, 1, 3, 5, 4}; static const int def_axinv[] = {0, 1, 1, 0, 1, 1}; void default_cfg(struct cfg *cfg) { int i; cfg->sensitivity = 1.0; for(i=0; i<3; i++) { cfg->sens_trans[i] = cfg->sens_rot[i] = 1.0; } for(i=0; i<6; i++) { cfg->dead_threshold[i] = 2; } cfg->led = 1; cfg->grab_device = 1; for(i=0; i<6; i++) { cfg->invert[i] = def_axinv[i]; cfg->map_axis[i] = def_axmap[i]; } for(i=0; imap_button[i] = i; cfg->kbmap_str[i] = 0; cfg->kbmap[i] = 0; } cfg->repeat_msec = -1; for(i=0; idevname[i] = 0; cfg->devid[i][0] = cfg->devid[i][1] = -1; } } #define EXPECT(cond) \ do { \ if(!(cond)) { \ fprintf(stderr, "%s: invalid value for %s\n", __func__, key_str); \ continue; \ } \ } while(0) int read_cfg(const char *fname, struct cfg *cfg) { FILE *fp; char buf[512]; struct flock flk; int num_devid = 0; /*int num_devnames = 0;*/ default_cfg(cfg); if(!(fp = fopen(fname, "r"))) { fprintf(stderr, "failed to open config file %s: %s. using defaults.\n", fname, strerror(errno)); return -1; } /* aquire shared read lock */ flk.l_type = F_RDLCK; flk.l_start = flk.l_len = 0; flk.l_whence = SEEK_SET; while(fcntl(fileno(fp), F_SETLKW, &flk) == -1); while(fgets(buf, sizeof buf, fp)) { int isint, isfloat, ival, i, bnidx, axisidx; float fval; char *endp, *key_str, *val_str, *line = buf; while(*line == ' ' || *line == '\t') line++; if(!*line || *line == '\n' || *line == '\r' || *line == '#') { continue; } if(!(key_str = strtok(line, " =\n\t\r"))) { fprintf(stderr, "invalid config line: %s, skipping.\n", line); continue; } if(!(val_str = strtok(0, " =\n\t\r"))) { fprintf(stderr, "missing value for config key: %s\n", key_str); continue; } ival = strtol(val_str, &endp, 10); isint = (endp > val_str); fval = strtod(val_str, &endp); isfloat = (endp > val_str); if(strcmp(key_str, "repeat-interval") == 0) { EXPECT(isint); cfg->repeat_msec = ival; } else if(strcmp(key_str, "dead-zone") == 0) { EXPECT(isint); for(i=0; i<6; i++) { cfg->dead_threshold[i] = ival; } } else if(strcmp(key_str, "dead-zone-translation-x") == 0) { EXPECT(isint); cfg->dead_threshold[0] = ival; } else if(strcmp(key_str, "dead-zone-translation-y") == 0) { EXPECT(isint); cfg->dead_threshold[1] = ival; } else if(strcmp(key_str, "dead-zone-translation-z") == 0) { EXPECT(isint); cfg->dead_threshold[2] = ival; } else if(strcmp(key_str, "dead-zone-rotation-x") == 0) { EXPECT(isint); cfg->dead_threshold[3] = ival; } else if(strcmp(key_str, "dead-zone-rotation-y") == 0) { EXPECT(isint); cfg->dead_threshold[4] = ival; } else if(strcmp(key_str, "dead-zone-rotation-z") == 0) { EXPECT(isint); cfg->dead_threshold[5] = ival; } else if(strcmp(key_str, "sensitivity") == 0) { EXPECT(isfloat); cfg->sensitivity = fval; } else if(strcmp(key_str, "sensitivity-translation") == 0) { EXPECT(isfloat); cfg->sens_trans[0] = cfg->sens_trans[1] = cfg->sens_trans[2] = fval; } else if(strcmp(key_str, "sensitivity-translation-x") == 0) { EXPECT(isfloat); cfg->sens_trans[0] = fval; } else if(strcmp(key_str, "sensitivity-translation-y") == 0) { EXPECT(isfloat); cfg->sens_trans[1] = fval; } else if(strcmp(key_str, "sensitivity-translation-z") == 0) { EXPECT(isfloat); cfg->sens_trans[2] = fval; } else if(strcmp(key_str, "sensitivity-rotation") == 0) { EXPECT(isfloat); cfg->sens_rot[0] = cfg->sens_rot[1] = cfg->sens_rot[2] = fval; } else if(strcmp(key_str, "sensitivity-rotation-x") == 0) { EXPECT(isfloat); cfg->sens_rot[0] = fval; } else if(strcmp(key_str, "sensitivity-rotation-y") == 0) { EXPECT(isfloat); cfg->sens_rot[1] = fval; } else if(strcmp(key_str, "sensitivity-rotation-z") == 0) { EXPECT(isfloat); cfg->sens_rot[2] = fval; } else if(strcmp(key_str, "invert-rot") == 0) { if(strchr(val_str, 'x')) { cfg->invert[RX] = !def_axinv[RX]; } if(strchr(val_str, 'y')) { cfg->invert[RY] = !def_axinv[RY]; } if(strchr(val_str, 'z')) { cfg->invert[RZ] = !def_axinv[RZ]; } } else if(strcmp(key_str, "invert-trans") == 0) { if(strchr(val_str, 'x')) { cfg->invert[TX] = !def_axinv[TX]; } if(strchr(val_str, 'y')) { cfg->invert[TY] = !def_axinv[TY]; } if(strchr(val_str, 'z')) { cfg->invert[TZ] = !def_axinv[TZ]; } } else if(strcmp(key_str, "swap-yz") == 0) { int i, swap_yz = 0; if(isint) { swap_yz = ival; } else { if(strcmp(val_str, "true") == 0 || strcmp(val_str, "on") == 0 || strcmp(val_str, "yes") == 0) { swap_yz = 1; } else if(strcmp(val_str, "false") == 0 || strcmp(val_str, "off") == 0 || strcmp(val_str, "no") == 0) { swap_yz = 0; } else { fprintf(stderr, "invalid configuration value for %s, expected a boolean value.\n", key_str); continue; } } for(i=0; i<6; i++) { cfg->map_axis[i] = swap_yz ? i : def_axmap[i]; } } else if(sscanf(key_str, "axismap%d", &axisidx) == 1) { EXPECT(isint); if(axisidx < 0 || axisidx >= MAX_AXES) { fprintf(stderr, "invalid option %s, valid input axis numbers 0 - %d\n", key_str, MAX_AXES - 1); continue; } if(ival < 0 || ival >= 6) { fprintf(stderr, "invalid config value for %s, expected a number from 0 to 6\n", key_str); continue; } cfg->map_axis[axisidx] = ival; } else if(sscanf(key_str, "bnmap%d", &bnidx) == 1) { EXPECT(isint); if(bnidx < 0 || bnidx >= MAX_BUTTONS || ival < 0 || ival >= MAX_BUTTONS) { fprintf(stderr, "invalid configuration value for %s, expected a number from 0 to %d\n", key_str, MAX_BUTTONS); continue; } if(cfg->map_button[bnidx] != bnidx) { printf("warning: multiple mappings for button %d\n", bnidx); } cfg->map_button[bnidx] = ival; } else if(sscanf(key_str, "kbmap%d", &bnidx) == 1) { if(bnidx < 0 || bnidx >= MAX_BUTTONS) { fprintf(stderr, "invalid configuration value for %s, expected a number from 0 to %d\n", key_str, MAX_BUTTONS); continue; } if(cfg->kbmap_str[bnidx]) { printf("warning: multiple keyboard mappings for button %d: %s -> %s\n", bnidx, cfg->kbmap_str[bnidx], val_str); free(cfg->kbmap_str[bnidx]); } cfg->kbmap_str[bnidx] = strdup(val_str); } else if(strcmp(key_str, "led") == 0) { if(isint) { cfg->led = ival; } else { if(strcmp(val_str, "true") == 0 || strcmp(val_str, "on") == 0 || strcmp(val_str, "yes") == 0) { cfg->led = 1; } else if(strcmp(val_str, "false") == 0 || strcmp(val_str, "off") == 0 || strcmp(val_str, "no") == 0) { cfg->led = 0; } else { fprintf(stderr, "invalid configuration value for %s, expected a boolean value.\n", key_str); continue; } } } else if(strcmp(key_str, "grab") == 0) { if(isint) { cfg->grab_device = ival; } else { if(strcmp(val_str, "true") == 0 || strcmp(val_str, "on") == 0 || strcmp(val_str, "yes") == 0) { cfg->grab_device = 1; } else if(strcmp(val_str, "false") == 0 || strcmp(val_str, "off") == 0 || strcmp(val_str, "no") == 0) { cfg->grab_device = 0; } else { fprintf(stderr, "invalid configuration value for %s, expected a boolean value.\n", key_str); continue; } } } else if(strcmp(key_str, "serial") == 0) { strncpy(cfg->serial_dev, val_str, PATH_MAX); } else if(strcmp(key_str, "device-id") == 0) { unsigned int vendor, prod; if(sscanf(val_str, "%x:%x", &vendor, &prod) == 2) { cfg->devid[num_devid][0] = (int)vendor; cfg->devid[num_devid][1] = (int)prod; num_devid++; } else { fprintf(stderr, "invalid configuration value for %s, expected a vendorid:productid pair\n", key_str); continue; } } else { fprintf(stderr, "unrecognized config option: %s\n", key_str); } } /* unlock */ flk.l_type = F_UNLCK; flk.l_start = flk.l_len = 0; flk.l_whence = SEEK_SET; fcntl(fileno(fp), F_SETLK, &flk); fclose(fp); return 0; } int write_cfg(const char *fname, struct cfg *cfg) { int i, wrote_comment; FILE *fp; struct flock flk; if(!(fp = fopen(fname, "w"))) { fprintf(stderr, "failed to write config file %s: %s\n", fname, strerror(errno)); return -1; } /* aquire exclusive write lock */ flk.l_type = F_WRLCK; flk.l_start = flk.l_len = 0; flk.l_whence = SEEK_SET; while(fcntl(fileno(fp), F_SETLKW, &flk) == -1); fprintf(fp, "# sensitivity is multiplied with every motion (1.0 normal).\n"); fprintf(fp, "sensitivity = %.3f\n\n", cfg->sensitivity); fprintf(fp, "# separate sensitivity for rotation and translation.\n"); if(cfg->sens_trans[0] == cfg->sens_trans[1] && cfg->sens_trans[1] == cfg->sens_trans[2]) { fprintf(fp, "sensitivity-translation = %.3f\n", cfg->sens_trans[0]); } else { fprintf(fp, "sensitivity-translation-x = %.3f\n", cfg->sens_trans[0]); fprintf(fp, "sensitivity-translation-y = %.3f\n", cfg->sens_trans[1]); fprintf(fp, "sensitivity-translation-z = %.3f\n", cfg->sens_trans[2]); } if(cfg->sens_rot[0] == cfg->sens_rot[1] && cfg->sens_rot[1] == cfg->sens_rot[2]) { fprintf(fp, "sensitivity-rotation = %.3f\n", cfg->sens_rot[0]); } else { fprintf(fp, "sensitivity-rotation-x = %.3f\n", cfg->sens_rot[0]); fprintf(fp, "sensitivity-rotation-y = %.3f\n", cfg->sens_rot[1]); fprintf(fp, "sensitivity-rotation-z = %.3f\n", cfg->sens_rot[2]); } fputc('\n', fp); fprintf(fp, "# dead zone; any motion less than this number, is discarded as noise.\n"); if(cfg->dead_threshold[0] == cfg->dead_threshold[1] && cfg->dead_threshold[1] == cfg->dead_threshold[2] && cfg->dead_threshold[2] == cfg->dead_threshold[3] && cfg->dead_threshold[3] == cfg->dead_threshold[4] && cfg->dead_threshold[4] == cfg->dead_threshold[5]) { fprintf(fp, "dead-zone = %d\n", cfg->dead_threshold[0]); } else { fprintf(fp, "dead-zone-translation-x = %d\n", cfg->dead_threshold[0]); fprintf(fp, "dead-zone-translation-y = %d\n", cfg->dead_threshold[1]); fprintf(fp, "dead-zone-translation-z = %d\n", cfg->dead_threshold[2]); fprintf(fp, "dead-zone-rotation-x = %d\n", cfg->dead_threshold[3]); fprintf(fp, "dead-zone-rotation-y = %d\n", cfg->dead_threshold[4]); fprintf(fp, "dead-zone-rotation-z = %d\n", cfg->dead_threshold[5]); } fputc('\n', fp); fprintf(fp, "# repeat interval; non-deadzone events are repeated every so many milliseconds (-1 to disable)\n"); fprintf(fp, "repeat-interval = %d\n", cfg->repeat_msec); if(cfg->invert[0] != def_axinv[0] || cfg->invert[1] != def_axinv[1] || cfg->invert[2] != def_axinv[2]) { fprintf(fp, "# invert translations on some axes.\n"); fprintf(fp, "invert-trans = "); if(cfg->invert[0] != def_axinv[0]) fputc('x', fp); if(cfg->invert[1] != def_axinv[1]) fputc('y', fp); if(cfg->invert[2] != def_axinv[2]) fputc('z', fp); fputs("\n\n", fp); } if(cfg->invert[3] != def_axinv[3] || cfg->invert[4] != def_axinv[4] || cfg->invert[5] != def_axinv[5]) { fprintf(fp, "# invert rotations around some axes.\n"); fprintf(fp, "invert-rot = "); if(cfg->invert[3] != def_axinv[3]) fputc('x', fp); if(cfg->invert[4] != def_axinv[4]) fputc('y', fp); if(cfg->invert[5] != def_axinv[5]) fputc('z', fp); fputs("\n\n", fp); } fprintf(fp, "# swap translation along Y and Z axes\n"); fprintf(fp, "swap-yz = %s\n\n", cfg->map_axis[1] == def_axmap[1] ? "false" : "true"); wrote_comment = 0; for(i=0; imap_button[i] != i) { if(!wrote_comment) { fprintf(fp, "# button mappings\n"); wrote_comment = 1; } fprintf(fp, "bnmap%d = %d\n", i, cfg->map_button[i]); } } if(wrote_comment) { fputc('\n', fp); } wrote_comment = 0; for(i=0; ikbmap_str[i]) { if(!wrote_comment) { fprintf(fp, "# button to key mappings\n"); wrote_comment = 1; } fprintf(fp, "kbmap%d = %s\n", i, cfg->kbmap_str[i]); } } if(wrote_comment) { fputc('\n', fp); } if(!cfg->led) { fprintf(fp, "# disable led\n"); fprintf(fp, "led = 0\n\n"); } if(!cfg->grab_device) { fprintf(fp, "# Don't grab USB device\n"); fprintf(fp, "# Grabbing the device ensures that other programs won't be able to use it without\n"); fprintf(fp, "# talking to spacenavd. For instance some versions of Xorg will use the device to move\n"); fprintf(fp, "# the mouse pointer if we don't grab it.\n"); fprintf(fp, "# Set this to false if you want to use programs that try to talk to the device directly\n"); fprintf(fp, "# such as google earth, then follow FAQ 11 http://spacenav.sourceforge.net/faq.html#faq11\n"); fprintf(fp, "# to force the X server to ignore the device\n"); fprintf(fp, "grab = false\n\n"); } fprintf(fp, "# serial device\n"); fprintf(fp, "# Set this only if you have a serial device, and make sure you specify the\n"); fprintf(fp, "# correct device file (On linux usually: /dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0 ... etc).\n"); if(cfg->serial_dev[0]) { fprintf(fp, "serial = %s\n\n", cfg->serial_dev); } else { fprintf(fp, "#serial = /dev/ttyS0\n"); } fprintf(fp, "# custom list USB device ids to open if present\n"); fprintf(fp, "# (multiple entries can be listed)\n"); for(i=0; idevid[i][0] != -1 && cfg->devid[i][1] != -1) { fprintf(fp, "device-id = %x:%x\n", cfg->devid[i][0], cfg->devid[i][1]); } } /* unlock */ flk.l_type = F_UNLCK; flk.l_start = flk.l_len = 0; flk.l_whence = SEEK_SET; fcntl(fileno(fp), F_SETLK, &flk); fclose(fp); return 0; } spacenavd-0.6/src/dev_usb.c0000644000175000017500000000252112162151603015546 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2013 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "dev_usb.h" void free_usb_devices_list(struct usb_device_info *list) { while(list) { int i; struct usb_device_info *tmp = list; list = list->next; free(tmp->name); for(i=0; inum_devfiles; i++) { free(tmp->devfiles[i]); } free(tmp); } } void print_usb_device_info(struct usb_device_info *devinfo) { int i; printf("[%x:%x]: \"%s\" (", devinfo->vendorid, devinfo->productid, devinfo->name ? devinfo->name : "unknown"); for(i=0; inum_devfiles; i++) { printf("%s ", devinfo->devfiles[i]); } fputs(")\n", stdout); } spacenavd-0.6/src/event.c0000644000175000017500000001514312161355713015253 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2013 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include #include #include "event.h" #include "client.h" #include "proto_unix.h" #include "spnavd.h" #ifdef USE_X11 #include "proto_x11.h" #include "kbemu.h" #endif enum { MOT_X, MOT_Y, MOT_Z, MOT_RX, MOT_RY, MOT_RZ }; struct dev_event { spnav_event event; struct timeval timeval; struct device *dev; int pending; struct dev_event *next; }; static struct dev_event *add_dev_event(struct device *dev); static struct dev_event *device_event_in_use(struct device *dev); static void dispatch_event(struct dev_event *dev); static void send_event(spnav_event *ev, struct client *c); static unsigned int msec_dif(struct timeval tv1, struct timeval tv2); static struct dev_event *dev_ev_list = NULL; static struct dev_event *add_dev_event(struct device *dev) { struct dev_event *dev_ev, *iter; int i; if((dev_ev = malloc(sizeof *dev_ev)) == NULL) { return NULL; } dev_ev->event.motion.data = (int*)&dev_ev->event.motion.x; for(i=0; i<6; i++) dev_ev->event.motion.data[i] = 0; gettimeofday(&dev_ev->timeval, 0); dev_ev->dev = dev; dev_ev->next = NULL; if(dev_ev_list == NULL) return dev_ev_list = dev_ev; iter = dev_ev_list; while(iter->next) { iter = iter->next; } iter->next = dev_ev; return dev_ev; } /* remove_dev_event takes a device pointer as argument so that upon removal of * a device the pending event (if any) can be removed. */ void remove_dev_event(struct device *dev) { struct dev_event dummy; struct dev_event *iter; dummy.next = dev_ev_list; iter = &dummy; while(iter->next) { if(iter->next->dev == dev) { struct dev_event *ev = iter->next; iter->next = ev->next; if(verbose) { printf("removing pending device event of: %s\n", dev->path); } free(ev); } else { iter = iter->next; } } dev_ev_list = dummy.next; } static struct dev_event *device_event_in_use(struct device *dev) { struct dev_event *iter = dev_ev_list; while(iter) { if(iter->dev == dev) { return iter; } iter = iter->next; } return NULL; } /* process_input processes an device input event, and dispatches * spacenav events to the clients by calling dispatch_event. * relative inputs (INP_MOTION) are accumulated, and dispatched when * we get an INP_FLUSH event. Button events are dispatched immediately * and they implicitly flush any pending motion event. */ void process_input(struct device *dev, struct dev_input *inp) { int sign; struct dev_event *dev_ev; switch(inp->type) { case INP_MOTION: if(abs(inp->val) < cfg.dead_threshold[inp->idx] ) { inp->val = 0; } inp->idx = cfg.map_axis[inp->idx]; sign = cfg.invert[inp->idx] ? -1 : 1; inp->val = (int)((float)inp->val * cfg.sensitivity * (inp->idx < 3 ? cfg.sens_trans[inp->idx] : cfg.sens_rot[inp->idx - 3])); dev_ev = device_event_in_use(dev); if(verbose && dev_ev == NULL) printf("adding dev event for device: %s\n", dev->path); if(dev_ev == NULL && (dev_ev = add_dev_event(dev)) == NULL) { fprintf(stderr, "failed to get dev_event\n"); break; } dev_ev->event.type = EVENT_MOTION; dev_ev->event.motion.data = (int*)&dev_ev->event.motion.x; dev_ev->event.motion.data[inp->idx] = sign * inp->val; dev_ev->pending = 1; break; case INP_BUTTON: #ifdef USE_X11 /* check to see if we must emulate a keyboard event instead of a * retular button event for this button */ if(cfg.kbmap_str[inp->idx]) { if(!cfg.kbmap[inp->idx]) { cfg.kbmap[inp->idx] = kbemu_keysym(cfg.kbmap_str[inp->idx]); printf("mapping ``%s'' to keysym %d\n", cfg.kbmap_str[inp->idx], (int)cfg.kbmap[inp->idx]); } send_kbevent(cfg.kbmap[inp->idx], inp->val); break; } #endif dev_ev = device_event_in_use(dev); if(dev_ev && dev_ev->pending) { dispatch_event(dev_ev); dev_ev->pending = 0; } inp->idx = cfg.map_button[inp->idx]; /* button events are not queued */ { struct dev_event dev_button_event; dev_button_event.dev = dev; dev_button_event.event.type = EVENT_BUTTON; dev_button_event.event.button.press = inp->val; dev_button_event.event.button.bnum = inp->idx; dispatch_event(&dev_button_event); } /* to have them replace motion events in the queue uncomment next section */ /* dev_ev = add_dev_event(dev); * dev_ev->event.type = EVENT_BUTTON; * dev_ev->event.button.press = inp->val; * dev_ev->event.button.bnum = inp->idx; * dispatch_event(dev_ev); */ break; case INP_FLUSH: dev_ev = device_event_in_use(dev); if(dev_ev && dev_ev->pending) { dispatch_event(dev_ev); dev_ev->pending = 0; } break; default: break; } } int in_deadzone(struct device *dev) { int i; struct dev_event *dev_ev; if((dev_ev = device_event_in_use(dev)) == NULL) return -1; for(i=0; i<6; i++) { if(dev_ev->event.motion.data[i] != 0) return 0; } return 1; } void repeat_last_event(struct device *dev) { struct dev_event *dev_ev; if((dev_ev = device_event_in_use(dev)) == NULL) return; dispatch_event(dev_ev); } static void dispatch_event(struct dev_event *dev_ev) { struct client *c, *client_iter; int dev_idx; if(dev_ev->event.type == EVENT_MOTION) { struct timeval tv; gettimeofday(&tv, 0); dev_ev->event.motion.period = msec_dif(tv, dev_ev->timeval); dev_ev->timeval = tv; } dev_idx = get_device_index(dev_ev->dev); client_iter = first_client(); while(client_iter) { c = client_iter; client_iter = next_client(); if(get_client_device_index(c) <= dev_idx) /* use <= until API changes, else == */ send_event(&dev_ev->event, c); } } static void send_event(spnav_event *ev, struct client *c) { switch(get_client_type(c)) { #ifdef USE_X11 case CLIENT_X11: send_xevent(ev, c); break; #endif case CLIENT_UNIX: send_uevent(ev, c); break; default: break; } } static unsigned int msec_dif(struct timeval tv1, struct timeval tv2) { unsigned int ms1, ms2; ms1 = tv1.tv_sec * 1000 + tv1.tv_usec / 1000; ms2 = tv2.tv_sec * 1000 + tv2.tv_usec / 1000; return ms1 - ms2; } spacenavd-0.6/src/proto_unix.h0000644000175000017500000000202611403040606016327 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PROTO_UNIX_H_ #define PROTO_UNIX_H_ #include "config.h" #include "event.h" #include "client.h" int init_unix(void); void close_unix(void); int get_unix_socket(void); void send_uevent(spnav_event *ev, struct client *c); int handle_uevents(fd_set *rset); #endif /* PROTO_UNIX_H_ */ spacenavd-0.6/src/dev.h0000644000175000017500000000311512162430754014711 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2013 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SPNAV_DEV_H_ #define SPNAV_DEV_H_ #include #include "config.h" struct dev_input; #define MAX_DEV_NAME 256 struct device { int fd; void *data; char name[MAX_DEV_NAME]; char path[PATH_MAX]; int num_axes; int *minval, *maxval; /* input value range (default: -500, 500) */ int *fuzz; /* noise threshold */ void (*close)(struct device*); int (*read)(struct device*, struct dev_input*); void (*set_led)(struct device*, int); struct device *next; }; int init_devices(void); void remove_device(struct device *dev); int get_device_fd(struct device *dev); #define is_device_valid(dev) (get_device_fd(dev) >= 0) int get_device_index(struct device *dev); int read_device(struct device *dev, struct dev_input *inp); void set_device_led(struct device *dev, int state); struct device *get_devices(void); #endif /* SPNAV_DEV_H_ */ spacenavd-0.6/src/hotplug_linux.c0000644000175000017500000000543012127374572017037 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef __linux__ #include "config.h" #include #include #include #include #ifdef USE_NETLINK #include #include #include #endif #include "hotplug.h" #include "dev.h" #include "spnavd.h" #include "cfgfile.h" static int con_hotplug(void); static void poll_timeout(int sig); static int hotplug_fd = -1; static int poll_time, poll_pipe; int init_hotplug(void) { if(hotplug_fd != -1) { fprintf(stderr, "WARNING: calling init_hotplug while hotplug is running!\n"); return hotplug_fd; } if((hotplug_fd = con_hotplug()) == -1) { int pfd[2]; if(verbose) { printf("hotplug failed will resort to polling\n"); } if(pipe(pfd) == -1) { perror("failed to open polling self-pipe"); return -1; } poll_pipe = pfd[1]; hotplug_fd = pfd[0]; poll_time = 1; signal(SIGALRM, poll_timeout); alarm(poll_time); } return hotplug_fd; } void shutdown_hotplug(void) { if(hotplug_fd != -1) { close(hotplug_fd); hotplug_fd = -1; } if(poll_pipe != -1) { close(poll_pipe); poll_pipe = -1; } } int get_hotplug_fd(void) { return hotplug_fd; } int handle_hotplug(void) { char buf[512]; read(hotplug_fd, buf, sizeof buf); if(verbose) printf("\nhandle_hotplug called\n"); if (init_devices() == -1) return -1; return 0; } static int con_hotplug(void) { int s = -1; #ifdef USE_NETLINK struct sockaddr_nl addr; if((s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)) == -1) { perror("failed to open hotplug netlink socket"); return -1; } memset(&addr, 0, sizeof addr); addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); addr.nl_groups = -1; if(bind(s, (struct sockaddr*)&addr, sizeof addr) == -1) { perror("failed to bind to hotplug netlink socket"); close(s); return -1; } #endif /* USE_NETLINK */ return s; } static void poll_timeout(int sig) { signal(sig, poll_timeout); if(sig == SIGALRM) { if(poll_pipe != -1) { write(poll_pipe, &sig, 1); poll_time *= 2; alarm(poll_time); } } } #endif /* __linux__ */ spacenavd-0.6/src/hotplug.h0000644000175000017500000000165311752110171015612 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SPNAV_HOTPLUG_H_ #define SPNAV_HOTPLUG_H_ int init_hotplug(void); void shutdown_hotplug(void); int get_hotplug_fd(void); int handle_hotplug(void); #endif /* SPNAV_HOTPLUG_H_ */ spacenavd-0.6/src/xdetect_freebsd.c0000644000175000017500000000702411754722440017265 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2011 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #if defined(__FreeBSD__) || defined(__APPLE__) #include "config.h" #ifdef USE_X11 #include #include #include #include #include #include #include #include #include "proto_x11.h" #include "spnavd.h" static int kq = -1; static int fd_x11 = -1; static int fd_tmp = -1; int xdet_start(void) { struct timespec ts = {0, 0}; struct kevent kev; if((kq = kqueue()) == -1) { perror("failed to create kqueue"); return -1; } if((fd_x11 = open("/tmp/.X11-unix", O_RDONLY)) == -1) { if((fd_tmp = open("/tmp", O_RDONLY)) == -1) { perror("failed to open /tmp"); goto err; } } EV_SET(&kev, fd_x11 != -1 ? fd_x11 : fd_tmp, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, 0); if(kevent(kq, &kev, 1, 0, 0, &ts) == -1) { perror("failed to register kqueue event notification"); goto err; } if(verbose) { printf("waiting for the X socket file to appear\n"); } return kq; err: if(fd_x11 != -1) close(fd_x11); if(fd_tmp != -1) close(fd_tmp); if(kq != -1) close(kq); kq = -1; return -1; } void xdet_stop(void) { if(kq != -1) { if(verbose) { printf("stopping X watch\n"); } if(fd_x11 != -1) close(fd_x11); if(fd_tmp != -1) close(fd_tmp); close(kq); kq = fd_x11 = fd_tmp = -1; } } int xdet_get_fd(void) { return kq; } int handle_xdet_events(fd_set *rset) { struct kevent kev; struct timespec ts = {0, 0}; if(kq == -1 || !FD_ISSET(kq, rset)) { return -1; } if(kevent(kq, 0, 0, &kev, 1, &ts) <= 0) { return -1; } if(kev.ident == fd_tmp) { assert(fd_x11 == -1); /* try to open the socket dir, see if that was what was added to /tmp */ if((fd_x11 = open("/tmp/.X11-unix", O_RDONLY)) == -1) { return -1; } EV_SET(&kev, fd_x11, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, 0); if(kevent(kq, &kev, 1, 0, 0, &ts) == -1) { perror("failed to register kqueue event notification for /tmp/.X11-unix"); close(fd_x11); fd_x11 = -1; return -1; } /* successfully added the notification for /tmp/.X11-unix, now we * don't need the /tmp notification anymore. by closing the fd it's * automatically removed from the kqueue. */ close(fd_tmp); fd_tmp = -1; } else if(kev.ident == fd_x11) { int i; if(verbose) { printf("found X socket, will now attempt to connect to the X server\n"); } /* poll for approximately 30 seconds (well a bit more than that) */ for(i=0; i<30; i++) { sleep(1); if(init_x11() != -1) { /* done, we don't need the X socket notification any more */ close(fd_x11); fd_x11 = -1; return 0; /* success */ } } fprintf(stderr, "found X socket yet failed to connect\n"); } return -1; } #endif /* USE_X11 */ #else int spacenavd_xdetect_freebsd_shut_up_empty_source_warning; #endif /* __FreeBSD__ */ spacenavd-0.6/src/proto_x11.c0000644000175000017500000002231412147160444015763 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #ifdef USE_X11 #include #include #include #include #include #ifdef HAVE_ALLOCA_H #include #endif #ifdef HAVE_MALLOC_H #include #endif #include #include #include "proto_x11.h" #include "client.h" #include "spnavd.h" #include "xdetect.h" #include "kbemu.h" enum cmd_msg { CMD_NONE, CMD_APP_WINDOW = 27695, /* set client window */ CMD_APP_SENS /* set app sensitivity */ }; static int xerr(Display *dpy, XErrorEvent *err); static int xioerr(Display *dpy); static Display *dpy; static Window win; static Atom xa_event_motion, xa_event_bpress, xa_event_brelease, xa_event_cmd; /* XXX This stands in for the client sensitivity. Due to the * bad design of the original magellan protocol, we can't know * which client requested the sensitivity change, so we have * to keep it global for all X clients. */ static float x11_sens = 1.0; static jmp_buf jbuf; int init_x11(void) { int i, screen, scr_count; Window root; XSetWindowAttributes xattr; Atom wm_delete, cmd_type; XTextProperty tp_wname; XClassHint class_hint; char *win_title = "Magellan Window"; if(dpy) return 0; /* if the server started from init, it probably won't have a DISPLAY env var * so let's add a default one. */ if(!getenv("DISPLAY")) { putenv("DISPLAY=:0.0"); } /* ... also there won't be an XAUTHORITY env var, so set one up */ if(!getenv("XAUTHORITY")) { struct passwd *p = getpwuid(getuid()); char *home, *buf; if(!p || !p->pw_dir) { if(!p) { fprintf(stderr, "getpwuid failed: %s\n", strerror(errno)); } fprintf(stderr, "falling back to getting the home directory from the HOME env var...\n"); if(!(home = getenv("HOME"))) { fprintf(stderr, "HOME env var not found, using /tmp as a home directory...\n"); home = "/tmp"; } } else { home = p->pw_dir; } buf = alloca(strlen("XAUTHORITY=") + strlen(home) + strlen("/.Xauthority") + 1); sprintf(buf, "XAUTHORITY=%s/.Xauthority", home); putenv(buf); } if(verbose) { printf("trying to open X11 display \"%s\"\n", getenv("DISPLAY")); printf(" XAUTHORITY=%s\n", getenv("XAUTHORITY")); } if(!(dpy = XOpenDisplay(0))) { fprintf(stderr, "failed to open X11 display \"%s\"\n", getenv("DISPLAY")); xdet_start(); return -1; } XSetErrorHandler(xerr); XSetIOErrorHandler(xioerr); if(setjmp(jbuf)) { return -1; } scr_count = ScreenCount(dpy); screen = DefaultScreen(dpy); root = RootWindow(dpy, screen); /* intern the various atoms used for communicating with the magellan clients */ xa_event_motion = XInternAtom(dpy, "MotionEvent", False); xa_event_bpress = XInternAtom(dpy, "ButtonPressEvent", False); xa_event_brelease = XInternAtom(dpy, "ButtonReleaseEvent", False); xa_event_cmd = XInternAtom(dpy, "CommandEvent", False); /* Create a dummy window, so that clients are able to send us events * through the magellan API. No need to map the window. */ xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, screen); xattr.colormap = DefaultColormap(dpy, screen); win = XCreateWindow(dpy, root, 0, 0, 10, 10, 0, CopyFromParent, InputOutput, DefaultVisual(dpy, screen), CWColormap | CWBackPixel | CWBorderPixel, &xattr); wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols(dpy, win, &wm_delete, 1); XStringListToTextProperty(&win_title, 1, &tp_wname); XSetWMName(dpy, win, &tp_wname); XFree(tp_wname.value); class_hint.res_name = "magellan"; class_hint.res_class = "magellan_win"; XSetClassHint(dpy, win, &class_hint); /* I believe this is a bit hackish, but the magellan API expects to find the CommandEvent * property on the root window, containing our window id. * The API doesn't look for a specific property type, so I made one up here (MagellanCmdType). */ cmd_type = XInternAtom(dpy, "MagellanCmdType", False); for(i=0; itype) { case EVENT_MOTION: xevent.xclient.message_type = xa_event_motion; xevent.xclient.format = 16; for(i=0; i<6; i++) { float val = (float)ev->motion.data[i] * x11_sens; xevent.xclient.data.s[i + 2] = (short)val; } xevent.xclient.data.s[0] = xevent.xclient.data.s[1] = 0; xevent.xclient.data.s[8] = ev->motion.period; break; case EVENT_BUTTON: xevent.xclient.message_type = ev->button.press ? xa_event_bpress : xa_event_brelease; xevent.xclient.format = 16; xevent.xclient.data.s[2] = ev->button.bnum; break; default: break; } XSendEvent(dpy, get_client_window(c), False, 0, &xevent); XFlush(dpy); } int handle_xevents(fd_set *rset) { if(!dpy) { if(xdet_get_fd() != -1) { handle_xdet_events(rset); } return -1; } /* process any pending X events */ if(FD_ISSET(ConnectionNumber(dpy), rset)) { if(setjmp(jbuf)) { return 0; } while(XPending(dpy)) { XEvent xev; XNextEvent(dpy, &xev); if(xev.type == ClientMessage && xev.xclient.message_type == xa_event_cmd) { unsigned int win_id; switch(xev.xclient.data.s[2]) { case CMD_APP_WINDOW: win_id = xev.xclient.data.s[1]; win_id |= (unsigned int)xev.xclient.data.s[0] << 16; set_client_window((Window)win_id); break; case CMD_APP_SENS: x11_sens = *(float*)xev.xclient.data.s; /* see decl of x11_sens for details */ break; default: break; } } } } return 0; } /* adds a new X11 client to the list, IF it does not already exist */ void set_client_window(Window win) { int i, scr_count; struct client *cnode; /* When a magellan application exits, the SDK sets another window to avoid * crashing the original proprietary daemon. The new free SDK will set * consistently the root window for that purpose, which we can ignore here * easily. */ scr_count = ScreenCount(dpy); for(i=0; ierror_code == BadWindow) { /* we may get a BadWindow error when trying to send events to * clients that have disconnected in the meanwhile. */ remove_client_window((Window)err->resourceid); } else { XGetErrorText(dpy, err->error_code, buf, sizeof buf); fprintf(stderr, "Caught unexpected X error: %s\n", buf); } return 0; } /* X11 I/O error handler * This function must not return or xlib will abort. */ static int xioerr(Display *display) { fprintf(stderr, "Lost the X server!\n"); dpy = 0; close_x11(); xdet_start(); longjmp(jbuf, 1); return 0; } #else int spacenavd_proto_x11_shut_up_empty_source_warning; #endif /* USE_X11 */ spacenavd-0.6/src/proto_x11.h0000644000175000017500000000221311403040606015753 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PROTO_X11_H_ #define PROTO_X11_H_ #include "config.h" #include #include #include "event.h" #include "client.h" int init_x11(void); void close_x11(void); int get_x11_socket(void); void send_xevent(spnav_event *ev, struct client *c); int handle_xevents(fd_set *rset); void set_client_window(Window win); void remove_client_window(Window win); #endif /* PROTO_X11_H_ */ spacenavd-0.6/src/kbemu.c0000644000175000017500000000312611751134441015230 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #ifdef USE_X11 #include #include #include "kbemu.h" static Display *dpy; void kbemu_set_display(Display *d) { dpy = d; } KeySym kbemu_keysym(const char *str) { return XStringToKeysym(str); } void send_kbevent(KeySym key, int press) { XEvent xevent; Window win; int rev_state; if(!dpy) return; XGetInputFocus(dpy, &win, &rev_state); xevent.type = press ? KeyPress : KeyRelease; xevent.xkey.display = dpy; xevent.xkey.root = DefaultRootWindow(dpy); xevent.xkey.window = win; xevent.xkey.subwindow = None; xevent.xkey.keycode = XKeysymToKeycode(dpy, key); xevent.xkey.state = 0; xevent.xkey.time = CurrentTime; xevent.xkey.x = xevent.xkey.y = 1; xevent.xkey.x_root = xevent.xkey.y_root = 1; XSendEvent(dpy, win, True, press ? KeyPressMask : KeyReleaseMask, &xevent); XFlush(dpy); } #endif /* USE_X11 */ spacenavd-0.6/src/xdetect.h0000644000175000017500000000166111403040606015565 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef XDETECT_H_ #define XDETECT_H_ #include "config.h" int xdet_start(void); void xdet_stop(void); int xdet_get_fd(void); int handle_xdet_events(fd_set *rset); #endif /* XDETECT_H_ */ spacenavd-0.6/src/event.h0000644000175000017500000000314712127374572015267 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef EVENT_H_ #define EVENT_H_ #include "config.h" #include #include "dev.h" enum { EVENT_MOTION, EVENT_BUTTON /* includes both press and release */ }; struct event_motion { int type; int x, y, z; int rx, ry, rz; unsigned int period; int *data; }; struct event_button { int type; int press; int bnum; }; typedef union spnav_event { int type; struct event_motion motion; struct event_button button; } spnav_event; enum { INP_MOTION, INP_BUTTON, INP_FLUSH }; struct dev_input { int type; struct timeval tm; int idx; int val; }; void remove_dev_event(struct device *dev); void process_input(struct device *dev, struct dev_input *inp); /* non-zero if the last processed motion event was in the deadzone */ int in_deadzone(struct device *dev); /* dispatches the last event */ void repeat_last_event(struct device *dev); #endif /* EVENT_H_ */ spacenavd-0.6/src/serial/0000755000175000017500000000000012374256237015250 5ustar nuclearnuclearspacenavd-0.6/src/serial/sballserial.c0000644000175000017500000001064311403040606017674 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . This file incorporates work covered by the following copyright and permission notice: Copyright 1997-2001 John E. Stone (j.stone@acm.org) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _POSIX_SOURCE 1 #include #include #include #include #include #include #include "sballserial.h" /* protos and types for this file */ typedef struct { int fd; /* serial port device file descriptor */ } commstruct; int sball_comm_open(const char *commname, SBallCommHandle * commhandleptr) { struct termios sballtermio; commstruct *comm; *commhandleptr = NULL; comm = malloc(sizeof(commstruct)); if(comm == NULL) return -1; comm->fd = open(commname, O_RDWR | O_NONBLOCK | O_NOCTTY); if(comm->fd == -1) { free(comm); return -1; /* failed open of comm port */ } tcgetattr(comm->fd, &sballtermio); #if 0 /* TIOCEXCL exclusive access by this process */ #if defined(TIOCEXCL) if(ioctl(comm->fd, TIOCEXCL) < 0) { return -1; /* couldn't get exclusive use of port */ } #endif #endif sballtermio.c_lflag = 0; sballtermio.c_lflag = 0; sballtermio.c_iflag = IGNBRK | IGNPAR; sballtermio.c_oflag = 0; sballtermio.c_cflag = CREAD | CS8 | CLOCAL | HUPCL; sballtermio.c_cc[VEOL] = '\r'; sballtermio.c_cc[VERASE] = 0; sballtermio.c_cc[VKILL] = 0; sballtermio.c_cc[VMIN] = 0; sballtermio.c_cc[VTIME] = 0; /* use of baud rate in cflag is deprecated according to the */ /* single unix spec, also doesn't work in IRIX > 6.2 */ cfsetispeed(&sballtermio, B9600); cfsetospeed(&sballtermio, B9600); tcsetattr(comm->fd, TCSAFLUSH, &sballtermio); *commhandleptr = (SBallCommHandle) comm; return 0; } int sball_comm_write(SBallCommHandle commhandle, const char *buf) { commstruct *comm = (commstruct *) commhandle; if(comm == NULL) return -1; return write(comm->fd, buf, strlen(buf)); } int sball_comm_read(SBallCommHandle commhandle, char *buf, int sz) { commstruct *comm = (commstruct *) commhandle; if(comm == NULL) return -1; return read(comm->fd, buf, sz); } int sball_comm_close(SBallCommHandle * commhandleptr) { commstruct *comm = (commstruct *) (*commhandleptr); if(comm == NULL) return -1; close(comm->fd); free(*commhandleptr); *commhandleptr = NULL; return 0; } int sball_comm_fd(SBallCommHandle commhandle) { return ((commstruct *) commhandle)->fd; } /* end of unix code */ spacenavd-0.6/src/serial/sball.c0000644000175000017500000004742211403040606016501 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . This file incorporates work covered by the following copyright and permission notice: Copyright 1997-2001 John E. Stone (j.stone@acm.org) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _POSIX_SOURCE 1 #include #include #include #include #include #include "sball.h" #include "sballserial.h" struct event { struct dev_input data; struct event *next; }; static struct event *ev_free_list; int evpool_size; static struct event *alloc_event(void); static void free_event(struct event *ev); typedef struct { SBallCommHandle commhandle; unsigned char buf[256]; char resetstring[256]; int bufpos; /* current char position in packet buffer */ int packtype; /* what kind of packet is it */ int packlen; /* how many bytes do we ultimately expect? */ int escapedchar; /* if set, we're processing an escape sequence */ int erroroccured; /* if set, we've received an error packet or packets */ int resetoccured; /* if set, ball was reset, so have to reinitialize it */ int spaceball4000; /* if set, its a Spaceball 4000 */ int leftymode4000; /* if set, Spaceball 4000 in "lefty" orientation */ int trans[3]; /* last translational data received */ int rot[3]; /* last rotational data received */ int buttons; /* current button status */ int timer; /* time since last packet was received */ int usenullregion; /* software-implemented null region flag */ int nulltrans[3]; /* translational null region values */ int nullrot[3]; /* rotational null region values */ /* event list added for spacenavd integration */ struct event *evhead, *evtail; } sballhandle; static void generate_motion_events(sballhandle *handle, int *prev_val, int *new_val, int timer); static void generate_button_events(sballhandle *handle, int prevstate, int newstate); /* Spaceball 1003/2003 recommended initialization string. */ /* Newer documentation suggests eliminating several of these */ /* settings during initialization, leaving them at factory values. */ static char *initstring = "CB\rNT\rFTp\rFRp\rP@r@r\rMSSV\rZ\rBcCcC\r"; /* Reset spaceball and ideally determine model */ static void sball_hwreset(sballhandle * handle) { /* Reset some state variables back to zero */ handle->spaceball4000 = 0; /* re-determine which type it is */ handle->leftymode4000 = 0; /* re-determine if its in lefty mode */ if(!handle->resetoccured) { #if defined(DEBUG) printf("Sending reset command to spaceball...\n"); #endif handle->resetoccured = 1; sball_comm_write(handle->commhandle, "@\r"); /* force reset */ } #if 0 /* give the spaceball time to reset itself */ sleep(2); #endif #if defined(DEBUG) printf("Sending initialization sequence to spaceball...\n"); #endif sball_comm_write(handle->commhandle, initstring); /* do remaining init */ } SBallHandle sball_open(const char *sballname) { sballhandle *handle; if(sballname == NULL) return NULL; handle = (sballhandle *) malloc(sizeof(sballhandle)); if(handle == NULL) return NULL; /* clear all values in sballhandle to 0 */ memset(handle, 0, sizeof(sballhandle)); handle->packlen = 1; handle->resetoccured = 0; if(sball_comm_open(sballname, &handle->commhandle) == -1) { free(handle); return NULL; } sball_hwreset(handle); return handle; /* successfull open */ } int sball_close(SBallHandle voidhandle) { sballhandle *handle = voidhandle; if(handle == NULL) return -1; sball_comm_close(&handle->commhandle); free(handle); return 0; /* successfull close */ } static int sball_update(SBallHandle voidhandle) { int i, num, packs; unsigned char rawbuf[1024]; sballhandle *handle = voidhandle; if(handle == NULL) return -1; packs = 0; /* no packs received yet */ num = sball_comm_read(handle->commhandle, (char *)rawbuf, 1023); if(num > 0) { for(i = 0; i < num; i++) { /* process potentially occuring escaped character sequences */ if(rawbuf[i] == '^') { if(!handle->escapedchar) { handle->escapedchar = 1; continue; /* eat the escape character from buffer */ } } if(handle->escapedchar) { handle->escapedchar = 0; switch(rawbuf[i]) { case '^': /* leave char in buffer unchanged */ break; case 'Q': case 'S': case 'M': rawbuf[i] &= 0x1F; /* convert character to unescaped form */ break; default: #if defined(DEBUG) printf("\nGot a bad escape sequence! 0x%02x", rawbuf[i]); if(isprint(rawbuf[i])) printf(" (%c)", rawbuf[i]); else printf(" (unprintable)"); printf("\n"); #endif break; } } /* figure out what kind of packet we received */ if(handle->bufpos == 0) { switch(rawbuf[i]) { case 'D': /* Displacement packet */ handle->packtype = 'D'; handle->packlen = 16; /* D packets are 15 bytes long */ break; case 'K': /* Button/Key packet */ handle->packtype = 'K'; handle->packlen = 4; /* K packets are 3 bytes long */ break; case '.': /* Spaceball 4000 FLX "advanced" button press event */ handle->packtype = '.'; handle->packlen = 4; /* . packets are 3 bytes long */ break; case 'C': /* Communications mode packet */ handle->packtype = 'C'; handle->packlen = 4; break; case 'F': /* Spaceball sensitization mode packet */ handle->packtype = 'F'; handle->packlen = 4; break; case 'M': /* Movement mode packet */ handle->packtype = 'M'; handle->packlen = 5; break; case 'N': /* Null region packet */ handle->packtype = 'N'; handle->packlen = 3; break; case 'P': /* Update rate packet */ handle->packtype = 'P'; handle->packlen = 6; break; case '\v': /* XON at poweron */ handle->packtype = '\v'; handle->packlen = 1; break; case '\n': /* carriage return at poweron */ case '\r': /* carriage return at poweron */ handle->packtype = '\r'; handle->packlen = 1; break; case '@': /* Spaceball Hard/Soft Reset packet */ handle->resetoccured = 1; handle->packtype = '@'; handle->packlen = 62; /* Resets aren't longer than 62 chars */ break; case 'E': /* Error packet */ handle->packtype = 'E'; handle->packlen = 8; /* E packets are up to 7 bytes long */ break; case 'Z': /* Zero packet (Spaceball 2003/3003/4000 FLX) */ handle->packtype = 'Z'; handle->packlen = 14; /* Z packets are hardware dependent */ break; default: /* Unknown packet! */ #if defined(DEBUG) printf("\nUnknown packet (1): 0x%02x \n ", rawbuf[i]); printf(" char: "); if(isprint(rawbuf[i])) printf("%c", rawbuf[i]); else printf(" (unprintable)"); printf("\n"); #endif continue; } } handle->buf[handle->bufpos] = rawbuf[i]; handle->bufpos++; /* Reset packet processing */ if(handle->packtype == '@') { if(rawbuf[i] != '\r') continue; else handle->packlen = handle->bufpos; } /* Error packet processing */ if(handle->packtype == 'E') { if(rawbuf[i] != '\r') continue; else handle->packlen = handle->bufpos; } else if(handle->bufpos != handle->packlen) continue; switch(handle->packtype) { case 'D': /* ball displacement event */ /* modified by John Tsiombikas for spacenavd integration */ { unsigned int tx, ty, tz, rx, ry, rz; int i, prev_val[6], new_val[6]; /* number of 1/16ths of milliseconds since last */ /* ball displacement packet */ handle->timer = ((handle->buf[1]) << 8) | (handle->buf[2]); tx = ((handle->buf[3]) << 8) | ((handle->buf[4])); ty = ((handle->buf[5]) << 8) | ((handle->buf[6])); tz = ((handle->buf[7]) << 8) | ((handle->buf[8])); rx = ((handle->buf[9]) << 8) | ((handle->buf[10])); ry = ((handle->buf[11]) << 8) | ((handle->buf[12])); rz = ((handle->buf[13]) << 8) | ((handle->buf[14])); for(i=0; i<3; i++) { prev_val[i] = handle->trans[i]; prev_val[i + 3] = handle->rot[i]; } new_val[0] = (((int)tx) << 16) >> 16; new_val[1] = (((int)ty) << 16) >> 16; new_val[2] = (((int)tz) << 16) >> 16; new_val[3] = (((int)rx) << 16) >> 16; new_val[4] = (((int)ry) << 16) >> 16; new_val[5] = (((int)rz) << 16) >> 16; generate_motion_events(handle, prev_val, new_val, handle->timer); for(i=0; i<3; i++) { handle->trans[i] = new_val[i]; handle->rot[i] = new_val[i + 3]; } } break; case 'K': /* button press event */ /* modified by John Tsiombikas for spacenavd integration */ { int newstate; /* Spaceball 2003A, 2003B, 2003 FLX, 3003 FLX, 4000 FLX */ /* button packet. (4000 only for backwards compatibility) */ /* The lowest 5 bits of the first byte are buttons 5-9 */ /* Button '8' on a Spaceball 2003 is the rezero button */ /* The lowest 4 bits of the second byte are buttons 1-4 */ /* For Spaceball 2003, we'll map the buttons 1-7 normally */ /* skip 8, as its a hardware "rezero button" on that device */ /* and call the "pick" button "8". */ /* On the Spaceball 3003, the "right" button also triggers */ /* the "pick" bit. We OR the 2003/3003 rezero bits together */ /* if we have found a Spaceball 4000, then we ignore the 'K' */ /* packets entirely, and only use the '.' packets. */ if(handle->spaceball4000) break; newstate = ((handle->buf[1] & 0x10) << 3) | /* 2003 pick button is "8" */ ((handle->buf[1] & 0x20) << 9) | /* 3003 rezero button */ ((handle->buf[1] & 0x08) << 11) | /* 2003 rezero button */ ((handle->buf[1] & 0x07) << 4) | /* 5,6,7 (2003/4000) */ ((handle->buf[2] & 0x30) << 8) | /* 3003 Left/Right buttons */ ((handle->buf[2] & 0x0F)); /* 1,2,3,4 (2003/4000) */ generate_button_events(handle, handle->buttons, newstate); handle->buttons = newstate; } break; case '.': /* button press event (4000) */ /* modified by John Tsiombikas for spacenavd integration */ { int newstate; /* Spaceball 4000 FLX "expanded" button packet, with 12 buttons */ /* extra packet validity check, since we use this packet type */ /* to override the 'K' button packets, and determine if its a */ /* Spaceball 4000 or not... */ if(handle->buf[3] != '\r') { break; /* if not terminated with a '\r', probably garbage */ } /* if we got a valid '.' packet, this must be a Spaceball 4000 */ #if defined(DEBUG) if(!handle->spaceball4000) printf("\nDetected a Spaceball 4000 FLX\n"); #endif handle->spaceball4000 = 1; /* Must be talking to a Spaceball 4000 */ /* Spaceball 4000 series "expanded" button press event */ /* includes data for 12 buttons, and left/right orientation */ newstate = (((~handle->buf[1]) & 0x20) << 10) | /* "left handed" mode */ ((handle->buf[1] & 0x1F) << 7) | /* 8,9,10,11,12 */ ((handle->buf[2] & 0x3F)) | /* 1,2,3,4,5,6 (4000) */ ((handle->buf[2] & 0x80) >> 1); /* 7 (4000) */ generate_button_events(handle, handle->buttons, newstate); handle->buttons = newstate; #if defined(DEBUG) if(handle->leftymode4000 != ((handle->buf[1] & 0x20) == 0)) printf("\nSpaceball 4000 mode changed to: %s\n", (((handle->buf[1] & 0x20) == 0) ? "left handed" : "right handed")); #endif /* set "lefty" orientation mode if "lefty bit" is _clear_ */ if((handle->buf[1] & 0x20) == 0) handle->leftymode4000 = 1; /* left handed mode */ else handle->leftymode4000 = 0; /* right handed mode */ } break; case 'C': /* Communications mode packet */ case 'F': /* Spaceball sensitization packet */ case 'P': /* Spaceball update rate packet */ case 'M': /* Spaceball movement mode packet */ case 'N': /* Null region packet */ case '\r': /* carriage return at poweron */ case '\v': /* XON at poweron */ /* eat and ignore these packets */ break; case '@': /* Reset packet */ #ifdef DEBUG printf("Spaceball reset: "); for(j = 0; j < handle->packlen; j++) { if(isprint(handle->buf[j])) printf("%c", handle->buf[j]); } printf("\n"); #endif /* if we get a reset packet, we have to re-initialize */ /* the device, and assume that its completely schizophrenic */ /* at this moment, we must reset it again at this point */ handle->resetoccured = 1; sball_hwreset(handle); break; case 'E': /* Error packet, hardware/software problem */ handle->erroroccured++; #ifdef DEBUG printf("\nSpaceball Error!! "); printf("Error code: "); for(j = 0; j < handle->packlen; j++) { printf(" 0x%02x ", handle->buf[j]); } printf("\n"); #endif break; case 'Z': /* Zero packet (Spaceball 2003/3003/4000 FLX) */ /* We just ignore these... */ break; default: #ifdef DEBUG printf("Unknown packet (2): 0x%02x\n", handle->packtype); printf(" char: "); if(isprint(handle->packtype)) printf("%c", handle->packtype); else printf(" (unprintable)"); printf("\n"); #endif break; } /* reset */ handle->bufpos = 0; handle->packtype = 0; handle->packlen = 1; packs++; } } return packs; } int sball_rezero(SBallHandle voidhandle) { sballhandle *handle = voidhandle; char outbuf[200]; if(handle == NULL) return -1; sprintf(outbuf, "\rZ\r"); sball_comm_write(handle->commhandle, outbuf); return 0; } int sball_init(SBallHandle voidhandle) { sballhandle *handle = voidhandle; /*char outbuf[200]; */ if(handle == NULL) return -1; sball_update(handle); #if 0 sprintf(outbuf, "\r"); sball_update(handle); sball_comm_write(handle->commhandle, outbuf); sball_rezero(handle); #endif return 0; } void sball_set_nullregion(SBallHandle voidhandle, int nulltx, int nullty, int nulltz, int nullrx, int nullry, int nullrz) { sballhandle *handle = voidhandle; handle->nulltrans[0] = abs(nulltx); handle->nulltrans[1] = abs(nullty); handle->nulltrans[2] = abs(nulltz); handle->nullrot[0] = abs(nullrx); handle->nullrot[1] = abs(nullry); handle->nullrot[2] = abs(nullrz); handle->usenullregion = 1; } static int nullregion(int null, int val) { if(abs(val) > null) { return ((val > 0) ? (val - null) : (val + null)); } return 0; } static void sball_do_nullregion(SBallHandle voidhandle) { sballhandle *handle = voidhandle; handle->trans[0] = nullregion(handle->nulltrans[0], handle->trans[0]); handle->trans[1] = nullregion(handle->nulltrans[1], handle->trans[1]); handle->trans[2] = nullregion(handle->nulltrans[2], handle->trans[2]); handle->rot[0] = nullregion(handle->nullrot[0], handle->rot[0]); handle->rot[1] = nullregion(handle->nullrot[1], handle->rot[1]); handle->rot[2] = nullregion(handle->nullrot[2], handle->rot[2]); } int sball_getstatus(SBallHandle voidhandle, int *tx, int *ty, int *tz, int *rx, int *ry, int *rz, int *buttons) { sballhandle *handle = voidhandle; int events; if(handle == NULL) return -1; events = sball_update(handle); /* check for new data */ /* perform null region processing */ if(handle->usenullregion) sball_do_nullregion(voidhandle); if(tx != NULL) *tx = handle->trans[0]; if(ty != NULL) *ty = handle->trans[1]; if(tz != NULL) *tz = handle->trans[2]; if(rx != NULL) *rx = handle->rot[0]; if(ry != NULL) *ry = handle->rot[1]; if(rz != NULL) *rz = handle->rot[2]; if(buttons != NULL) *buttons = handle->buttons; /* no timer code yet */ return events; } /* everything from this point to the end of file was added by * John Tsiombikas for spacenavd integration. */ int sball_get_input(SBallHandle voidhandle, struct dev_input *inp) { struct event *ev; sballhandle *handle = voidhandle; /* read pending packets from the device and append them in the event list */ sball_update(handle); /* if there are any events in the list, grab the first and return it */ if((ev = handle->evhead)) { handle->evhead = handle->evhead->next; *inp = ev->data; free_event(ev); return 1; } return 0; } int sball_get_fd(SBallHandle voidhandle) { sballhandle *sball = voidhandle; return sball_comm_fd(sball->commhandle); } static struct event *alloc_event(void) { struct event *ev; if(ev_free_list) { ev = ev_free_list; ev_free_list = ev->next; } else { ev = malloc(sizeof *ev); evpool_size++; } return ev; } static void free_event(struct event *ev) { if(evpool_size > 512) { free(ev); evpool_size--; } else { ev->next = ev_free_list; ev_free_list = ev; } } static void generate_motion_events(sballhandle *handle, int *prev_val, int *new_val, int timer) { int i, pending = 0; struct event *ev; for(i=0; i<6; i++) { if(prev_val[i] == new_val[i]) { continue; } if((ev = alloc_event())) { ev->data.type = INP_MOTION; ev->data.idx = i; ev->data.val = new_val[i]; ev->next = 0; if(handle->evhead) { handle->evtail->next = ev; handle->evtail = ev; } else { handle->evhead = handle->evtail = ev; } pending = 1; } } if(pending) { if((ev = alloc_event())) { ev->data.type = INP_FLUSH; ev->next = 0; } if(handle->evhead) { handle->evtail->next = ev; handle->evtail = ev; } else { handle->evhead = handle->evtail = ev; } } } static void generate_button_events(sballhandle *handle, int prevstate, int newstate) { int i; for(i=0; i<16; i++) { int newbit = (newstate >> i) & 1; if(newbit != ((prevstate >> i) & 1)) { /* state changed, trigger event */ struct event *ev = alloc_event(); if(!ev) continue; ev->data.type = INP_BUTTON; ev->data.idx = i; ev->data.val = newbit; ev->next = 0; if(handle->evhead) { handle->evtail->next = ev; handle->evtail = ev; } else { handle->evhead = handle->evtail = ev; } } } } spacenavd-0.6/src/serial/sballserial.h0000644000175000017500000000576511403040606017712 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . This file incorporates work covered by the following copyright and permission notice: Copyright 1997-2001 John E. Stone (j.stone@acm.org) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Machine/OS dependent serial port I/O routines. * * sball_comm_open() - open the serial port device for communication with * the sball. Settings are 9600,N,8,1, non-blocking, * no controlling tty. * sball_comm_read() - nonblocking read of up to size bytes * sball_comm_write() - blocking write of up to size bytes * sball_comm_close() - close the serial port device */ typedef void *SBallCommHandle; int sball_comm_open(const char *commname, SBallCommHandle * commhandleptr); int sball_comm_write(SBallCommHandle commhandle, const char *buf); int sball_comm_read(SBallCommHandle commhandle, char *buf, int sz); int sball_comm_close(SBallCommHandle * commhandleptr); int sball_comm_fd(SBallCommHandle commhandle); spacenavd-0.6/src/serial/sball.h0000644000175000017500000001624511403040606016505 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . This file incorporates work covered by the following copyright and permission notice: Copyright 1997-2001 John E. Stone (j.stone@acm.org) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(SBALL_H) #define SBALL_H 1 #include "event.h" #ifdef __cplusplus extern "C" { #endif typedef void *SBallHandle; /* Handle type, used by all sball API functions */ /* Spaceball Button bit-masks */ #define SBALL_BUTTON_1 1 /* bit 0 */ #define SBALL_BUTTON_2 2 /* bit 1 */ #define SBALL_BUTTON_3 4 /* bit 2 */ #define SBALL_BUTTON_4 8 /* bit 3 */ #define SBALL_BUTTON_5 16 /* bit 4 */ #define SBALL_BUTTON_6 32 /* bit 5 */ #define SBALL_BUTTON_7 64 /* bit 6 */ #define SBALL_BUTTON_8 128 /* bit 7 */ #define SBALL_BUTTON_9 256 /* bit 8 */ #define SBALL_BUTTON_10 512 /* bit 9 */ #define SBALL_BUTTON_11 1024 /* bit 10 */ #define SBALL_BUTTON_12 2048 /* bit 11 */ /* The Spaceball 3003 and 3003 FLX only have "left" and "right" buttons */ #define SBALL_BUTTON_LEFT 4096 /* bit 12 */ #define SBALL_BUTTON_RIGHT 8192 /* bit 13 */ /* The Spaceball 2003A and 2003B have a dedicated pick button on the ball */ /* The Spaceball 2003 FLX uses "button 9" as the pick button. */ /* All of them return this as "button 9" in their encoded button data */ #define SBALL_BUTTON_PICK 128 /* bit 8 */ /* On Spaceball 2003A and 2003B, the Rezero is "button 8" on the device */ /* On the newer devices, there are dedicated rezero buttons */ #define SBALL_BUTTON_REZERO 16384 /* bit 14 */ /* The Spaceball 4000 FLX has a configurable palm rest which can be in */ /* either "left" or "right" handed mode. When it is configured in "left" */ /* handed mode, the "lefty" bit is set, and coordinate systems need to be */ /* inverted on one axis. */ #define SBALL_MODE_LEFTY 32768 /* bit 15 */ /* * sball_open() * Open a named serial port which a Spaceball is attached to. * Returns a handle which is used by all other sball API functions. * If the serial port open fails, or the sball does not pass initialization * tests, then a NULL is returned as the handle. */ SBallHandle sball_open(const char *sballname); /* * sball_close() * Closes down the Spaceball serial port, frees allocated resources and * discards any unprocessed sball messages. */ int sball_close(SBallHandle voidhandle); /* * sball_getstatus() * Polls the Spaceball serial port for new packets, performs any optional * postprocessing of Spaceball data such as null-region, scaling, and * value clamping. The most recent values for translation, rotation and * buttons are stored in the memory locations supplied by the caller. * Returns the number of events processed. If the number of events returned * is less than 1, either an error occured or there were no Spaceball * events to process. */ int sball_getstatus(SBallHandle voidhandle, int *tx, int *ty, int *tz, int *rx, int *ry, int *rz, int *buttons); /* sball_get_input() - Added for spacenavd integration by John Tsiombikas. * * returns the first of any pending events through inp. * returns 1 if it got an event, 0 if there where none pending */ int sball_get_input(SBallHandle voidhandle, struct dev_input *inp); /* sball_get_fd() - Added for spacenavd integration by John Tsiombikas. * * retreives the device file descriptor */ int sball_get_fd(SBallHandle voidhandle); /* * sball_rezero() * Forces the Orb to re-zero itself at the present twist/position. * All future event data is relative to this zero point. */ int sball_rezero(SBallHandle voidhandle); /* * sball_init() * Performs a software re-initialization of the Spaceball, clearing * all unprocessed events. Initialization also forces the Orb to re-zero * itself. */ int sball_init(SBallHandle voidhandle); /* * sball_set_nullregion() * Enables null-region processing on Spaceball output. * The null-region is the area (centered at 0) around which * each coordinate will report zero even when the Spaceball itself * reports a number whose absolute value is less than the null region * value for that coordinate. For example, if the null region on the * X translation coordinate is set to 50, all sball_getstatus() would report * 0 if X is less than 50 and greater than -50. If X is 51, sball_getstatus * would report 1. If X is -51, sball_getstatus() would report -1. * Null-regions help novice users gradually become accustomed to the * incredible sensitivity of the Spaceball, and make some applications * significantly easier to control. A resonable default nullregion for all * six axes is 65. Null regions should be tunable by the user, since its * likely that not all Spaceballs are quite identical, and it is guaranteed * that users have varying levels of manual dexterity. * Note that setting the null-region too high significantly reduces the * dynamic range of the output values from the Spaceball. */ void sball_set_nullregion(SBallHandle voidhandle, int nulltx, int nullty, int nulltz, int nullrx, int nullry, int nullrz); #ifdef __cplusplus } #endif #endif spacenavd-0.6/src/dev.c0000644000175000017500000001225312305113415014676 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2013 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include #include #include #include #include "dev.h" #include "dev_usb.h" #include "dev_serial.h" #include "event.h" /* remove pending events upon device removal */ #include "spnavd.h" static struct device *add_device(void); static struct device *dev_path_in_use(char const * dev_path); static int match_usbdev(const struct usb_device_info *devinfo); static struct device *dev_list = NULL; int init_devices(void) { struct device *dev; int i, device_added = 0; struct usb_device_info *usblist, *usbdev; /* try to open a serial device if specified in the config file */ if(cfg.serial_dev[0]) { if(!dev_path_in_use(cfg.serial_dev)) { dev = add_device(); strcpy(dev->path, cfg.serial_dev); if(open_dev_serial(dev) == -1) { remove_device(dev); } else { strcpy(dev->name, "serial device"); printf("using device: %s\n", cfg.serial_dev); device_added++; } } } /* detect any supported USB devices */ usblist = find_usb_devices(match_usbdev); usbdev = usblist; while(usbdev) { for(i=0; inum_devfiles; i++) { if((dev = dev_path_in_use(usbdev->devfiles[i]))) { if(verbose) { fprintf(stderr, "already using device: %s (%s)\n", dev->name, dev->path); } break; } dev = add_device(); strcpy(dev->path, usbdev->devfiles[i]); if(open_dev_usb(dev) == -1) { remove_device(dev); } else { printf("using device: %s\n", dev->path); device_added++; break; } } usbdev = usbdev->next; } free_usb_devices_list(usblist); if(!device_added) { fprintf(stderr, "failed to find any supported devices\n"); return -1; } return 0; } static struct device *add_device(void) { struct device *dev; if(!(dev = malloc(sizeof *dev))) { return 0; } memset(dev, 0, sizeof *dev); printf("adding device.\n"); dev->fd = -1; dev->next = dev_list; dev_list = dev; return dev_list; } void remove_device(struct device *dev) { struct device dummy; struct device *iter; printf("removing device: %s\n", dev->name); dummy.next = dev_list; iter = &dummy; while(iter->next) { if(iter->next == dev) { iter->next = dev->next; break; } iter = iter->next; } dev_list = dummy.next; remove_dev_event(dev); if(dev->close) { dev->close(dev); } free(dev); } static struct device *dev_path_in_use(char const *dev_path) { struct device *iter = dev_list; while(iter) { if(strcmp(iter->path, dev_path) == 0) { return iter; } iter = iter->next; } return 0; } int get_device_fd(struct device *dev) { return dev ? dev->fd : -1; } int get_device_index(struct device *dev) { struct device *iter = dev_list; int index = 0; while(iter) { if(dev == iter) { return index; } index++; iter = iter->next; } return -1; } int read_device(struct device *dev, struct dev_input *inp) { if(dev->read == NULL) { return -1; } return dev->read(dev, inp); } void set_device_led(struct device *dev, int state) { if(dev->set_led) { dev->set_led(dev, state); } } struct device *get_devices(void) { return dev_list; } static int devid_list[][2] = { /* 3Dconnexion devices */ {0x46d, 0xc603}, /* spacemouse plus XT */ {0x46d, 0xc605}, /* cadman */ {0x46d, 0xc606}, /* spacemouse classic */ {0x46d, 0xc621}, /* spaceball 5000 */ {0x46d, 0xc623}, /* space traveller */ {0x46d, 0xc625}, /* space pilot */ {0x46d, 0xc626}, /* space navigator */ {0x46d, 0xc627}, /* space explorer */ {0x46d, 0xc628}, /* space navigator for notebooks*/ {0x46d, 0xc629}, /* space pilot pro*/ {0x46d, 0xc62b}, /* space mouse pro*/ {0x46d, 0xc640}, /* nulooq */ {-1, -1} }; static int match_usbdev(const struct usb_device_info *devinfo) { int i; /* if it's a 3Dconnexion device match it immediately */ if((devinfo->name && strstr(devinfo->name, "3Dconnexion"))) { return 1; } /* match any device in the devid_list */ if(devinfo->vendorid != -1 && devinfo->productid != -1) { for(i=0; devid_list[i][0] > 0; i++) { if(devinfo->vendorid == devid_list[i][0] && devinfo->productid == devid_list[i][1]) { return 1; } } } /* match any joystick devices listed in the config file */ for(i=0; ivendorid && (unsigned int)cfg.devid[i][1] == devinfo->productid) { return 1; } if(cfg.devname[i] && devinfo->name && strcmp(cfg.devname[i], devinfo->name) == 0) { return 1; } } return 0; /* no match */ } spacenavd-0.6/src/dev_serial.c0000644000175000017500000000267412127374572016262 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include "dev_serial.h" #include "dev.h" #include "event.h" #include "serial/sball.h" static void close_dev_serial(struct device *dev); static int read_dev_serial(struct device *dev, struct dev_input *inp); int open_dev_serial(struct device *dev) { if(!(dev->data = sball_open(dev->path))) { return -1; } dev->fd = sball_get_fd(dev->data); dev->close = close_dev_serial; dev->read = read_dev_serial; return 0; } static void close_dev_serial(struct device *dev) { if(dev->data) { sball_close(dev->data); } dev->data = 0; } static int read_dev_serial(struct device *dev, struct dev_input *inp) { if(!dev->data || !sball_get_input(dev->data, inp)) { return -1; } return 0; } spacenavd-0.6/src/client.c0000644000175000017500000000623012127374572015413 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include #include #include "client.h" #ifdef USE_X11 #include #include #endif struct client { int type; int sock; /* UNIX domain socket */ #ifdef USE_X11 Window win; /* X11 client window */ #endif float sens; /* sensitivity */ int dev_idx; /* device index */ struct client *next; }; static struct client *client_list = NULL; static struct client *client_iter; /* iterator (used by first/next calls) */ /* add a client to the list * cdata points to the socket fd for new-protocol clients, or the * window XID for clients talking to us through the magellan protocol */ struct client *add_client(int type, void *cdata) { struct client *client; #ifdef USE_X11 if(!cdata || (type != CLIENT_UNIX && type != CLIENT_X11)) #else if(!cdata || type != CLIENT_UNIX) #endif { return 0; } if(!(client = malloc(sizeof *client))) { return 0; } client->type = type; if(type == CLIENT_UNIX) { client->sock = *(int*)cdata; #ifdef USE_X11 } else { client->win = *(Window*)cdata; #endif } client->sens = 1.0f; client->dev_idx = 0; /* default/first device */ if(client_list == NULL) { client->next = NULL; return (client_list = client); } client->next = client_list; client_list = client; return client; } void remove_client(struct client *client) { struct client *iter = client_list; if(iter == NULL) return; if(iter == client) { client_list = iter->next; free(iter); if((iter = client_list) == NULL) return; } while(iter->next) { if(iter->next == client) { struct client *tmp = iter->next; iter->next = tmp->next; free(tmp); } else { iter = iter->next; } } } int get_client_type(struct client *client) { return client->type; } int get_client_socket(struct client *client) { return client->sock; } #ifdef USE_X11 Window get_client_window(struct client *client) { return client->win; } #endif void set_client_sensitivity(struct client *client, float sens) { client->sens = sens; } float get_client_sensitivity(struct client *client) { return client->sens; } void set_client_device_index(struct client *client, int dev_idx) { client->dev_idx = dev_idx; } int get_client_device_index(struct client *client) { return client->dev_idx; } struct client *first_client(void) { return (client_iter = client_list); } struct client *next_client(void) { if(client_iter) client_iter = client_iter->next; return client_iter; } spacenavd-0.6/src/xdetect_linux.c0000644000175000017500000000715512162700004017001 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef __linux__ #include "config.h" #ifdef USE_X11 #include #include #include #include #include #include #include #include #include "proto_x11.h" #include "spnavd.h" /* TODO implement fallback to polling if inotify is not available */ static int try_xconnect(void); static int fd = -1; static int watch_tmp = -1, watch_x11 = -1; int xdet_start(void) { if((fd = inotify_init()) == -1) { perror("failed to create inotify queue"); return -1; } fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); if((watch_x11 = inotify_add_watch(fd, "/tmp/.X11-unix", IN_CREATE)) == -1) { if((watch_tmp = inotify_add_watch(fd, "/tmp", IN_CREATE)) == -1) { perror("failed to watch /tmp for file events"); close(fd); fd = -1; return -1; } } if(verbose) { printf("waiting for the X socket file to appear\n"); } return fd; } /* this is called by init_x11 if it's successful */ void xdet_stop(void) { if(fd != -1) { if(verbose) { printf("stopping X watch\n"); } close(fd); fd = watch_tmp = watch_x11 = -1; } } int xdet_get_fd(void) { return fd; } int handle_xdet_events(fd_set *rset) { char buf[512]; struct inotify_event *ev = (struct inotify_event*)buf; ssize_t res; if(fd == -1 || !FD_ISSET(fd, rset)) { return -1; } for(;;) { if((res = read(fd, buf, sizeof buf)) <= 0) { if(res == 0) { /* kernels before 2.6.14 returned 0 for not enough space */ errno = EINVAL; } if(errno == EINTR) continue; if(errno != EAGAIN) { perror("failed to read inotify event"); } return -1; } if(ev->wd == watch_tmp) { if(watch_x11 != -1) { inotify_rm_watch(fd, watch_tmp); continue; } if(ev->len > 0 && strcmp(ev->name, ".X11-unix") == 0) { if((watch_x11 = inotify_add_watch(fd, "/tmp/.X11-unix", IN_CREATE)) == -1) { perror("failed to add /tmp/.X11-unix to the watch queue"); continue; } if(try_xconnect() == 0) { return 0; } } } else if(ev->wd == watch_x11) { char *dpystr, sock_file[64]; int dpynum = 0; if((dpystr = getenv("DISPLAY"))) { char *tmp = strchr(dpystr, ':'); if(tmp && isdigit(tmp[1])) { dpynum = atoi(tmp + 1); } } sprintf(sock_file, "X%d", dpynum); if(ev->len > 0 && strcmp(ev->name, sock_file) == 0) { if(verbose) { printf("found X socket, will now attempt to connect to the X server\n"); } if(try_xconnect() == 0) { return 0; } fprintf(stderr, "found X socket yet failed to connect\n"); } } } return -1; } static int try_xconnect(void) { int i; /* poll for approximately 15 seconds (well a bit more than that) */ for(i=0; i<15; i++) { sleep(1); if(init_x11() != -1) { return 0; /* success */ } } return -1; } #endif /* USE_X11 */ #else int spacenavd_xdetect_linux_shut_up_empty_source_warning; #endif /* __linux__ */ spacenavd-0.6/src/kbemu.h0000644000175000017500000000172011751134441015233 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KBEMU_H_ #define KBEMU_H_ #include #include void kbemu_set_display(Display *dpy); KeySym kbemu_keysym(const char *str); void send_kbevent(KeySym key, int press); #endif /* KBEMU_H_ */ spacenavd-0.6/src/spnavd.c0000644000175000017500000001706512273205246015431 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2013 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "spnavd.h" #include "dev.h" #include "hotplug.h" #include "client.h" #include "proto_unix.h" #ifdef USE_X11 #include "proto_x11.h" #endif static void cleanup(void); static void daemonize(void); static int write_pid_file(void); static int find_running_daemon(void); static void handle_events(fd_set *rset); static void sig_handler(int s); int main(int argc, char **argv) { int i, pid, ret, become_daemon = 1; for(i=1; i max_fd) max_fd = fd; } dev = dev->next; } if((fd = get_hotplug_fd()) != -1) { FD_SET(fd, &rset); if(fd > max_fd) max_fd = fd; } /* the UNIX domain socket listening for connections */ if((fd = get_unix_socket()) != -1) { FD_SET(fd, &rset); if(fd > max_fd) max_fd = fd; } /* all the UNIX socket clients */ client_iter = first_client(); while(client_iter) { if(get_client_type(client_iter) == CLIENT_UNIX) { int s = get_client_socket(client_iter); assert(s >= 0); FD_SET(s, &rset); if(s > max_fd) max_fd = s; } client_iter = next_client(); } /* and the X server socket */ #ifdef USE_X11 if((fd = get_x11_socket()) != -1) { FD_SET(fd, &rset); if(fd > max_fd) max_fd = fd; } #endif do { /* if there is at least one device out of the deadzone and repeat is enabled * wait for only as long as specified in cfg.repeat_msec */ struct timeval tv, *timeout = 0; if(cfg.repeat_msec >= 0) { dev = get_devices(); while(dev) { if(is_device_valid(dev) && !in_deadzone(dev)) { tv.tv_sec = cfg.repeat_msec / 1000; tv.tv_usec = cfg.repeat_msec % 1000; timeout = &tv; break; } dev = dev->next; } } ret = select(max_fd + 1, &rset, 0, 0, timeout); } while(ret == -1 && errno == EINTR); if(ret > 0) { handle_events(&rset); } else { if(cfg.repeat_msec >= 0) { dev = get_devices(); while(dev) { if(!in_deadzone(dev)) { repeat_last_event(dev); } dev = dev->next; } } } } return 0; /* unreachable */ } static void cleanup(void) { struct device *dev; #ifdef USE_X11 close_x11(); /* call to avoid leaving garbage in the X server's root windows */ #endif close_unix(); shutdown_hotplug(); dev = get_devices(); while(dev) { struct device *tmp = dev; dev = dev->next; remove_device(tmp); } remove(PIDFILE); } static void daemonize(void) { int i, pid; if((pid = fork()) == -1) { perror("failed to fork"); exit(1); } else if(pid) { exit(0); } setsid(); chdir("/"); /* redirect standard input/output/error */ for(i=0; i<3; i++) { close(i); } open("/dev/zero", O_RDONLY); if(open(LOGFILE, O_WRONLY | O_CREAT | O_TRUNC, 0644) == -1) { open("/dev/null", O_WRONLY); } dup(1); setvbuf(stdout, 0, _IOLBF, 0); setvbuf(stderr, 0, _IONBF, 0); } static int write_pid_file(void) { FILE *fp; int pid = getpid(); if(!(fp = fopen(PIDFILE, "w"))) { return -1; } fprintf(fp, "%d\n", pid); fclose(fp); return 0; } static int find_running_daemon(void) { FILE *fp; int s, pid; struct sockaddr_un addr; /* try to open the pid-file */ if(!(fp = fopen(PIDFILE, "r"))) { return -1; } if(fscanf(fp, "%d\n", &pid) != 1) { fclose(fp); return -1; } fclose(fp); /* make sure it's not just a stale pid-file */ if((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { return -1; } memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, SOCK_NAME, sizeof addr.sun_path); if(connect(s, (struct sockaddr*)&addr, sizeof addr) == -1) { close(s); return -1; } /* managed to connect alright, it's running... */ close(s); return pid; } static void handle_events(fd_set *rset) { int dev_fd, hotplug_fd; struct device *dev; struct dev_input inp; /* handle anything coming through the UNIX socket */ handle_uevents(rset); #ifdef USE_X11 /* handle any X11 events (magellan protocol) */ handle_xevents(rset); #endif /* finally read any pending device input data */ dev = get_devices(); while(dev) { /* keep the next pointer because read_device can potentially destroy * the device node if the read fails. */ struct device *next = dev->next; if((dev_fd = get_device_fd(dev)) != -1 && FD_ISSET(dev_fd, rset)) { /* read an event from the device ... */ while(read_device(dev, &inp) != -1) { /* ... and process it, possibly dispatching a spacenav event to clients */ process_input(dev, &inp); } } dev = next; } if((hotplug_fd = get_hotplug_fd()) != -1) { if(FD_ISSET(hotplug_fd, rset)) { handle_hotplug(); } } } /* signals usr1 & usr2 are sent by the spnav_x11 script to start/stop the * daemon's connection to the X server. */ static void sig_handler(int s) { int prev_led = cfg.led; switch(s) { case SIGHUP: read_cfg("/etc/spnavrc", &cfg); if(cfg.led != prev_led) { struct device *dev = get_devices(); while(dev) { if(is_device_valid(dev)) { if(verbose) { printf("turn led %s, device: %s\n", cfg.led ? "on": "off", dev->name); } set_device_led(dev, cfg.led); } dev = dev->next; } } break; case SIGSEGV: fprintf(stderr, "Segmentation fault caught, trying to exit gracefully\n"); case SIGINT: case SIGTERM: exit(0); #ifdef USE_X11 case SIGUSR1: init_x11(); break; case SIGUSR2: close_x11(); break; #endif default: break; } } spacenavd-0.6/src/magellan/0000755000175000017500000000000012374256237015551 5ustar nuclearnuclearspacenavd-0.6/src/magellan/smag_detect.h0000644000175000017500000000162712043026343020171 0ustar nuclearnuclear/* serial magellan device support for spacenavd Copyright (C) 2012 John Tsiombikas Copyright (C) 2010 Thomas Anderson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SMAG_DETECT_H_ #define SMAG_DETECT_H_ int smag_detect(const char *fname, char *buf, int sz); #endif /* SMAG_DETECT_H_ */ spacenavd-0.6/src/magellan/smag.c0000644000175000017500000002446612043026343016642 0ustar nuclearnuclear/* serial magellan device support for spacenavd Copyright (C) 2012 John Tsiombikas Copyright (C) 2010 Thomas Anderson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "magellan/smag.h" #include "magellan/smag_comm.h" #include "magellan/smag_event.h" #include "magellan/serialconstants.h" static void gen_disp_events(int *newval); static void proc_disp_packet(void); static void gen_button_event(int button, int new_state); static void read_copy(void); static void proc_disp_packet(void); static void proc_bn_k_packet(void); static void proc_bn_c_packet(void); static void proc_bn_n_packet(void); static void proc_bn_q_packet(void); static void clean_input(); static int dev_fd; struct input_struct { char rbuf[MAXREADSIZE]; int rbuf_sz; char packet_buf[MAXPACKETSIZE]; int packet_buf_pos; struct smag_event *evhead; struct smag_event *evtail; } input; static int first_byte_parity[16] = { 0xE0, 0xA0, 0xA0, 0x60, 0xA0, 0x60, 0x60, 0xA0, 0x90, 0x50, 0x50, 0x90, 0xD0, 0x90, 0x90, 0x50 }; static int second_byte_parity[64] = { 0x80, 0x40, 0x40, 0x80, 0x40, 0x80, 0x80, 0x40, 0x40, 0x80, 0x80, 0x40, 0x80, 0x40, 0x40, 0x80, 0x40, 0x80, 0x80, 0x40, 0x80, 0x40, 0x40, 0x80, 0x80, 0x40, 0x40, 0x80, 0xC0, 0x80, 0x80, 0x40, 0xC0, 0x80, 0x80, 0x40, 0x80, 0x40, 0x40, 0x80, 0x80, 0x40, 0x40, 0x80, 0x40, 0x80, 0x80, 0x40, 0x80, 0x40, 0x40, 0x80, 0x40, 0x80, 0x80, 0x40, 0x40, 0x80, 0x80, 0x40, 0x80, 0x40, 0x00, 0x80 }; void smag_init_device(int fd) { smag_write(fd, "", 0); smag_write(fd, "\r\rm0", 4); smag_write(fd, "pAA", 3); smag_write(fd, "q00", 3); /*default translation and rotation */ smag_write(fd, "nM", 2); /*zero radius. 0-15 defaults to 13 */ smag_write(fd, "z", 1); /*zero device */ smag_write(fd, "c33", 3); /*set translation, rotation on and dominant axis off */ smag_write(fd, "l2\r\0", 4); smag_write(fd, "\r\r", 2); smag_write(fd, "l300", 4); smag_write(fd, "b9", 2); /*these are beeps */ smag_write(fd, "b9", 2); usleep(SMAG_DELAY_USEC); tcflush(fd, TCIOFLUSH); clean_input(); } static void read_copy(void) { int i; for(i=0; i 0) { read_copy(); } ev = input.evhead; if(ev) { input.evhead = input.evhead->next; *inp = ev->data; free_event(ev); return 1; } return 0; } int get_fd_smag() { return dev_fd; } void get_version_string(int fd, char *buf, int sz) { int bytesrd; char tmpbuf[MAXREADSIZE]; smag_write(fd, "\r\rm0", 4); smag_write(fd, "", 0); smag_write(fd, "\r\rm0", 4); smag_write(fd, "c03", 3); smag_write(fd, "z", 1); smag_write(fd, "Z", 1); smag_write(fd, "l000", 4); usleep(SMAG_DELAY_USEC); tcflush(fd, TCIOFLUSH); clean_input(); smag_write(fd, "vQ", 2); bytesrd = smag_read(fd, tmpbuf, MAXREADSIZE); if(bytesrd > 0 && bytesrd < sz) { strcpy(buf, tmpbuf); } clean_input(); } static void gen_disp_events(int *newval) { int i, pending; static int oldval[6] = {0, 0, 0, 0, 0, 0}; struct smag_event *newev; pending = 0; for(i=0; i<6; i++) { if(newval[i] == oldval[i]) { continue; } oldval[i] = newval[i]; newev = alloc_event(); if(newev) { newev->data.type = INP_MOTION; newev->data.idx = i; newev->data.val = newval[i]; newev->next = 0; if(input.evhead) { input.evtail->next = newev; input.evtail = newev; } else input.evhead = input.evtail = newev; pending = 1; } } if(pending) { newev = alloc_event(); if(newev) { newev->data.type = INP_FLUSH; newev->next = 0; } if(input.evhead) { input.evtail->next = newev; input.evtail = newev; } else { input.evhead = input.evtail = newev; } } } static void proc_disp_packet(void) { int i, last_bytes, offset, values[6]; short int accum_last, number, accum_last_adj; accum_last = offset = 0; for(i=1; i<13; i+=2) { /*first byte check */ unsigned char low, up; low = input.packet_buf[i] & 0x0F; up = input.packet_buf[i] & 0xF0; if(up != first_byte_parity[low]) { fprintf(stderr, "bad first packet\n"); return; } /*second byte check */ low = input.packet_buf[i + 1] & 0x3F; up = input.packet_buf[i + 1] & 0xC0; if(up != second_byte_parity[low]) { fprintf(stderr, "bad second packet\n"); return; } number = (short int)((input.packet_buf[i] << 6 & 0x03C0) | (input.packet_buf[i + 1] & 0x3F)); if(number > 512) { number -= 1024; } accum_last += number; if(number < 0) { offset += ((int)(number + 1) / 64) - 1; } else { offset += (int)number / 64; } /*printf("%8i ", number); */ values[(i + 1) / 2 - 1] = number; } /*last byte of packet is a sum of 6 numbers and a factor of 64. use as a packet check. still not sure what the second to last byte is for. */ accum_last_adj = accum_last & 0x003F; accum_last_adj += offset; if(accum_last_adj < 0) { accum_last_adj += 64; } if(accum_last_adj > 63) { accum_last_adj -= 64; } last_bytes = (short int)(input.packet_buf[14] & 0x3F); if(accum_last_adj != last_bytes) { printf(" bad packet\n"); return; } gen_disp_events(values); return; } static void gen_button_event(int button, int new_state) { struct smag_event *newev = alloc_event(); if(!newev) { return; } newev->data.type = INP_BUTTON; newev->data.idx = button; newev->data.val = new_state; newev->next = 0; if(input.evhead) { input.evtail->next = newev; input.evtail = newev; } else { input.evhead = input.evtail = newev; } } static void proc_bn_k_packet(void) { static char old_state[5] = { 0, 0, 0, 0, 0 }; if(input.packet_buf[1] != old_state[1]) { if((input.packet_buf[1] & 0x01) != (old_state[1] & 0x01)) { gen_button_event(0, input.packet_buf[1] & 0x01); } if((input.packet_buf[1] & 0x02) != (old_state[1] & 0x02)) { gen_button_event(1, input.packet_buf[1] & 0x02); } if((input.packet_buf[1] & 0x04) != (old_state[1] & 0x04)) { gen_button_event(2, input.packet_buf[1] & 0x04); } if((input.packet_buf[1] & 0x08) != (old_state[1] & 0x08)) { gen_button_event(3, input.packet_buf[1] & 0x08); } } if(input.packet_buf[2] != old_state[2]) { if((input.packet_buf[2] & 0x01) != (old_state[2] & 0x01)) { gen_button_event(4, input.packet_buf[2] & 0x01); } if((input.packet_buf[2] & 0x02) != (old_state[2] & 0x02)) { gen_button_event(5, input.packet_buf[2] & 0x02); } if((input.packet_buf[2] & 0x04) != (old_state[2] & 0x04)) { gen_button_event(6, input.packet_buf[2] & 0x04); } if((input.packet_buf[2] & 0x08) != (old_state[2] & 0x08)) { gen_button_event(7, input.packet_buf[2] & 0x08); } } /*skipping asterisk button. asterisk function come in through other packets. */ /*magellan plus has left and right (10, 11) buttons not magellan classic */ /*not sure if we need to filter out lower button events for magellan classic */ if(input.packet_buf[3] != old_state[3]) { /* if (input.packet_buf[3] & 0x01) printf("button asterisk "); */ if((input.packet_buf[3] & 0x02) != (old_state[3] & 0x02)) { gen_button_event(8, input.packet_buf[3] & 0x02); /*left button */ } if((input.packet_buf[3] & 0x04) != (old_state[3] & 0x04)) { gen_button_event(9, input.packet_buf[3] & 0x04); /*right button */ } } strcpy(old_state, input.packet_buf); } static void proc_bn_c_packet(void) { /*these are implemented at device and these signals are to keep the driver in sync */ if(input.packet_buf[1] & 0x02) { printf("translation is on "); } else { printf("translation is off "); } if(input.packet_buf[1] & 0x01) { printf("rotation is on "); } else { printf("rotation is off "); } if(input.packet_buf[1] & 0x04) { printf("dominant axis is on "); } else { printf("dominant axis is off "); } printf("\n"); /*printf("%s\n", input.packet_buf); */ } static void proc_bn_n_packet(void) { int radius; radius = (int)input.packet_buf[1] & 0x0F; printf("zero radius set to %i\n", radius); } static void proc_bn_q_packet(void) { /* this has no effect on the device numbers. Driver is to implement any scale of numbers */ int rotation, translation; rotation = (int)input.packet_buf[1] & 0x07; translation = (int)input.packet_buf[2] & 0x07; printf("rotation = %i translation = %i\n", rotation, translation); } static void clean_input(void) { memset(input.rbuf, 0x00, MAXREADSIZE); input.rbuf_sz = 0; memset(input.packet_buf, 0x00, MAXPACKETSIZE); input.packet_buf_pos = 0; } spacenavd-0.6/src/magellan/smag_event.h0000644000175000017500000000201112043026343020026 0ustar nuclearnuclear/* serial magellan device support for spacenavd Copyright (C) 2012 John Tsiombikas Copyright (C) 2010 Thomas Anderson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SMAG_EVENT_H_ #define SMAG_EVENT_H_ #include "event.h" struct smag_event { struct dev_input data; struct smag_event *next; }; struct smag_event *alloc_event(void); void free_event(struct smag_event *ev); #endif /* SMAG_EVENT_H_ */ spacenavd-0.6/src/magellan/serialconstants.h0000644000175000017500000000162212043026343021121 0ustar nuclearnuclear /* serialmagellan - decoding serial magellan spaceball data. Copyright (C) 2010 Thomas Anderson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SERIAL_CONSTANTS_H_ #define SERIAL_CONSTANTS_H_ #define MAXPACKETSIZE 16 #define VERSION_STRING_MAX 512 #define DEVICE_NAME_MAX 64 #define MAXREADSIZE 512 #endif spacenavd-0.6/src/magellan/smag.h0000644000175000017500000000203012043026343016626 0ustar nuclearnuclear/* serial magellan device support for spacenavd Copyright (C) 2012 John Tsiombikas Copyright (C) 2010 Thomas Anderson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "event.h" int open_smag(const char *devfile); int close_smag(); int read_smag(struct dev_input *inp); int get_fd_smag(); void get_version_string(int fd, char *buf, int sz); void smag_init_device(int fd); void clearInput(void); void readCopy(void); spacenavd-0.6/src/magellan/smag_comm.c0000644000175000017500000000620412147167510017652 0ustar nuclearnuclear/* serial magellan device support for spacenavd Copyright (C) 2012 John Tsiombikas Copyright (C) 2010 Thomas Anderson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "magellan/smag_comm.h" int smag_open_device(const char *fname) { return open(fname, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY); } int smag_set_port_spaceball(int fd) { int status; struct termios term; if(tcgetattr(fd, &term) == -1) { perror("error tcgetattr"); return -1; } term.c_cflag = CREAD | CS8 | CLOCAL | HUPCL; term.c_iflag |= IGNBRK | IGNPAR; term.c_oflag = 0; term.c_lflag = 0; term.c_cc[VMIN] = 1; term.c_cc[VTIME] = 0; cfsetispeed(&term, 9600); cfsetospeed(&term, 9600); if(tcsetattr(fd, TCSANOW, &term) == -1) { perror("error tcsetattr"); return -1; } if(ioctl(fd, TIOCMGET, &status) == -1) { perror("error TIOMCGET"); return -1; } status |= TIOCM_DTR; status |= TIOCM_RTS; if(ioctl(fd, TIOCMSET, &status) == -1) { perror("error TIOCMSET"); return -1; } return 0; } int smag_set_port_magellan(int fd) { int status; struct termios term; if(tcgetattr(fd, &term) == -1) { perror("error tcgetattr"); return -1; } term.c_cflag = CS8 | CSTOPB | CRTSCTS | CREAD | HUPCL | CLOCAL; term.c_iflag |= IGNBRK | IGNPAR; term.c_oflag = 0; term.c_lflag = 0; term.c_cc[VMIN] = 1; term.c_cc[VTIME] = 0; cfsetispeed(&term, 9600); cfsetospeed(&term, 9600); if(tcsetattr(fd, TCSANOW, &term) == -1) { perror("error tcsetattr"); return -1; } if(ioctl(fd, TIOCMGET, &status) == -1) { perror("error TIOCMGET"); return -1; } status |= TIOCM_DTR; status |= TIOCM_RTS; if(ioctl(fd, TIOCMSET, &status) == -1) { perror("error TIOCMSET"); return -1; } return 0; } #define LONG_DELAY 150000 void smag_write(int fd, const char *buf, int sz) { int i; for(i=0; i This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "smag_event.h" int evpool_size = 0; static struct smag_event *ev_free_list; struct smag_event *alloc_event(void) { struct smag_event *ev; if(ev_free_list) { ev = ev_free_list; ev_free_list = ev->next; } else { ev = malloc(sizeof *ev); evpool_size++; } return ev; } void free_event(struct smag_event *ev) { if(evpool_size > 512) { free(ev); evpool_size--; } else { ev->next = ev_free_list; ev_free_list = ev; } } spacenavd-0.6/src/magellan/smag_comm.h0000644000175000017500000000216012043026343017645 0ustar nuclearnuclear/* serial magellan device support for spacenavd Copyright (C) 2012 John Tsiombikas Copyright (C) 2010 Thomas Anderson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SMAG_COMM_H_ #define SMAG_COMM_H_ #define SMAG_DELAY_USEC 2000 int smag_open_device(const char *fname); int smag_set_port_spaceball(int fd); int smag_set_port_magellan(int fd); void smag_write(int fd, const char *buf, int sz); int smag_read(int fd, char *buf, int sz); int smag_wait_read(int fd, char *buf, int sz, int wait_sec); #endif spacenavd-0.6/src/magellan/smag_detect.c0000644000175000017500000000523412043026343020162 0ustar nuclearnuclear/* serial magellan device support for spacenavd Copyright (C) 2012 John Tsiombikas Copyright (C) 2010 Thomas Anderson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "magellan/smag.h" #include "magellan/smag_detect.h" #include "magellan/serialconstants.h" #include "magellan/smag_comm.h" /*swap out /r for /n for string printing*/ static void make_printable(char *str) { while(*str) { if(*str == '\r') { *str = '\n'; } str++; } } int smag_detect(const char *fname, char *buf, int sz) { int fd, bytesrd, pos; char tmpbuf[MAXREADSIZE]; if((fd = smag_open_device(fname)) == -1) { fprintf(stderr, "%s: couldn't open device file: %s\n", __func__, fname); return -1; } if(smag_set_port_spaceball(fd) == -1) { close(fd); fprintf(stderr, "%s: couldn't setup port\n", __func__); return -1; } /* first look for spaceball. should have data after open and port setup. * I was hoping that using the select inside serialWaitRead would allow me * to get rid of the following sleep. Removing the sleep causes port to freeze. */ sleep(1); bytesrd = 0; pos = 0; while((pos = smag_wait_read(fd, tmpbuf + bytesrd, MAXREADSIZE - bytesrd, 1)) > 0) { bytesrd += pos; } if(bytesrd > 0) { smag_write(fd, "hm", 2); while((pos = smag_wait_read(fd, tmpbuf + bytesrd, MAXREADSIZE - bytesrd, 1)) > 0) { bytesrd += pos; } smag_write(fd, "\"", 1); while((pos = smag_wait_read(fd, tmpbuf + bytesrd, MAXREADSIZE - bytesrd, 1)) > 0) { bytesrd += pos; } make_printable(tmpbuf); strncpy(buf, tmpbuf, sz); if(bytesrd < sz) { fprintf(stderr, "%s: buffer overrun\n", __func__); return -1; } } /*now if we are here we don't have a spaceball and now we need to check for a magellan */ close(fd); pos = 0; if((fd = smag_open_device(fname)) == -1) { return -1; } if(smag_set_port_magellan(fd) == -1) { return -1; } sleep(1); smag_init_device(fd); get_version_string(fd, tmpbuf, MAXREADSIZE); make_printable(tmpbuf); strncpy(buf, tmpbuf, sz); close(fd); return 0; } spacenavd-0.6/src/dev_usb_linux.c0000644000175000017500000002754212273210212016772 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2013 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef __linux__ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "dev.h" #include "dev_usb.h" #include "spnavd.h" #include "event.h" #include "hotplug.h" #define DEF_MINVAL (-500) #define DEF_MAXVAL 500 #define DEF_RANGE (DEF_MAXVAL - DEF_MINVAL) #define IS_DEV_OPEN(dev) ((dev)->fd >= 0) /* sometimes the rotation events are missing from linux/input.h */ #ifndef REL_RX #define REL_RX 3 #endif #ifndef REL_RY #define REL_RY 4 #endif #ifndef REL_RZ #define REL_RZ 5 #endif /* apparently some old versions of input.h do not define EV_SYN */ #ifndef EV_SYN #define EV_SYN 0 #endif static void close_evdev(struct device *dev); static int read_evdev(struct device *dev, struct dev_input *inp); static void set_led_evdev(struct device *dev, int state); int open_dev_usb(struct device *dev) { int i; struct input_absinfo absinfo; unsigned char evtype_mask[(EV_MAX + 7) / 8]; if((dev->fd = open(dev->path, O_RDWR)) == -1) { if((dev->fd = open(dev->path, O_RDONLY)) == -1) { perror("failed to open device"); return -1; } fprintf(stderr, "opened device read-only, LEDs won't work\n"); } if(ioctl(dev->fd, EVIOCGNAME(sizeof dev->name), dev->name) == -1) { perror("EVIOCGNAME ioctl failed"); strcpy(dev->name, "unknown"); } printf("device name: %s\n", dev->name); /* get number of axes */ dev->num_axes = 6; /* default to regular 6dof controller axis count */ if(ioctl(dev->fd, EVIOCGBIT(EV_ABS, sizeof evtype_mask), evtype_mask) == 0) { dev->num_axes = 0; for(i=0; inum_axes++; } else { break; } } } if(verbose) { printf(" Number of axes: %d\n", dev->num_axes); } dev->minval = malloc(dev->num_axes * sizeof *dev->minval); dev->maxval = malloc(dev->num_axes * sizeof *dev->maxval); dev->fuzz = malloc(dev->num_axes * sizeof *dev->fuzz); if(!dev->minval || !dev->maxval || !dev->fuzz) { perror("failed to allocate memory"); return -1; } /* if the device is an absolute device, find the minimum and maximum axis values */ for(i=0; inum_axes; i++) { dev->minval[i] = DEF_MINVAL; dev->maxval[i] = DEF_MAXVAL; dev->fuzz[i] = 0; if(ioctl(dev->fd, EVIOCGABS(i), &absinfo) == 0) { dev->minval[i] = absinfo.minimum; dev->maxval[i] = absinfo.maximum; dev->fuzz[i] = absinfo.fuzz; if(verbose) { printf(" Axis %d value range: %d - %d (fuzz: %d)\n", i, dev->minval[i], dev->maxval[i], dev->fuzz[i]); } } } /*if(ioctl(dev->fd, EVIOCGBIT(0, sizeof(evtype_mask)), evtype_mask) == -1) { perror("EVIOCGBIT ioctl failed\n"); close(dev->fd); return -1; }*/ if(cfg.grab_device) { int grab = 1; /* try to grab the device */ if(ioctl(dev->fd, EVIOCGRAB, &grab) == -1) { perror("failed to grab the device"); } } /* set non-blocking */ fcntl(dev->fd, F_SETFL, fcntl(dev->fd, F_GETFL) | O_NONBLOCK); if(cfg.led) { set_led_evdev(dev, 1); } /* fill the device function pointers */ dev->close = close_evdev; dev->read = read_evdev; dev->set_led = set_led_evdev; return 0; } static void close_evdev(struct device *dev) { if(IS_DEV_OPEN(dev)) { dev->set_led(dev, 0); close(dev->fd); dev->fd = -1; free(dev->minval); free(dev->maxval); free(dev->fuzz); } } static INLINE int map_range(struct device *dev, int axidx, int val) { int range = dev->maxval[axidx] - dev->minval[axidx]; if(range <= 0) { return val; } return (val - dev->minval[axidx]) * DEF_RANGE / range + DEF_MINVAL; } static int read_evdev(struct device *dev, struct dev_input *inp) { struct input_event iev; /* linux evdev event */ int rdbytes; if(!IS_DEV_OPEN(dev)) return -1; do { rdbytes = read(dev->fd, &iev, sizeof iev); } while(rdbytes == -1 && errno == EINTR); /* disconnect? */ if(rdbytes == -1) { if(errno != EAGAIN) { perror("read error"); remove_device(dev); } return -1; } if(rdbytes > 0) { inp->tm = iev.time; switch(iev.type) { case EV_REL: inp->type = INP_MOTION; inp->idx = iev.code - REL_X; inp->val = iev.value; /*printf("[%s] EV_REL(%d): %d\n", dev->name, inp->idx, iev.value);*/ break; case EV_ABS: inp->type = INP_MOTION; inp->idx = iev.code - ABS_X; inp->val = map_range(dev, inp->idx, iev.value); /*printf("[%s] EV_ABS(%d): %d (orig: %d)\n", dev->name, inp->idx, inp->val, iev.value);*/ break; case EV_KEY: inp->type = INP_BUTTON; inp->idx = iev.code - BTN_0; inp->val = iev.value; break; case EV_SYN: inp->type = INP_FLUSH; /*printf("[%s] EV_SYN\n", dev->name);*/ break; default: return -1; } } return 0; } static void set_led_evdev(struct device *dev, int state) { struct input_event ev; if(!IS_DEV_OPEN(dev)) return; memset(&ev, 0, sizeof ev); ev.type = EV_LED; ev.code = LED_MISC; ev.value = state; if(write(dev->fd, &ev, sizeof ev) == -1) { fprintf(stderr, "failed to turn LED %s\n", state ? "on" : "off"); } } #define PROC_DEV "/proc/bus/input/devices" struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_info*)) { struct usb_device_info *devlist = 0, devinfo; int buf_used, buf_len, bytes_read; char buf[1024]; char *buf_pos, *section_start, *next_section = 0, *cur_line, *next_line; FILE *fp; DIR *dir; struct dirent *dent; if(verbose) { printf("Device detection, parsing " PROC_DEV "\n"); } devlist = 0; buf_pos = buf; buf_len = sizeof(buf) - 1; if(!(fp = fopen(PROC_DEV, "r"))) { if(verbose) { perror("failed to open " PROC_DEV); } goto alt_detect; } while((bytes_read = fread(buf_pos, 1, buf_len, fp)) >= 0) { buf_pos[bytes_read] = '\0'; section_start = buf; for(;;) { char *keyptr; next_section = strstr(section_start, "\n\n"); if(next_section == NULL) { /* move last (partial) section to start of buf */ /* sizeof(buf) - 1 because the last one is '\0' */ buf_used = (buf + sizeof(buf) - 1) - section_start; memmove(buf, section_start, buf_used); buf[buf_used] = '\0'; /* point to end of last section and calc remaining space in buf */ buf_pos = buf + buf_used; buf_len = sizeof(buf) - buf_used; /* break to read from file again */ break; } /* set second newline to teminating null */ next_section[1] = 0; /* point to start of next section */ next_section += 2; memset(&devinfo, 0, sizeof devinfo); cur_line = section_start; while (*cur_line) { next_line = strchr(cur_line, '\n'); *next_line = 0; next_line++; switch (*cur_line) { case 'I': keyptr = strstr(cur_line, "Vendor="); if(keyptr) { char *endp, *valptr = keyptr + strlen("Vendor="); devinfo.vendorid = strtol(valptr, &endp, 16); } keyptr = strstr(cur_line, "Product="); if(keyptr) { char *endp, *valptr = keyptr + strlen("Product="); devinfo.productid = strtol(valptr, &endp, 16); } break; case 'N': keyptr = strstr(cur_line, "Name=\""); if(keyptr) { char *valptr = keyptr + strlen("Name=\""); char *endp = strrchr(cur_line, '"'); if(endp) { *endp = 0; } if(!(devinfo.name = strdup(valptr))) { fprintf(stderr, "failed to allocate the device name buffer for: %s: %s\n", valptr, strerror(errno)); } } break; case 'H': keyptr = strstr(cur_line, "Handlers="); if(keyptr) { char *devfile, *valptr = keyptr + strlen("Handlers="); static const char *prefix = "/dev/input/"; int idx = 0; while((devfile = strtok(idx ? 0 : valptr, " \t\v\n\r"))) { if(!(devinfo.devfiles[idx] = malloc(strlen(devfile) + strlen(prefix) + 1))) { perror("failed to allocate device filename buffer"); continue; } sprintf(devinfo.devfiles[idx++], "%s%s", prefix, devfile); } devinfo.num_devfiles = idx; } break; } cur_line = next_line; } /* check with the user-supplied matching callback to see if we should include * this device in the returned list or not... */ if(!match || match(&devinfo)) { /* add it to the list */ struct usb_device_info *node = malloc(sizeof *node); if(node) { if(verbose) { printf("found usb device [%x:%x]: \"%s\" (%s) \n", devinfo.vendorid, devinfo.productid, devinfo.name ? devinfo.name : "unknown", devinfo.devfiles[0]); } *node = devinfo; memset(&devinfo, 0, sizeof devinfo); node->next = devlist; devlist = node; } else { perror("failed to allocate usb device info node"); } } else { /* cleanup devinfo before moving to the next line */ int i; for(i = 0; i < devinfo.num_devfiles; ++i) { free(devinfo.devfiles[i]); } free(devinfo.name); memset(&devinfo, 0, sizeof devinfo); } section_start = next_section; } if(bytes_read == 0) break; } fclose(fp); if(devlist) { return devlist; } /* otherwise try the alternative detection in case it finds something... */ alt_detect: if(verbose) { fprintf(stderr, "trying alternative detection, querying /dev/input/ devices...\n"); } /* if for some reason we can't open the /proc/bus/input/devices file, or we * couldn't find our device there, we'll try opening all /dev/input/ * devices, and see if anyone matches our predicate */ if(!(dir = opendir("/dev/input"))) { perror("failed to open /dev/input/ directory"); return 0; } while((dent = readdir(dir))) { int fd; struct stat st; struct input_id id; memset(&devinfo, 0, sizeof devinfo); if(!(devinfo.devfiles[0] = malloc(strlen(dent->d_name) + strlen("/dev/input/") + 1))) { perror("failed to allocate device file name"); continue; } sprintf(devinfo.devfiles[0], "/dev/input/%s", dent->d_name); devinfo.num_devfiles = 1; if(verbose) { fprintf(stderr, " trying \"%s\" ... ", devinfo.devfiles[0]); } if(stat(devinfo.devfiles[0], &st) == -1 || !S_ISCHR(st.st_mode)) { free(devinfo.devfiles[0]); continue; } if((fd = open(devinfo.devfiles[0], O_RDONLY)) == -1) { fprintf(stderr, "failed to open %s: %s\n", devinfo.devfiles[0], strerror(errno)); free(devinfo.devfiles[0]); continue; } if(ioctl(fd, EVIOCGID, &id) != -1) { devinfo.vendorid = id.vendor; devinfo.productid = id.product; } if(ioctl(fd, EVIOCGNAME(sizeof buf), buf) != -1) { if(!(devinfo.name = strdup(buf))) { perror("failed to allocate device name buffer"); close(fd); free(devinfo.devfiles[0]); continue; } } if(!match || match(&devinfo)) { struct usb_device_info *node = malloc(sizeof *node); if(node) { if(verbose) { printf("found usb device [%x:%x]: \"%s\" (%s) \n", devinfo.vendorid, devinfo.productid, devinfo.name ? devinfo.name : "unknown", devinfo.devfiles[0]); } *node = devinfo; node->next = devlist; devlist = node; } else { free(devinfo.name); free(devinfo.devfiles[0]); perror("failed to allocate usb device info"); } } else { free(devinfo.name); free(devinfo.devfiles[0]); } close(fd); } closedir(dir); return devlist; } #endif /* __linux__ */ spacenavd-0.6/src/dev_usb.h0000644000175000017500000000245112162151603015555 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SPNAV_DEV_USB_H_ #define SPNAV_DEV_USB_H_ struct device; int open_dev_usb(struct device *dev); /* USB device enumeration and matching */ #define MAX_USB_DEV_FILES 16 struct usb_device_info { char *name; int num_devfiles; char *devfiles[MAX_USB_DEV_FILES]; int vendorid, productid; struct usb_device_info *next; }; struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_info*)); void free_usb_devices_list(struct usb_device_info *list); void print_usb_device_info(struct usb_device_info *devinfo); #endif /* SPNAV_DEV_USB_H_ */ spacenavd-0.6/src/spnavd_win32.c0000644000175000017500000000256512167571175016463 0ustar nuclearnuclear#ifdef WIN32 #include #include static void WINAPI svc_main(unsigned long argc, char **argv); static unsigned long WINAPI svc_handler(unsigned long op, unsigned long evtype, void *evdata, void *cls); static SERVICE_STATUS_HANDLE svc; int main(int argc, char **argv) { SERVICE_TABLE_ENTRY serv_tbl[] = { {"", svc_main}, {0, 0} }; if(!StartServiceCtrlDispatcher(serv_tbl)) { fprintf(stderr, "failed to start the spacenavd service\n"); return 1; } return 0; } static void WINAPI svc_main(unsigned long argc, char **argv) { SERVICE_STATUS status; svc = RegisterServiceCtrlHandlerEx("spacenavd", svc_handler, 0); status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; status.dwCurrentState = SERVICE_START_PENDING; status.dwControlsAccepted = SERVICE_ACCEPT_STOP; status.dwWin32ExitCode = 0; status.dwCheckPoint = 0; status.dwWaitHint = 2000; SetServiceStatus(svc, &status); /* init */ status.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(svc, &status); } static unsigned long WINAPI svc_handler(unsigned long op, unsigned long evtype, void *evdata, void *cls) { printf("received service control message: %ld\n", op); switch(op) { case SERVICE_CONTROL_INTERROGATE: return 0; case SERVICE_CONTROL_STOP: exit(0); default: break; } return ERROR_CALL_NOT_IMPLEMENTED; } #else int spnavd_win32_silcence_empty_file_warnings = 1; #endif spacenavd-0.6/src/dummy_usb.c0000644000175000017500000000311612162151603016124 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #if !defined(__linux__) && !(defined(__APPLE__) && defined(__MACH__)) #include #include "dev.h" static const char *message = "Unfortunately this version of spacenavd does not support USB devices on your " "platform yet. Make sure you are using the latest version of spacenavd.\n"; struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_info*)) { fputs(message, stderr); return 0; } void free_usb_devices_list(struct usb_device_info *list) { } int open_dev_usb(struct device *dev) { return -1; } /* the hotplug functions will also be missing on unsupported platforms */ int init_hotplug(void) { return -1; } void shutdown_hotplug(void) { } int get_hotplug_fd(void) { return -1; } int handle_hotplug(void) { return -1; } #else int dummy_usb_c_avoid_stupid_compiler_warnings = 1; #endif /* unsupported platform */ spacenavd-0.6/src/client.h0000644000175000017500000000316712127374572015426 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef CLIENT_H_ #define CLIENT_H_ #include "config.h" #ifdef USE_X11 #include #endif /* client types */ enum { CLIENT_X11, /* through the magellan X11 protocol */ CLIENT_UNIX /* through the new UNIX domain socket */ }; struct client; struct client *add_client(int type, void *cdata); void remove_client(struct client *client); int get_client_type(struct client *client); int get_client_socket(struct client *client); #ifdef USE_X11 Window get_client_window(struct client *client); #endif void set_client_sensitivity(struct client *client, float sens); float get_client_sensitivity(struct client *client); void set_client_device_index(struct client *client, int dev_idx); int get_client_device_index(struct client *client); /* these two can be used to iterate over all clients */ struct client *first_client(void); struct client *next_client(void); #endif /* CLIENT_H_ */ spacenavd-0.6/src/cfgfile.h0000644000175000017500000000270112162430754015532 0ustar nuclearnuclear/* spnavcfg - an interactive GUI configurator for the spacenavd daemon. Copyright (C) 2007-2013 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef CFGFILE_H_ #define CFGFILE_H_ #include #define MAX_AXES 64 #define MAX_BUTTONS 64 #define MAX_CUSTOM 64 struct cfg { float sensitivity, sens_trans[3], sens_rot[3]; int dead_threshold[MAX_AXES]; int invert[MAX_AXES]; int map_axis[MAX_AXES]; int map_button[MAX_BUTTONS]; int kbmap[MAX_BUTTONS]; char *kbmap_str[MAX_BUTTONS]; int led, grab_device; char serial_dev[PATH_MAX]; int repeat_msec; char *devname[MAX_CUSTOM]; /* custom USB device name list */ int devid[MAX_CUSTOM][2]; /* custom USB vendor/product id list */ }; void default_cfg(struct cfg *cfg); int read_cfg(const char *fname, struct cfg *cfg); int write_cfg(const char *fname, struct cfg *cfg); #endif /* CFGFILE_H_ */ spacenavd-0.6/src/dev_usb_darwin.c0000644000175000017500000000520712257701304017122 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #if defined(__APPLE__) && defined(__MACH__) #include "config.h" #include #include #include #include "dev.h" #include "dev_usb.h" int open_dev_usb(struct device *dev) { return -1; } struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_info*)) { struct usb_device_info *devlist = 0; struct usb_device_info devinfo; /*static const int vendor_id = 1133;*/ /* 3dconnexion */ static char dev_path[512]; io_object_t dev; io_iterator_t iter; CFMutableDictionaryRef match_dict; CFNumberRef number_ref; match_dict = IOServiceMatching(kIOHIDDeviceKey); /* add filter rule: vendor-id should be 3dconnexion's */ /*number_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor_id); CFDictionarySetValue(match_dict, CFSTR(kIOHIDVendorIDKey), number_ref); CFRelease(number_ref); */ /* fetch... */ if(IOServiceGetMatchingServices(kIOMasterPortDefault, match_dict, &iter) != kIOReturnSuccess) { fprintf(stderr, "failed to retrieve USB HID devices\n"); return 0; } while((dev = IOIteratorNext(iter))) { memset(&devinfo, 0, sizeof devinfo); IORegistryEntryGetPath(dev, kIOServicePlane, dev_path); if(!(devinfo.devfiles[0] = strdup(dev_path))) { perror("failed to allocate device file path buffer"); continue; } devinfo.num_devfiles = 1; /* TODO retrieve vendor id and product id */ if(!match || match(&devinfo)) { struct usb_device_info *node = malloc(sizeof *node); if(node) { if(verbose) { printf("found usb device: "); print_usb_device_info(&devinfo); } *node = devinfo; node->next = devlist; devlist = node; } else { free(devinfo.devfiles[0]); perror("failed to allocate usb device info node"); } } } IOObjectRelease(dev); IOObjectRelease(iter); return devlist; } #else int spacenavd_dev_usb_darwin_silence_empty_warning; #endif /* __APPLE__ && __MACH__ */ spacenavd-0.6/src/hotplug_darwin.c0000644000175000017500000000210111755073334017152 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #if defined(__APPLE__) && defined(__MACH__) #include "config.h" #include #include #include int init_hotplug(void) { return -1; } void shutdown_hotplug(void) { } int get_hotplug_fd(void) { return -1; } int handle_hotplug(void) { return -1; } #endif /* __APPLE__ && __MACH__ */ spacenavd-0.6/src/proto_unix.c0000644000175000017500000000671311403040606016331 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2010 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include #include #include #include #include #include #include #include #include "proto_unix.h" #include "spnavd.h" enum { UEV_TYPE_MOTION, UEV_TYPE_PRESS, UEV_TYPE_RELEASE }; static int lsock; int init_unix(void) { int s; mode_t prev_umask; struct sockaddr_un addr; if(lsock) return 0; if((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { perror("failed to create socket"); return -1; } unlink(SOCK_NAME); /* in case it already exists */ memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, SOCK_NAME); prev_umask = umask(0); if(bind(s, (struct sockaddr*)&addr, sizeof addr) == -1) { fprintf(stderr, "failed to bind unix socket: %s: %s\n", SOCK_NAME, strerror(errno)); return -1; } umask(prev_umask); if(listen(s, 8) == -1) { perror("listen failed"); return -1; } lsock = s; return 0; } void close_unix(void) { if(lsock != -1) { close(lsock); lsock = -1; unlink(SOCK_NAME); } } int get_unix_socket(void) { return lsock; } void send_uevent(spnav_event *ev, struct client *c) { int i, data[8] = {0}; float motion_mul; if(!lsock) return; switch(ev->type) { case EVENT_MOTION: data[0] = UEV_TYPE_MOTION; motion_mul = get_client_sensitivity(c); for(i=0; i<6; i++) { float val = (float)ev->motion.data[i] * motion_mul; data[i + 1] = (int)val; } data[7] = ev->motion.period; break; case EVENT_BUTTON: data[0] = ev->button.press ? UEV_TYPE_PRESS : UEV_TYPE_RELEASE; data[1] = ev->button.bnum; break; default: break; } while(write(get_client_socket(c), data, sizeof data) == -1 && errno == EINTR); } int handle_uevents(fd_set *rset) { struct client *citer; if(lsock == -1) { return -1; } if(FD_ISSET(lsock, rset)) { /* got an incoming connection */ int s; if((s = accept(lsock, 0, 0)) == -1) { perror("error while accepting connection on the UNIX socket"); } else { if(!add_client(CLIENT_UNIX, &s)) { perror("failed to add client"); } } } /* all the UNIX socket clients */ citer = first_client(); while(citer) { struct client *c = citer; citer = next_client(); if(get_client_type(c) == CLIENT_UNIX) { int s = get_client_socket(c); if(FD_ISSET(s, rset)) { int rdbytes; float sens; /* got a request from a client, decode and execute it */ /* XXX currently only sensitivity comes from clients */ while((rdbytes = read(s, &sens, sizeof sens)) <= 0 && errno == EINTR); if(rdbytes <= 0) { /* something went wrong... disconnect client */ close(get_client_socket(c)); remove_client(c); continue; } set_client_sensitivity(c, sens); } } } return 0; } spacenavd-0.6/src/dev_serial.h0000644000175000017500000000160512127374572016260 0ustar nuclearnuclear/* spacenavd - a free software replacement driver for 6dof space-mice. Copyright (C) 2007-2012 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SPNAV_DEV_SERIAL_H_ #define SPNAV_DEV_SERIAL_H_ struct device; int open_dev_serial(struct device *dev); #endif /* SPNAV_DEV_SERIAL_H_ */ spacenavd-0.6/contrib/0000755000175000017500000000000012374256237014642 5ustar nuclearnuclearspacenavd-0.6/contrib/systemd/0000755000175000017500000000000012374256237016332 5ustar nuclearnuclearspacenavd-0.6/contrib/systemd/spacenavd.service0000644000175000017500000000033512045726041021647 0ustar nuclearnuclear[Unit] Description=3Dconnexion Input Devices Userspace Driver After=syslog.target [Service] Type=forking PIDFile=/var/run/spnavd.pid ExecStart=/usr/bin/spacenavd StandardError=syslog [Install] WantedBy=graphical.target spacenavd-0.6/contrib/README0000644000175000017500000000026312045726041015511 0ustar nuclearnuclearFiles in the contrib subdirectory are not maintained by the spacenav project maintainers. They are just 3rd party contributions included just in case they prove useful to anyone. spacenavd-0.6/doc/0000755000175000017500000000000012374256237013747 5ustar nuclearnuclearspacenavd-0.6/doc/example-spnavrc0000644000175000017500000000221511425140625016764 0ustar nuclearnuclear# This is an example configuration file for spacenavd. Uncomment and change # any settings you need and copy it to /etc/spnavrc. # # Note that it's much easier to configure sensitivities and figure out axis # reversals by using the interactive configure utility "spnavcfg", which lets # you fiddle with all the settings and see the result immediately. # # Lines that start with a `#' are comments, and are ignored by spacenavd. # Sensitivity is multiplied with every motion (1.0 normal). #sensitivity = 1.0 # Separate sensitivity for rotation and translation. #sensitivity-translation = 1.0 #sensitivity-rotation = 1.0 # Dead zone; any motion less than this number is discarded as noise. #dead-zone = 2 # Selectively invert translation and rotation axes. Valid values are # combinations of the letters x, y, and z. #invert-rot = yz #invert-trans = yz # Swap Y and Z axes #swap-yz = false # Serial device # Set this only if you have a serial device, and make sure you specify the # correct device file. If you do set this option, any USB devices will be # ignored! #serial = /dev/ttyS0 # Enable/disable LED light (for devices that have one). #led = on spacenavd-0.6/README0000644000175000017500000000524012374254355014062 0ustar nuclearnuclearspacenavd - 0.6 --------------- 1. About Spacenavd, is a free software replacement user-space driver (daemon), for 3Dconnexion's space-something 6dof input devices. It's compatible with the original 3dxsrv proprietary daemon provided by 3Dconnexion, and works perfectly with any program that was written for the 3Dconnexion driver. For more info on the spacenav project, visit: http://spacenav.sourceforge.net 2. Dependencies In order to compile the spacenavd daemon, you'll need the following: * GNU C Compiler * GNU make * Xlib headers (optional) You can compile the daemon without Xlib, but it won't be compatible with applications that where written for the original proprietary 3Dconnexion driver (e.g. blender, maya, etc). The 3dxsrv compatibility interface needs to go through the X window system. 3. Installation If you have the dependencies installed, just run ``./configure'' and then ``make'' to compile the daemon, and ``make install'', to install it. The default installation prefix is ``/usr/local''. If you wish to install somewhere else, you may pass --prefix=/whatever to the configure script. 4. Running spacenavd If your system uses SysV init, then you may run the supplied ``setup_init'' script, and everything should be set up to have spacenavd start automatically during system startup. However, for now, you won't have to reboot, just type ``/etc/init.d/spacenavd start'' as root, to start the daemon immediately. If your system uses BSD init (you can tell if you *don't* have a series of directories called rc0.d, rc1.d, and so on, in /etc), then you'll have to follow your init documentation and set this up yourself. You may be able to use the provided init_script file as a starting point. 5. Configuration The spacenavd daemon reads a number of options from the /etc/spnavrc file. If that file doesn't exist, then it uses default values for everything. You may use the graphical spnavcfg program to interactively set any of these options. The daemon should respond immediately to your changes, and also the configuration file should be updated automatically. An example configuration file is included in the doc subdirectory. 6. Troubleshooting If you're having trouble running spacenavd, read the up to date FAQ on the spacenav website: http://spacenav.sourceforge.net/faq.html If you're still having trouble, send a description of your problem to the spacenav-users mailing list: spacenav-users@lists.sourceforge.net along with a copy of your /var/log/spnavd.log and any other relevant information. 7. License This program is released under the terms of the GNU GPLv3, see COPYING for details. spacenavd-0.6/AUTHORS0000644000175000017500000000034011425213725014236 0ustar nuclearnuclearMain author and maintainer: John Tsiombikas Serial device support (libsball): John E. Stone Minor fixes: Doug LaRue, Krister Svanlun, Hans Meine, Jaroslaw Bulat, Pavel Frolov spacenavd-0.6/configure0000755000175000017500000001000611755073334015103 0ustar nuclearnuclear#!/bin/sh test_kver() { req_major=`echo $1 | awk -F . '{ print $1 }'` req_minor=`echo $1 | awk -F . '{ print $2 }'` req_rev=`echo $1 | awk -F . '{ print $3 }'` linux_rev=`uname -r | sed 's/-.*//'` kver_major=`echo $linux_rev | awk -F . '{ print $1 }'` kver_minor=`echo $linux_rev | awk -F . '{ print $2 }'` kver_rev=`echo $linux_rev | awk -F . '{ print $3 }'` if [ "$kver_major" -lt "$req_major" ]; then return 1 fi if [ "$kver_major" = "$req_major" ]; then if [ "$kver_minor" -lt "$req_minor" ]; then return 1 fi if [ "$kver_minor" = "$req_minor" -a "$kver_rev" -lt "$req_rev" ]; then return 1 fi fi return 0 } check_header() { echo "#include <$1>" >.chkhdr.c if cpp .chkhdr.c >/dev/null 2>&1; then echo "#define HAVE_`echo $1 | tr '[:lower:]' '[:upper:]' | sed 's/\./_/g'`" fi rm -f .chkhdr.c } PREFIX=/usr/local OPT=yes DBG=yes X11=yes HOTPLUG=yes VER=`head -1 README | sed 's/^.*- //'` if echo $VER | grep '$Rev' >/dev/null; then VER=svn-r`echo $VER | awk '{print $2}' | sed 's/$//g'` fi echo "configuring spacenavd - $VER" sys=`uname -s` if [ "$sys" = Linux ]; then # NETLINK_KOBJECT_UEVENT used for hotplug detection requires 2.6.10 if test_kver 2.6.10; then HOTPLUG=yes else HOTPLUG=no fi elif [ "$sys" = Darwin ]; then add_ldflags='-framework CoreFoundation -framework IOKit' else # TODO implement hotplug for other systems then switch this on HOTPLUG=no fi srcdir="`dirname "$0"`" # process arguments for arg; do case "$arg" in --prefix=*) value=`echo $arg | sed 's/--prefix=//'` PREFIX=${value:-$prefix} ;; --enable-opt) OPT=yes;; --disable-opt) OPT=no;; --enable-debug) DBG=yes;; --disable-debug) DBG=no;; --enable-x11) X11=yes;; --disable-x11) X11=no;; --enable-hotplug) HOTPLUG=yes;; --disable-hotplug) HOTPLUG=no;; --help) echo 'usage: ./configure [options]' echo 'options:' echo ' --prefix=: installation path (default: /usr/local)' echo ' --enable-x11: enable X11 communication mode (default)' echo ' --disable-x11: disable X11 communication mode' echo ' --enable-hotplug: enable hotplug using NETLINK_KOBJECT_UEVENT (default)' echo ' --disable-hotplug: disable hotplug, fallback to polling for the device' echo ' --enable-opt: enable speed optimizations (default)' echo ' --disable-opt: disable speed optimizations' echo ' --enable-debug: include debugging symbols (default)' echo ' --disable-debug: do not include debugging symbols' echo 'all invalid options are silently ignored' exit 0 ;; esac done echo " prefix: $PREFIX" echo " optimize for speed: $OPT" echo " include debugging symbols: $DBG" echo " x11 communication method: $X11" echo " use hotplug: $HOTPLUG" echo "" if [ "$X11" = "no" ]; then echo "WARNING: you have disabled the X11 interface, the resulting daemon \ won't be compatible with applications written for the proprietary 3Dconnexion \ daemon (3dxserv)!" echo fi # create Makefile echo 'creating Makefile ...' echo "PREFIX = $PREFIX" >Makefile echo "srcdir = $srcdir" >>Makefile echo "ver = $VER" >>Makefile if [ "$DBG" = 'yes' ]; then echo 'dbg = -g' >>Makefile fi if [ "$OPT" = 'yes' ]; then echo 'opt = -O2' >>Makefile fi if [ "$X11" = 'yes' ]; then echo 'xlib = -L/usr/X11/lib -lX11' >>Makefile fi if [ -n "$add_cflags" ]; then echo "add_cflags = $add_cflags" >>Makefile fi if [ -n "$add_ldflags" ]; then echo "add_ldflags = $add_ldflags" >>Makefile fi cat "$srcdir/Makefile.in" >>Makefile # create config.h echo 'creating config.h' echo '#ifndef CONFIG_H_' >src/config.h echo '#define CONFIG_H_' >>src/config.h echo >>src/config.h if [ "$X11" = yes ]; then echo '#define USE_X11' >>src/config.h echo >>src/config.h fi if [ "$HOTPLUG" = yes ]; then echo '#define USE_NETLINK' >>src/config.h echo >>src/config.h fi echo '#define VERSION "'$VER'"' >>src/config.h echo >>src/config.h # check for alloca.h check_header alloca.h >>src/config.h echo >>src/config.h echo '#endif /* CONFIG_H_ */' >>src/config.h echo '' echo 'Done. You can now type make (or gmake) to compile spacenavd.' echo '' spacenavd-0.6/COPYING0000644000175000017500000010451310642273060014226 0ustar nuclearnuclear GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .