kopia lustrzana https://github.com/FreeSpacenav/spacenavd
Porównaj commity
150 Commity
Autor | SHA1 | Data |
---|---|---|
John Tsiombikas | d7e6f2c7ba | |
John Tsiombikas | c6f8a6bf4a | |
John Tsiombikas | 9751030bfc | |
John Tsiombikas | ed8d255c27 | |
Rui Carmo | 39856625a6 | |
John Tsiombikas | 4ee37e2b75 | |
John Tsiombikas | 251cf549e4 | |
John Tsiombikas | 92e7f9c67b | |
Jaime van Kessel | 3000236988 | |
John Tsiombikas | 5d0a9639c4 | |
John Tsiombikas | 1d6fde6676 | |
John Tsiombikas | a2d121af16 | |
John Tsiombikas | 9b46aebfee | |
John Tsiombikas | 15e6848716 | |
Rickard Lind | 220436cd32 | |
Rickard Lind | 6df0c8d7d3 | |
Rickard Lind | 54e8333c6e | |
Rickard Lind | 83d45dd3a5 | |
John Meacham | edeb9bf3e2 | |
John Tsiombikas | d186c5d6ae | |
Alberto Fanjul | 3bcaf90519 | |
John Tsiombikas | d537c9b002 | |
John Tsiombikas | 0469e67524 | |
John Tsiombikas | 1723bcb3f7 | |
John Tsiombikas | a9c7cce212 | |
John Tsiombikas | 6640a155b0 | |
John Tsiombikas | 2e4909cd51 | |
John Tsiombikas | 6bc733eaae | |
John Tsiombikas | 9b8d20e52d | |
John Tsiombikas | aebe1999a6 | |
John Tsiombikas | d8f6de4697 | |
John Tsiombikas | b26464190f | |
John Tsiombikas | b748b4d980 | |
John Tsiombikas | ea02aee368 | |
John Tsiombikas | 919a86f6b5 | |
John Tsiombikas | b6351158fa | |
John Tsiombikas | 3e1cbc52ef | |
John Tsiombikas | 21d90d0db6 | |
John Tsiombikas | 8a3c9617a5 | |
John Tsiombikas | a067ab7a2d | |
John Tsiombikas | 2c5a443bf5 | |
John Tsiombikas | 8383f8d493 | |
John Tsiombikas | 01634327c3 | |
luz paz | 601817f03f | |
John Tsiombikas | 223d170aef | |
John Tsiombikas | 8355311364 | |
John Tsiombikas | 3a005aa21b | |
John Tsiombikas | c58eaa3bba | |
John Tsiombikas | b19e9c5bb1 | |
John Tsiombikas | 9020967ddb | |
John Tsiombikas | 0f1f40f38e | |
John Tsiombikas | b922cb75f8 | |
John Tsiombikas | 3fb72ed7ef | |
John Tsiombikas | 945fba993b | |
John Tsiombikas | 60cb7f6f13 | |
John Tsiombikas | 743d5eb3f1 | |
John Tsiombikas | a6e9f4b2ca | |
John Tsiombikas | 34adbc5799 | |
John Tsiombikas | 88585ca7d1 | |
John Tsiombikas | 6aaf85f598 | |
John Tsiombikas | 01e59bed7a | |
John Tsiombikas | bea696f24f | |
John Tsiombikas | 881feaed74 | |
John Tsiombikas | 59b0be26f8 | |
John Tsiombikas | b4dc387931 | |
John Tsiombikas | cf397150a8 | |
John Tsiombikas | 84c17ea404 | |
John Tsiombikas | 3cfc60b80e | |
John Tsiombikas | 8f062901b0 | |
John Tsiombikas | 2d0db49d0f | |
John Tsiombikas | 4505d67bb7 | |
John Tsiombikas | 04a883da90 | |
John Tsiombikas | 4ec6e2abcb | |
John Tsiombikas | 8f09c30e2d | |
John Tsiombikas | caaf31d759 | |
John Tsiombikas | 62cb5389c4 | |
John Tsiombikas | 7145788c5e | |
John Tsiombikas | 946a0874d2 | |
John Tsiombikas | 6e458f1676 | |
Alberto Fanjul | 4f7989d04b | |
John Tsiombikas | eb0287f6e8 | |
luz paz | bf0ea1ecf9 | |
John Tsiombikas | ab4751c4ea | |
John Tsiombikas | 7689859318 | |
Stephen Hurd | 07d8a39075 | |
Stephen Hurd | 816335c963 | |
John Tsiombikas | e2c809ee36 | |
John Tsiombikas | 58bc16dd0c | |
John Tsiombikas | 9f0870ed51 | |
Stephen Hurd | 9c5976d41c | |
Stephen Hurd | 02a9806b9d | |
John Tsiombikas | d56c0e502c | |
John Tsiombikas | 2a86295910 | |
Stephen Hurd | 4cc34b78fe | |
Stephen Hurd | 8d3ab7e514 | |
Stephen Hurd | d0469c5fb6 | |
Stephen Hurd | 8cdfa0c282 | |
Stephen Hurd | 1b3d9770fe | |
Stephen Hurd | 5f91c9c107 | |
Stephen Hurd | f94c344a75 | |
Stephen Hurd | e4d1cdfd15 | |
Stephen Hurd | 1b5cd4538a | |
Stephen Hurd | 794a6b8e1a | |
Stephen Hurd | 4fd3dc3c13 | |
Stephen Hurd | 9506c0e295 | |
Stephen Hurd | dafaf8c4c7 | |
Stephen Hurd | a5c51ded53 | |
Stephen Hurd | 2ad357f13c | |
Stephen Hurd | d90fa571ea | |
Stephen Hurd | 8bb892b5a1 | |
John Tsiombikas | 83b6124188 | |
Stephen Hurd | ccafde465a | |
John Tsiombikas | c370884e65 | |
John Tsiombikas | 9f748d16fe | |
John Tsiombikas | c5288167d1 | |
John Tsiombikas | 4c503c2d1d | |
John Tsiombikas | 80e1eab670 | |
Gaël Écorchard | d79a2e36d0 | |
John Tsiombikas | c408cbeaa9 | |
John Tsiombikas | 6bd07cf0f6 | |
John Tsiombikas | 6972066793 | |
John Tsiombikas | 4060531a2b | |
Gabriel Ebner | fbf9019470 | |
John Tsiombikas | fc6ba4e7a5 | |
Stéphan Kochen | d6a25d5c3f | |
John Tsiombikas | 7e14d4892e | |
John Tsiombikas | d3a3aa017a | |
John Tsiombikas | 3b2eeb011f | |
John Tsiombikas | e88d9dbe85 | |
John Tsiombikas | 18ae3074a4 | |
John Tsiombikas | d330773cae | |
John Tsiombikas | 486645527f | |
John Tsiombikas | 26c840da63 | |
John Tsiombikas | 11a43b8be5 | |
datadog23 | 81a32c8752 | |
John Tsiombikas | e3d941caac | |
John Tsiombikas | 7c271fa265 | |
John Tsiombikas | 0e7ee937e0 | |
John Tsiombikas | a9221a74ed | |
John Tsiombikas | 407416d8f3 | |
John Tsiombikas | ed40a14ad7 | |
John Tsiombikas | 3168b1dedf | |
Sebastian Pipping | d84e336c25 | |
John Tsiombikas | d16ef85b7e | |
John Tsiombikas | 081182f167 | |
John Tsiombikas | 8acf883d97 | |
John Tsiombikas | e1de3389a6 | |
tpiekar | 5b3a76b84d | |
Thomas | 4d2c0117e9 | |
tpiekar | 278e608d99 |
|
@ -0,0 +1,32 @@
|
|||
name: FreeBSD build
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: FreeBSD build
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
prepare: |
|
||||
pkg install -y git gmake libX11 libXi libXtst
|
||||
|
||||
run: |
|
||||
./configure
|
||||
gmake
|
||||
gmake DESTDIR=spacenavd-freebsd install
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: spacenavd-freebsd
|
||||
path: spacenavd-freebsd
|
||||
|
||||
# vi:ts=2 sts=2 sw=2 expandtab:
|
|
@ -0,0 +1,57 @@
|
|||
name: GNU/Linux build
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libx11-dev libxi-dev libxtst-dev
|
||||
|
||||
- name: configure
|
||||
run: ./configure
|
||||
|
||||
- name: build
|
||||
run: make
|
||||
|
||||
- name: stage install
|
||||
run: DESTDIR=spacenavd-gnulinux make install
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: spacenavd-gnulinux
|
||||
path: spacenavd-gnulinux
|
||||
|
||||
build-nox11:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: configure
|
||||
run: ./configure --disable-x11
|
||||
|
||||
- name: build
|
||||
run: make
|
||||
|
||||
- name: stage install
|
||||
run: DESTDIR=spacenavd-gnulinux-nox11 make install
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: spacenavd-gnulinux-nox11
|
||||
path: spacenavd-gnulinux-nox11
|
||||
|
||||
|
||||
# vi:ts=2 sts=2 sw=2 expandtab:
|
|
@ -0,0 +1,55 @@
|
|||
name: MacOS X build
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: install dependencies
|
||||
run: |
|
||||
brew install libx11 libxi libxtst
|
||||
|
||||
- name: configure
|
||||
run: ./configure
|
||||
|
||||
- name: build
|
||||
run: make
|
||||
|
||||
- name: stage install
|
||||
run: DESTDIR=spacenavd-macosx make install
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: spacenavd-macosx
|
||||
path: spacenavd-macosx
|
||||
|
||||
build-nox11:
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: configure
|
||||
run: ./configure --disable-x11
|
||||
|
||||
- name: build
|
||||
run: make
|
||||
|
||||
- name: stage install
|
||||
run: DESTDIR=spacenavd-macosx-nox11 make install
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: spacenavd-macosx-nox11
|
||||
path: spacenavd-macosx-nox11
|
||||
|
||||
# vi:ts=2 sts=2 sw=2 expandtab:
|
18
AUTHORS
18
AUTHORS
|
@ -1,12 +1,20 @@
|
|||
Main author and maintainer:
|
||||
John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
Serial device support (libsball):
|
||||
John E. Stone <j.stone@acm.org>
|
||||
|
||||
Contributors:
|
||||
Doug LaRue, Krister Svanlun, Hans Meine, Jaroslaw Bulat, Pavel Frolov,
|
||||
Robert Haschke, David Lister
|
||||
Doug LaRue
|
||||
Krister Svanlun
|
||||
Hans Meine
|
||||
Jaroslaw Bulat
|
||||
Pavel Frolov,
|
||||
Robert Haschke
|
||||
David Lister
|
||||
Stephen Hurd
|
||||
Gaël Écorchard
|
||||
Alberto Fanjul
|
||||
|
||||
Serial device support in early versions (before the v0.8 rewrite):
|
||||
John E. Stone <j.stone@acm.org>
|
||||
|
||||
NOTE: This file has been left untended for quite a while. There are bound to be
|
||||
contributions from people not listed here. If you ever contributed code to this
|
||||
|
|
29
Makefile.in
29
Makefile.in
|
@ -1,14 +1,16 @@
|
|||
src = $(sort $(wildcard src/*.c) $(wildcard src/serial/*.c) $(wildcard src/magellan/*.c))
|
||||
hdr = $(wildcard src/*.h) $(wildcard src/serial/*.h) $(wildcard src/magellan/*.h)
|
||||
src = $(sort $(wildcard src/*.c))
|
||||
hdr = $(wildcard src/*.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)
|
||||
warn = -pedantic -Wall
|
||||
|
||||
CC ?= gcc
|
||||
CFLAGS = $(warn) $(dbg) $(opt) -fno-strict-aliasing -fcommon \
|
||||
-I$(srcdir)/src $(xinc) -MMD $(add_cflags)
|
||||
LDFLAGS = $(xlib) $(add_ldflags) -lm
|
||||
|
||||
$(bin): $(obj)
|
||||
$(CC) -o $@ $(obj) $(LDFLAGS)
|
||||
|
@ -21,9 +23,6 @@ tags: $(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)
|
||||
|
@ -34,18 +33,12 @@ cleandep:
|
|||
|
||||
.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)
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||
cp $(bin) $(DESTDIR)$(PREFIX)/bin/$(bin)
|
||||
cp $(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
|
||||
|
|
34
README.md
34
README.md
|
@ -1,6 +1,10 @@
|
|||
spacenavd
|
||||
=========
|
||||
|
||||
![GNU/Linux build status](https://github.com/FreeSpacenav/spacenavd/actions/workflows/build_gnulinux.yml/badge.svg)
|
||||
![FreeBSD build status](https://github.com/FreeSpacenav/spacenavd/actions/workflows/build_freebsd.yml/badge.svg)
|
||||
![MacOS X build status](https://github.com/FreeSpacenav/spacenavd/actions/workflows/build_macosx.yml/badge.svg)
|
||||
|
||||
About
|
||||
-----
|
||||
Spacenavd is a free software user-space driver (daemon), for 6-dof input
|
||||
|
@ -12,12 +16,19 @@ with spacenavd.
|
|||
|
||||
For more info on the spacenav project, visit: http://spacenav.sourceforge.net
|
||||
|
||||
> We are currently in the process of documenting how button numbers relate to
|
||||
> physical buttons, across all 6dof devices:
|
||||
> https://github.com/FreeSpacenav/spacenavd/wiki/Device-button-names
|
||||
>
|
||||
> If you have a device which is missing from that wiki page, please help us
|
||||
> expand the database by adding it, or send us the information through email.
|
||||
|
||||
License
|
||||
-------
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
This program is free software. Feel free to copy, modify and/or redistribute it
|
||||
under the terms of the GNU General Public Licens version 3, or at your option,
|
||||
under the terms of the GNU General Public License version 3, or at your option,
|
||||
any later version published by the Free Software Foundation. See COPYING for
|
||||
details.
|
||||
|
||||
|
@ -26,7 +37,9 @@ Dependencies
|
|||
In order to compile the spacenavd daemon, you'll need the following:
|
||||
- GNU C Compiler
|
||||
- GNU make
|
||||
- Xlib headers (optional)
|
||||
- Xlib (libX11, optional)
|
||||
- XInput2 (libXi, optional)
|
||||
- Xtest (libXtst, 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
|
||||
|
@ -65,18 +78,23 @@ that file doesn't exist, then it will use default values for everything. An
|
|||
example configuration file is included in the doc subdirectory, which you may
|
||||
copy to `/etc` and tweak.
|
||||
|
||||
You may use the graphical spnavcfg program to interactively set any of these
|
||||
options, which will create the spnavrc file for you, and automatically notify
|
||||
the daemon to re-read it.
|
||||
You may use the graphical spnavcfg program to interactively set and tweak any
|
||||
of the configuration options.
|
||||
|
||||
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 not sure if spacenavd is set up correctly and works with your device,
|
||||
a good first step is to try and run the "simple" example program which comes
|
||||
with libspnav. It builds into two variants: `simple_af_unix` and `simple_x11`,
|
||||
which is helpful for testing both supported communication protocols. If either
|
||||
or both fail to work, there's something wrong with your setup.
|
||||
|
||||
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.
|
||||
|
||||
If you have encountered a bug, please file a bug report in our bug tracking
|
||||
system: https://sourceforge.net/p/spacenav/bugs/
|
||||
If you have encountered a bug, please file a bug report in our bug tracker:
|
||||
https://github.com/FreeSpacenav/spacenavd/issues
|
||||
|
|
|
@ -26,9 +26,13 @@ test_kver() {
|
|||
}
|
||||
|
||||
check_header() {
|
||||
printf "Looking for header: $1 ... " >&2
|
||||
echo "#include <$1>" >.chkhdr.c
|
||||
if cpp .chkhdr.c >/dev/null 2>&1; then
|
||||
if cpp -I/usr/local/include $x11inc .chkhdr.c >/dev/null 2>&1; then
|
||||
echo found >&2
|
||||
echo "#define HAVE_`basename $1 | tr '[:lower:]' '[:upper:]' | sed 's/\./_/g'`"
|
||||
else
|
||||
echo not found >&2
|
||||
fi
|
||||
rm -f .chkhdr.c
|
||||
}
|
||||
|
@ -41,9 +45,16 @@ X11=yes
|
|||
HOTPLUG=yes
|
||||
XINPUT=yes
|
||||
VER=`git describe --tags 2>/dev/null`
|
||||
CFGDIR=/etc
|
||||
|
||||
if [ -z "$VER" ]; then
|
||||
VER=`git rev-parse --short HEAD`
|
||||
if [ -z "$VER" ]; then
|
||||
VER=v`pwd | grep 'spacenavd-[0-9]\+\.' | sed 's/.*spacenavd-\(\([0-9]\+\.\)\+[0-9]\+\).*$/\1/'`
|
||||
if [ $VER = v ]; then
|
||||
VER='<unknown version>'
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "configuring spacenavd - $VER"
|
||||
|
@ -57,7 +68,7 @@ if [ "$sys" = Linux ]; then
|
|||
HOTPLUG=no
|
||||
fi
|
||||
elif [ "$sys" = Darwin ]; then
|
||||
add_ldflags='-framework CoreFoundation -framework IOKit'
|
||||
LDFLAGS='-framework CoreFoundation -framework IOKit'
|
||||
else
|
||||
# TODO implement hotplug for other systems then switch this on
|
||||
HOTPLUG=no
|
||||
|
@ -72,6 +83,11 @@ for arg; do
|
|||
value=`echo $arg | sed 's/--prefix=//'`
|
||||
PREFIX=${value:-$prefix}
|
||||
;;
|
||||
|
||||
--cfgdir=*)
|
||||
value=`echo $arg | sed 's/--cfgdir=//'`
|
||||
CFGDIR=${value:-$cfgdir}
|
||||
;;
|
||||
|
||||
--enable-opt)
|
||||
OPT=yes;;
|
||||
|
@ -112,17 +128,48 @@ for arg; do
|
|||
done
|
||||
|
||||
echo " prefix: $PREFIX"
|
||||
echo " config dir: $CFGDIR"
|
||||
echo " optimize for speed: $OPT"
|
||||
echo " include debugging symbols: $DBG"
|
||||
echo " x11 communication method: $X11"
|
||||
echo " use hotplug: $HOTPLUG"
|
||||
echo ""
|
||||
|
||||
|
||||
HAVE_ALLOCA_H=`check_header alloca.h`
|
||||
HAVE_MALLOC_H=`check_header malloc.h`
|
||||
HAVE_STDINT_H=`check_header stdint.h`
|
||||
HAVE_INTTYPES_H=`check_header inttypes.h`
|
||||
|
||||
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
|
||||
else
|
||||
# find alternate X11 header/lib paths
|
||||
if [ -e /usr/local/include/X11/Xlib.h ]; then
|
||||
x11prefix='/usr/local'
|
||||
elif [ -e /usr/X11/include/X11/Xlib.h ]; then
|
||||
x11prefix='/usr/X11'
|
||||
elif [ -e /usr/X11R6/include/X11/Xlib.h ]; then
|
||||
x11prefix='/usr/X11R6'
|
||||
elif [ -e /opt/homebrew/include/X11/Xlib.h ]; then
|
||||
x11prefix='/opt/homebrew'
|
||||
fi
|
||||
if [ -n "$x11prefix" ]; then
|
||||
echo "X11 prefix: $x11prefix"
|
||||
x11inc=-I$x11prefix/include
|
||||
x11lib=-L$x11prefix/lib
|
||||
fi
|
||||
|
||||
HAVE_XINPUT2_H=`check_header X11/extensions/XInput2.h`
|
||||
HAVE_XTEST_H=`check_header X11/extensions/XTest.h`
|
||||
|
||||
if [ -z "$HAVE_XTEST_H" ]; then
|
||||
echo "WARNING: building without XTEST support, makes keyboard emulation \
|
||||
less reliable (fallback to XSendEvent)."
|
||||
fi
|
||||
fi
|
||||
|
||||
# create Makefile
|
||||
|
@ -140,44 +187,59 @@ if [ "$OPT" = 'yes' ]; then
|
|||
fi
|
||||
|
||||
if [ "$X11" = 'yes' ]; then
|
||||
echo 'xlib = -L/usr/X11/lib -lX11' >>Makefile
|
||||
if [ -n "`check_header X11/extensions/XInput2.h 2>&1`" ]; then
|
||||
echo "xinc = $x11inc" >>Makefile
|
||||
echo "xlib = $x11lib" >>Makefile
|
||||
|
||||
if [ -n "$HAVE_XINPUT2_H" ]; then
|
||||
echo 'xlib += -lXi' >>Makefile
|
||||
fi
|
||||
if [ -n "$HAVE_XTEST_H" ]; then
|
||||
echo xlib += -lXtst >>Makefile
|
||||
fi
|
||||
echo 'xlib += -lX11 -lXext' >>Makefile
|
||||
fi
|
||||
|
||||
if [ -n "$add_cflags" ]; then
|
||||
echo "add_cflags = $add_cflags" >>Makefile
|
||||
if [ -n "$CFLAGS" ]; then
|
||||
echo "add_cflags = $CFLAGS" >>Makefile
|
||||
fi
|
||||
if [ -n "$add_ldflags" ]; then
|
||||
echo "add_ldflags = $add_ldflags" >>Makefile
|
||||
if [ -n "$LDFLAGS" ]; then
|
||||
echo "add_ldflags = $LDFLAGS" >>Makefile
|
||||
fi
|
||||
|
||||
cat "$srcdir/Makefile.in" >>Makefile
|
||||
|
||||
|
||||
# create config.h
|
||||
cfgheader=$srcdir/src/config.h
|
||||
|
||||
echo 'creating config.h'
|
||||
echo '#ifndef CONFIG_H_' >src/config.h
|
||||
echo '#define CONFIG_H_' >>src/config.h
|
||||
echo >>src/config.h
|
||||
echo '#ifndef CONFIG_H_' >$cfgheader
|
||||
echo '#define CONFIG_H_' >>$cfgheader
|
||||
echo >>$cfgheader
|
||||
if [ "$X11" = yes ]; then
|
||||
echo '#define USE_X11' >>src/config.h
|
||||
echo >>src/config.h
|
||||
echo '#define USE_X11' >>$cfgheader
|
||||
echo >>$cfgheader
|
||||
fi
|
||||
if [ "$HOTPLUG" = yes ]; then
|
||||
echo '#define USE_NETLINK' >>src/config.h
|
||||
echo >>src/config.h
|
||||
echo '#define USE_NETLINK' >>$cfgheader
|
||||
echo >>$cfgheader
|
||||
fi
|
||||
echo '#define VERSION "'$VER'"' >>src/config.h
|
||||
echo >>src/config.h
|
||||
echo '#define VERSION "'$VER'"' >>$cfgheader
|
||||
echo >>$cfgheader
|
||||
|
||||
# check for alloca.h
|
||||
check_header alloca.h >>src/config.h
|
||||
check_header X11/extensions/XInput2.h >>src/config.h
|
||||
[ -n "$HAVE_ALLOCA_H" ] && echo $HAVE_ALLOCA_H >>$cfgheader
|
||||
[ -n "$HAVE_MALLOC_H" ] && echo $HAVE_MALLOC_H >>$cfgheader
|
||||
[ -n "$HAVE_STDINT_H" ] && echo $HAVE_STDINT_H >>$cfgheader
|
||||
[ -n "$HAVE_INTTYPES_H" ] && echo $HAVE_INTTYPES_H >>$cfgheader
|
||||
[ -n "$HAVE_XINPUT2_H" ] && echo $HAVE_XINPUT2_H >>$cfgheader
|
||||
[ -n "$HAVE_XTEST_H" ] && echo $HAVE_XTEST_H >>$cfgheader
|
||||
echo >>$cfgheader
|
||||
|
||||
echo >>src/config.h
|
||||
echo '#endif /* CONFIG_H_ */' >>src/config.h
|
||||
echo "#define CFGDIR \"$CFGDIR\"" >>$cfgheader
|
||||
echo >>$cfgheader
|
||||
|
||||
echo '#endif /* CONFIG_H_ */' >>$cfgheader
|
||||
|
||||
echo ''
|
||||
echo 'Done. You can now type make (or gmake) to compile spacenavd.'
|
||||
|
|
|
@ -5,7 +5,7 @@ After=syslog.target
|
|||
[Service]
|
||||
Type=forking
|
||||
PIDFile=/var/run/spnavd.pid
|
||||
ExecStart=/usr/bin/spacenavd
|
||||
ExecStart=/usr/local/bin/spacenavd
|
||||
StandardError=syslog
|
||||
|
||||
[Install]
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#sensitivity-translation = 1.0
|
||||
#sensitivity-rotation = 1.0
|
||||
|
||||
# Separate sensitivity for each roation and translation axis.
|
||||
# Separate sensitivity for each rotation and translation axis.
|
||||
#
|
||||
#sensitivity-translation-x = 1.0
|
||||
#sensitivity-translation-y = 1.0
|
||||
|
@ -32,6 +32,8 @@
|
|||
#dead-zone = 2
|
||||
|
||||
# Separate dead-zone for each rotation and translation axis.
|
||||
# This is still supported, but unclear in face of device axes mapping, and
|
||||
# therefore you are encouraged to use dead-zoneN instead.
|
||||
#
|
||||
#dead-zone-translation-x = 2
|
||||
#dead-zone-translation-y = 2
|
||||
|
@ -40,6 +42,12 @@
|
|||
#dead-zone-rotation-y = 2
|
||||
#dead-zone-rotation-z = 2
|
||||
|
||||
# Separate dead-zone for each device axis.
|
||||
#
|
||||
#dead-zone0 = 2
|
||||
#dead-zone1 = 2
|
||||
# ...
|
||||
#dead-zoneN = 2
|
||||
|
||||
# Selectively invert translation and rotation axes. Valid values are
|
||||
# combinations of the letters x, y, and z.
|
||||
|
@ -68,6 +76,20 @@
|
|||
#...
|
||||
#bnmapN = N (N < 64)
|
||||
|
||||
# Button action mapping
|
||||
# Use the following syntax:
|
||||
# bnactN = <action>
|
||||
# Where N is the button number (zero-based), and action is one of the following
|
||||
# available actions:
|
||||
# none,
|
||||
# sensitivity-up, sensitivity-down, sensitivity-reset
|
||||
# disable-rotation
|
||||
# disable-translation
|
||||
# dominant-axis
|
||||
#
|
||||
#bnact16 = sensitivity-up
|
||||
#bnact17 = sensitivity-down
|
||||
#bnact18 = sensitivity-reset
|
||||
|
||||
# Map buttons to keyboard keys (experimental)
|
||||
# By default no such mappings are active. Use the following syntax:
|
||||
|
@ -122,17 +144,3 @@
|
|||
# the re-centering power of the device.
|
||||
#
|
||||
#repeat-interval = -1
|
||||
|
||||
|
||||
# Log file path
|
||||
#
|
||||
#logfile = /var/log/spnavd.log
|
||||
|
||||
# Log targets
|
||||
#
|
||||
# Valid options are:
|
||||
# - file: log messages to a file defined by the logfile option.
|
||||
# - syslog: log messages to the system logging daemon.
|
||||
# Combine multiple options by listing them on the same line.
|
||||
#
|
||||
#log = file, syslog
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# Sensible button mappings for the SpaceMouse Enterprise
|
||||
# Copy to /etc/spnavrc to use
|
||||
|
||||
bnact2 = sensitivity-reset
|
||||
bnact8 = sensitivity-down
|
||||
bnact9 = sensitivity-up
|
||||
kbmap18 = Escape
|
||||
bnact22 = disable-rotation
|
||||
kbmap13 = KP_Delete
|
||||
kbmap20 = Shift_L
|
||||
kbmap21 = Control_L
|
||||
kbmap24 = Delete
|
||||
kbmap25 = Tab
|
||||
kbmap26 = space
|
|
@ -0,0 +1,15 @@
|
|||
# Sensible button mappings for the original SpacePilot
|
||||
# Copy to /etc/spnavrc to use
|
||||
|
||||
kbmap6 = KP_7
|
||||
kbmap8 = KP_3
|
||||
kbmap9 = KP_1
|
||||
kbmap11 = Alt_L
|
||||
kbmap12 = Shift_L
|
||||
kbmap13 = Control_L
|
||||
kbmap14 = KP_Delete
|
||||
kbmap19 = KP_5
|
||||
bnact16 = sensitivity-up
|
||||
bnact17 = sensitivity-down
|
||||
bnact18 = sensitivity-reset
|
||||
kbmap10 = Escape
|
|
@ -0,0 +1,40 @@
|
|||
#!/bin/sh
|
||||
# helper script to prepare a release
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo 'pass the release version number' >&2
|
||||
exit 1
|
||||
fi
|
||||
if echo $1 | grep -v '[0-9\.]' >/dev/null; then
|
||||
echo 'invalid release number, try something like "1.2"' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ver=$1
|
||||
name=$(pwd | xargs basename)
|
||||
fullname=$name-$ver
|
||||
|
||||
if git tag | grep "^v$ver$" >/dev/null; then
|
||||
echo "release tag v$ver already exists" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "tagging v$ver ..."
|
||||
git tag v$ver
|
||||
echo "preparing tarball: $fullname.tar.gz ..."
|
||||
git archive -o $fullname.tar.gz --prefix=$fullname/ v$ver
|
||||
|
||||
# fixup VER in the configure script
|
||||
echo "fixing up version in configure script to not rely on git ..."
|
||||
orig_pwd=`pwd`
|
||||
mkdir -p /tmp/mkrelfix
|
||||
cd /tmp/mkrelfix
|
||||
tar xzvf $orig_pwd/$fullname.tar.gz
|
||||
sed -i "s/^VER=.*/VER=$ver/" $fullname/configure
|
||||
# also while we're at it, remove this script too
|
||||
rm -f $fullname/`basename $0`
|
||||
tar czvf $orig_pwd/$fullname.tar.gz $fullname
|
||||
|
||||
echo
|
||||
echo "Done. If there was some mistake delete the tag with: git tag -d v$ver"
|
||||
echo 'Otherwise, push the new tag with: git push --tags, and upload the tarball.'
|
632
src/cfgfile.c
632
src/cfgfile.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -19,17 +19,58 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include "cfgfile.h"
|
||||
#include "logger.h"
|
||||
#include "spnavd.h"
|
||||
|
||||
/* all parsable config options... some of them might map to the same cfg field */
|
||||
enum {
|
||||
CFG_REPEAT,
|
||||
CFG_DEADZONE, CFG_DEADZONE_N,
|
||||
CFG_DEADZONE_TX, CFG_DEADZONE_TY, CFG_DEADZONE_TZ,
|
||||
CFG_DEADZONE_RX, CFG_DEADZONE_RY, CFG_DEADZONE_RZ,
|
||||
CFG_SENS,
|
||||
CFG_SENS_TRANS, CFG_SENS_TX, CFG_SENS_TY, CFG_SENS_TZ,
|
||||
CFG_SENS_ROT, CFG_SENS_RX, CFG_SENS_RY, CFG_SENS_RZ,
|
||||
CFG_INVROT, CFG_INVTRANS, CFG_SWAPYZ,
|
||||
CFG_AXISMAP_N, CFG_BNMAP_N, CFG_BNACT_N, CFG_KBMAP_N,
|
||||
CFG_LED, CFG_GRAB,
|
||||
CFG_SERIAL, CFG_DEVID,
|
||||
|
||||
NUM_CFG_OPTIONS
|
||||
};
|
||||
|
||||
enum { RMCFG_ALL, RMCFG_OWN };
|
||||
|
||||
/* number of lines to add to the cfglines allocation, in order to allow for
|
||||
* adding any number of additional options if necessary
|
||||
*/
|
||||
#define NUM_EXTRA_LINES (NUM_CFG_OPTIONS + MAX_CUSTOM + MAX_BUTTONS * 3 + MAX_AXES + 16)
|
||||
|
||||
static int parse_bnact(const char *s);
|
||||
static const char *bnact_name(int bnact);
|
||||
static int add_cfgopt(int opt, int idx, const char *fmt, ...);
|
||||
static int add_cfgopt_devid(int vid, int pid);
|
||||
static int rm_cfgopt(const char *name, int mode);
|
||||
|
||||
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};
|
||||
struct cfgline {
|
||||
char *str; /* actual line text */
|
||||
int opt; /* CFG_* item */
|
||||
int idx;
|
||||
int own; /* added and owned by spacenavd, not in the original user config */
|
||||
};
|
||||
|
||||
static struct cfgline *cfglines;
|
||||
static int num_lines;
|
||||
|
||||
|
||||
void default_cfg(struct cfg *cfg)
|
||||
{
|
||||
|
@ -50,8 +91,7 @@ void default_cfg(struct cfg *cfg)
|
|||
cfg->grab_device = 1;
|
||||
|
||||
for(i=0; i<6; i++) {
|
||||
cfg->invert[i] = def_axinv[i];
|
||||
cfg->map_axis[i] = def_axmap[i];
|
||||
cfg->map_axis[i] = i;
|
||||
}
|
||||
|
||||
for(i=0; i<MAX_BUTTONS; i++) {
|
||||
|
@ -68,6 +108,15 @@ void default_cfg(struct cfg *cfg)
|
|||
}
|
||||
}
|
||||
|
||||
void unlock_cfgfile(int fd)
|
||||
{
|
||||
struct flock flk;
|
||||
flk.l_type = F_UNLCK;
|
||||
flk.l_start = flk.l_len = 0;
|
||||
flk.l_whence = SEEK_SET;
|
||||
fcntl(fd, F_SETLK, &flk);
|
||||
}
|
||||
|
||||
#define EXPECT(cond) \
|
||||
do { \
|
||||
if(!(cond)) { \
|
||||
|
@ -79,10 +128,11 @@ void default_cfg(struct cfg *cfg)
|
|||
int read_cfg(const char *fname, struct cfg *cfg)
|
||||
{
|
||||
FILE *fp;
|
||||
int i, c, fd;
|
||||
char buf[512];
|
||||
struct flock flk;
|
||||
int num_devid = 0;
|
||||
/*int num_devnames = 0;*/
|
||||
struct cfgline *lptr;
|
||||
|
||||
default_cfg(cfg);
|
||||
|
||||
|
@ -91,21 +141,52 @@ int read_cfg(const char *fname, struct cfg *cfg)
|
|||
logmsg(LOG_WARNING, "failed to open config file %s: %s. using defaults.\n", fname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
fd = fileno(fp);
|
||||
|
||||
/* acquire 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(fcntl(fd, F_SETLKW, &flk) == -1);
|
||||
|
||||
/* count newlines and populate lines array */
|
||||
num_lines = 0;
|
||||
while((c = fgetc(fp)) != -1) {
|
||||
if(c == '\n') num_lines++;
|
||||
}
|
||||
rewind(fp);
|
||||
if(!num_lines) num_lines = 1;
|
||||
|
||||
/* add enough lines to be able to append any number of new options */
|
||||
free(cfglines);
|
||||
if(!(cfglines = calloc(num_lines + NUM_EXTRA_LINES, sizeof *cfglines))) {
|
||||
logmsg(LOG_WARNING, "failed to allocate config lines buffer (%d lines)\n", num_lines);
|
||||
unlock_cfgfile(fd);
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parse config file */
|
||||
num_lines = 0;
|
||||
while(fgets(buf, sizeof buf, fp)) {
|
||||
int isint, isfloat, ival, i, bnidx, axisidx;
|
||||
int isint, isfloat, ival, bnidx, axisidx;
|
||||
float fval;
|
||||
char *endp, *key_str, *val_str, *line = buf;
|
||||
|
||||
lptr = cfglines + num_lines++;
|
||||
|
||||
if((endp = strchr(buf, '\r')) || (endp = strchr(buf, '\n'))) {
|
||||
*endp = 0;
|
||||
}
|
||||
if(!(lptr->str = strdup(buf))) {
|
||||
logmsg(LOG_WARNING, "failed to allocate config line buffer, skipping line %d.\n", num_lines);
|
||||
continue;
|
||||
}
|
||||
|
||||
while(*line == ' ' || *line == '\t') line++;
|
||||
|
||||
if(!*line || *line == '\n' || *line == '\r' || *line == '#') {
|
||||
continue;
|
||||
continue; /* ignore comments and empty lines */
|
||||
}
|
||||
|
||||
if(!(key_str = strtok(line, " =\n\t\r"))) {
|
||||
|
@ -124,117 +205,146 @@ int read_cfg(const char *fname, struct cfg *cfg)
|
|||
isfloat = (endp > val_str);
|
||||
|
||||
if(strcmp(key_str, "repeat-interval") == 0) {
|
||||
lptr->opt = CFG_REPEAT;
|
||||
EXPECT(isint);
|
||||
cfg->repeat_msec = ival;
|
||||
|
||||
} else if(strcmp(key_str, "dead-zone") == 0) {
|
||||
lptr->opt = CFG_DEADZONE;
|
||||
EXPECT(isint);
|
||||
for(i=0; i<6; i++) {
|
||||
for(i=0; i<MAX_AXES; i++) {
|
||||
cfg->dead_threshold[i] = ival;
|
||||
}
|
||||
|
||||
} else if(sscanf(key_str, "dead-zone%d", &axisidx) == 1) {
|
||||
if(axisidx < 0 || axisidx >= MAX_AXES) {
|
||||
logmsg(LOG_WARNING, "invalid option %s, valid input axis numbers 0 - %d\n", key_str, MAX_AXES - 1);
|
||||
continue;
|
||||
}
|
||||
lptr->opt = CFG_DEADZONE_N;
|
||||
lptr->idx = axisidx;
|
||||
cfg->dead_threshold[axisidx] = ival;
|
||||
|
||||
} else if(strcmp(key_str, "dead-zone-translation-x") == 0) {
|
||||
logmsg(LOG_WARNING, "Deprecated option: %s. You are encouraged to use dead-zoneN instead\n", key_str);
|
||||
lptr->opt = CFG_DEADZONE_TX;
|
||||
EXPECT(isint);
|
||||
cfg->dead_threshold[0] = ival;
|
||||
|
||||
} else if(strcmp(key_str, "dead-zone-translation-y") == 0) {
|
||||
logmsg(LOG_WARNING, "Deprecated option: %s. You are encouraged to use dead-zoneN instead\n", key_str);
|
||||
lptr->opt = CFG_DEADZONE_TY;
|
||||
EXPECT(isint);
|
||||
cfg->dead_threshold[1] = ival;
|
||||
|
||||
} else if(strcmp(key_str, "dead-zone-translation-z") == 0) {
|
||||
logmsg(LOG_WARNING, "Deprecated option: %s. You are encouraged to use dead-zoneN instead\n", key_str);
|
||||
lptr->opt = CFG_DEADZONE_TZ;
|
||||
EXPECT(isint);
|
||||
cfg->dead_threshold[2] = ival;
|
||||
|
||||
} else if(strcmp(key_str, "dead-zone-rotation-x") == 0) {
|
||||
logmsg(LOG_WARNING, "Deprecated option: %s. You are encouraged to use dead-zoneN instead\n", key_str);
|
||||
lptr->opt = CFG_DEADZONE_RX;
|
||||
EXPECT(isint);
|
||||
cfg->dead_threshold[3] = ival;
|
||||
|
||||
} else if(strcmp(key_str, "dead-zone-rotation-y") == 0) {
|
||||
logmsg(LOG_WARNING, "Deprecated option: %s. You are encouraged to use dead-zoneN instead\n", key_str);
|
||||
lptr->opt = CFG_DEADZONE_RY;
|
||||
EXPECT(isint);
|
||||
cfg->dead_threshold[4] = ival;
|
||||
|
||||
} else if(strcmp(key_str, "dead-zone-rotation-z") == 0) {
|
||||
logmsg(LOG_WARNING, "Deprecated option: %s. You are encouraged to use dead-zoneN instead\n", key_str);
|
||||
lptr->opt = CFG_DEADZONE_RZ;
|
||||
EXPECT(isint);
|
||||
cfg->dead_threshold[5] = ival;
|
||||
|
||||
} else if(strcmp(key_str, "sensitivity") == 0) {
|
||||
lptr->opt = CFG_SENS;
|
||||
EXPECT(isfloat);
|
||||
cfg->sensitivity = fval;
|
||||
|
||||
} else if(strcmp(key_str, "sensitivity-translation") == 0) {
|
||||
lptr->opt = CFG_SENS_TRANS;
|
||||
EXPECT(isfloat);
|
||||
cfg->sens_trans[0] = cfg->sens_trans[1] = cfg->sens_trans[2] = fval;
|
||||
|
||||
} else if(strcmp(key_str, "sensitivity-translation-x") == 0) {
|
||||
lptr->opt = CFG_SENS_TX;
|
||||
EXPECT(isfloat);
|
||||
cfg->sens_trans[0] = fval;
|
||||
|
||||
} else if(strcmp(key_str, "sensitivity-translation-y") == 0) {
|
||||
lptr->opt = CFG_SENS_TY;
|
||||
EXPECT(isfloat);
|
||||
cfg->sens_trans[1] = fval;
|
||||
|
||||
} else if(strcmp(key_str, "sensitivity-translation-z") == 0) {
|
||||
lptr->opt = CFG_SENS_TZ;
|
||||
EXPECT(isfloat);
|
||||
cfg->sens_trans[2] = fval;
|
||||
|
||||
} else if(strcmp(key_str, "sensitivity-rotation") == 0) {
|
||||
lptr->opt = CFG_SENS_ROT;
|
||||
EXPECT(isfloat);
|
||||
cfg->sens_rot[0] = cfg->sens_rot[1] = cfg->sens_rot[2] = fval;
|
||||
|
||||
} else if(strcmp(key_str, "sensitivity-rotation-x") == 0) {
|
||||
lptr->opt = CFG_SENS_RX;
|
||||
EXPECT(isfloat);
|
||||
cfg->sens_rot[0] = fval;
|
||||
|
||||
} else if(strcmp(key_str, "sensitivity-rotation-y") == 0) {
|
||||
lptr->opt = CFG_SENS_RY;
|
||||
EXPECT(isfloat);
|
||||
cfg->sens_rot[1] = fval;
|
||||
|
||||
} else if(strcmp(key_str, "sensitivity-rotation-z") == 0) {
|
||||
lptr->opt = CFG_SENS_RZ;
|
||||
EXPECT(isfloat);
|
||||
cfg->sens_rot[2] = fval;
|
||||
|
||||
} else if(strcmp(key_str, "invert-rot") == 0) {
|
||||
lptr->opt = CFG_INVROT;
|
||||
if(strchr(val_str, 'x')) {
|
||||
cfg->invert[RX] = !def_axinv[RX];
|
||||
cfg->invert[RX] = 1;
|
||||
}
|
||||
if(strchr(val_str, 'y')) {
|
||||
cfg->invert[RY] = !def_axinv[RY];
|
||||
cfg->invert[RY] = 1;
|
||||
}
|
||||
if(strchr(val_str, 'z')) {
|
||||
cfg->invert[RZ] = !def_axinv[RZ];
|
||||
cfg->invert[RZ] = 1;
|
||||
}
|
||||
|
||||
} else if(strcmp(key_str, "invert-trans") == 0) {
|
||||
lptr->opt = CFG_INVTRANS;
|
||||
if(strchr(val_str, 'x')) {
|
||||
cfg->invert[TX] = !def_axinv[TX];
|
||||
cfg->invert[TX] = 1;
|
||||
}
|
||||
if(strchr(val_str, 'y')) {
|
||||
cfg->invert[TY] = !def_axinv[TY];
|
||||
cfg->invert[TY] = 1;
|
||||
}
|
||||
if(strchr(val_str, 'z')) {
|
||||
cfg->invert[TZ] = !def_axinv[TZ];
|
||||
cfg->invert[TZ] = 1;
|
||||
}
|
||||
|
||||
} else if(strcmp(key_str, "swap-yz") == 0) {
|
||||
int i, swap_yz = 0;
|
||||
|
||||
lptr->opt = CFG_SWAPYZ;
|
||||
if(isint) {
|
||||
swap_yz = ival;
|
||||
cfg->swapyz = ival;
|
||||
} else {
|
||||
if(strcmp(val_str, "true") == 0 || strcmp(val_str, "on") == 0 || strcmp(val_str, "yes") == 0) {
|
||||
swap_yz = 1;
|
||||
cfg->swapyz = 1;
|
||||
} else if(strcmp(val_str, "false") == 0 || strcmp(val_str, "off") == 0 || strcmp(val_str, "no") == 0) {
|
||||
swap_yz = 0;
|
||||
cfg->swapyz = 0;
|
||||
} else {
|
||||
logmsg(LOG_WARNING, "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) {
|
||||
|
@ -245,6 +355,8 @@ int read_cfg(const char *fname, struct cfg *cfg)
|
|||
logmsg(LOG_WARNING, "invalid config value for %s, expected a number from 0 to 6\n", key_str);
|
||||
continue;
|
||||
}
|
||||
lptr->opt = CFG_AXISMAP_N;
|
||||
lptr->idx = axisidx;
|
||||
cfg->map_axis[axisidx] = ival;
|
||||
|
||||
} else if(sscanf(key_str, "bnmap%d", &bnidx) == 1) {
|
||||
|
@ -256,13 +368,30 @@ int read_cfg(const char *fname, struct cfg *cfg)
|
|||
if(cfg->map_button[bnidx] != bnidx) {
|
||||
logmsg(LOG_WARNING, "warning: multiple mappings for button %d\n", bnidx);
|
||||
}
|
||||
lptr->opt = CFG_BNMAP_N;
|
||||
lptr->idx = bnidx;
|
||||
cfg->map_button[bnidx] = ival;
|
||||
|
||||
} else if(sscanf(key_str, "bnact%d", &bnidx) == 1) {
|
||||
if(bnidx < 0 || bnidx >= MAX_BUTTONS) {
|
||||
logmsg(LOG_WARNING, "invalid configuration value for %s, expected a number from 0 to %d\n", key_str, MAX_BUTTONS);
|
||||
continue;
|
||||
}
|
||||
lptr->opt = CFG_BNACT_N;
|
||||
lptr->idx = bnidx;
|
||||
if((cfg->bnact[bnidx] = parse_bnact(val_str)) == -1) {
|
||||
cfg->bnact[bnidx] = BNACT_NONE;
|
||||
logmsg(LOG_WARNING, "invalid button action: \"%s\"\n", val_str);
|
||||
continue;
|
||||
}
|
||||
|
||||
} else if(sscanf(key_str, "kbmap%d", &bnidx) == 1) {
|
||||
if(bnidx < 0 || bnidx >= MAX_BUTTONS) {
|
||||
logmsg(LOG_WARNING, "invalid configuration value for %s, expected a number from 0 to %d\n", key_str, MAX_BUTTONS);
|
||||
continue;
|
||||
}
|
||||
lptr->opt = CFG_KBMAP_N;
|
||||
lptr->idx = bnidx;
|
||||
if(cfg->kbmap_str[bnidx]) {
|
||||
logmsg(LOG_WARNING, "warning: multiple keyboard mappings for button %d: %s -> %s\n", bnidx, cfg->kbmap_str[bnidx], val_str);
|
||||
free(cfg->kbmap_str[bnidx]);
|
||||
|
@ -270,6 +399,7 @@ int read_cfg(const char *fname, struct cfg *cfg)
|
|||
cfg->kbmap_str[bnidx] = strdup(val_str);
|
||||
|
||||
} else if(strcmp(key_str, "led") == 0) {
|
||||
lptr->opt = CFG_LED;
|
||||
if(isint) {
|
||||
cfg->led = ival;
|
||||
} else {
|
||||
|
@ -286,6 +416,7 @@ int read_cfg(const char *fname, struct cfg *cfg)
|
|||
}
|
||||
|
||||
} else if(strcmp(key_str, "grab") == 0) {
|
||||
lptr->opt = CFG_GRAB;
|
||||
if(isint) {
|
||||
cfg->grab_device = ival;
|
||||
} else {
|
||||
|
@ -300,10 +431,12 @@ int read_cfg(const char *fname, struct cfg *cfg)
|
|||
}
|
||||
|
||||
} else if(strcmp(key_str, "serial") == 0) {
|
||||
lptr->opt = CFG_SERIAL;
|
||||
strncpy(cfg->serial_dev, val_str, PATH_MAX - 1);
|
||||
|
||||
} else if(strcmp(key_str, "device-id") == 0) {
|
||||
unsigned int vendor, prod;
|
||||
lptr->opt = CFG_DEVID;
|
||||
if(sscanf(val_str, "%x:%x", &vendor, &prod) == 2) {
|
||||
cfg->devid[num_devid][0] = (int)vendor;
|
||||
cfg->devid[num_devid][1] = (int)prod;
|
||||
|
@ -318,152 +451,225 @@ int read_cfg(const char *fname, struct cfg *cfg)
|
|||
}
|
||||
}
|
||||
|
||||
/* unlock */
|
||||
flk.l_type = F_UNLCK;
|
||||
flk.l_start = flk.l_len = 0;
|
||||
flk.l_whence = SEEK_SET;
|
||||
fcntl(fileno(fp), F_SETLK, &flk);
|
||||
|
||||
unlock_cfgfile(fd);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_cfg(const char *fname, struct cfg *cfg)
|
||||
{
|
||||
int i, wrote_comment;
|
||||
int i, same;
|
||||
FILE *fp;
|
||||
struct flock flk;
|
||||
struct cfg def;
|
||||
char buf[128];
|
||||
|
||||
if(!(fp = fopen(fname, "w"))) {
|
||||
logmsg(LOG_ERR, "failed to write config file %s: %s\n", fname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!cfglines) {
|
||||
if(!(cfglines = calloc(NUM_EXTRA_LINES, sizeof *cfglines))) {
|
||||
logmsg(LOG_WARNING, "failed to allocate config lines buffer\n");
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
default_cfg(&def); /* default config for comparisons */
|
||||
|
||||
if(cfg->sensitivity != def.sensitivity) {
|
||||
add_cfgopt(CFG_SENS, 0, "sensitivity = %.3f", cfg->sensitivity);
|
||||
}
|
||||
|
||||
if(cfg->sens_trans[0] == cfg->sens_trans[1] && cfg->sens_trans[1] == cfg->sens_trans[2]) {
|
||||
rm_cfgopt("sensitivity-translation-x", RMCFG_ALL);
|
||||
rm_cfgopt("sensitivity-translation-y", RMCFG_ALL);
|
||||
rm_cfgopt("sensitivity-translation-z", RMCFG_ALL);
|
||||
if(cfg->sens_trans[0] != def.sens_trans[0]) {
|
||||
add_cfgopt(CFG_SENS_TRANS, 0, "sensitivity-translation = %.3f", cfg->sens_trans[0]);
|
||||
} else {
|
||||
rm_cfgopt("sensitivity-translation", RMCFG_OWN);
|
||||
}
|
||||
} else {
|
||||
if(cfg->sens_trans[0] != def.sens_trans[0]) {
|
||||
add_cfgopt(CFG_SENS_TX, 0, "sensitivity-translation-x = %.3f", cfg->sens_trans[0]);
|
||||
rm_cfgopt("sensitivity-translation", RMCFG_ALL);
|
||||
} else {
|
||||
rm_cfgopt("sensitivity-translation-x", RMCFG_OWN);
|
||||
}
|
||||
if(cfg->sens_trans[1] != def.sens_trans[1]) {
|
||||
add_cfgopt(CFG_SENS_TY, 0, "sensitivity-translation-y = %.3f", cfg->sens_trans[1]);
|
||||
rm_cfgopt("sensitivity-translation", RMCFG_ALL);
|
||||
} else {
|
||||
rm_cfgopt("sensitivity-translation-y", RMCFG_OWN);
|
||||
}
|
||||
if(cfg->sens_trans[2] != def.sens_trans[2]) {
|
||||
add_cfgopt(CFG_SENS_TZ, 0, "sensitivity-translation-z = %.3f", cfg->sens_trans[2]);
|
||||
rm_cfgopt("sensitivity-translation", RMCFG_ALL);
|
||||
} else {
|
||||
rm_cfgopt("sensitivity-translation-z", RMCFG_OWN);
|
||||
}
|
||||
}
|
||||
|
||||
if(cfg->sens_rot[0] == cfg->sens_rot[1] && cfg->sens_rot[1] == cfg->sens_rot[2]) {
|
||||
rm_cfgopt("sensitivity-rotation-x", RMCFG_ALL);
|
||||
rm_cfgopt("sensitivity-rotation-y", RMCFG_ALL);
|
||||
rm_cfgopt("sensitivity-rotation-z", RMCFG_ALL);
|
||||
if(cfg->sens_rot[0] != def.sens_rot[0]) {
|
||||
add_cfgopt(CFG_SENS_ROT, 0, "sensitivity-rotation = %.3f", cfg->sens_rot[0]);
|
||||
} else {
|
||||
rm_cfgopt("sensitivity-rotation", RMCFG_OWN);
|
||||
}
|
||||
} else {
|
||||
if(cfg->sens_rot[0] != def.sens_rot[0]) {
|
||||
add_cfgopt(CFG_SENS_RX, 0, "sensitivity-rotation-x = %.3f", cfg->sens_rot[0]);
|
||||
rm_cfgopt("sensitivity-rotation", RMCFG_ALL);
|
||||
} else {
|
||||
rm_cfgopt("sensitivity-rotation-x", RMCFG_OWN);
|
||||
}
|
||||
if(cfg->sens_rot[1] != def.sens_rot[1]) {
|
||||
add_cfgopt(CFG_SENS_RY, 0, "sensitivity-rotation-y = %.3f", cfg->sens_rot[1]);
|
||||
rm_cfgopt("sensitivity-rotation", RMCFG_ALL);
|
||||
} else {
|
||||
rm_cfgopt("sensitivity-rotation-y", RMCFG_OWN);
|
||||
}
|
||||
if(cfg->sens_rot[2] != def.sens_rot[2]) {
|
||||
add_cfgopt(CFG_SENS_RZ, 0, "sensitivity-rotation-z = %.3f", cfg->sens_rot[2]);
|
||||
rm_cfgopt("sensitivity-rotation", RMCFG_ALL);
|
||||
} else {
|
||||
rm_cfgopt("sensitivity-rotation-z", RMCFG_OWN);
|
||||
}
|
||||
}
|
||||
|
||||
same = 1;
|
||||
for(i=1; i<MAX_AXES; i++) {
|
||||
if(cfg->dead_threshold[i] != cfg->dead_threshold[i - 1]) {
|
||||
same = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(same) {
|
||||
if(cfg->dead_threshold[0] != def.dead_threshold[0]) {
|
||||
add_cfgopt(CFG_DEADZONE, 0, "dead-zone = %d", cfg->dead_threshold[0]);
|
||||
for(i=0; i<MAX_AXES; i++) {
|
||||
sprintf(buf, "dead-zone%d", i);
|
||||
rm_cfgopt(buf, RMCFG_ALL);
|
||||
}
|
||||
} else {
|
||||
rm_cfgopt("dead-zone", RMCFG_OWN);
|
||||
}
|
||||
} else {
|
||||
for(i=0; i<MAX_AXES; i++) {
|
||||
if(cfg->dead_threshold[i] != def.dead_threshold[i]) {
|
||||
add_cfgopt(CFG_DEADZONE_N, i, "dead-zone%d = %d", i, cfg->dead_threshold[i]);
|
||||
rm_cfgopt("dead-zone", RMCFG_ALL);
|
||||
} else {
|
||||
sprintf(buf, "dead-zone%d", i);
|
||||
rm_cfgopt(buf, RMCFG_OWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(cfg->repeat_msec != def.repeat_msec) {
|
||||
add_cfgopt(CFG_REPEAT, 0, "repeat-interval = %d\n", cfg->repeat_msec);
|
||||
} else {
|
||||
rm_cfgopt("repeat-interval", RMCFG_ALL);
|
||||
}
|
||||
|
||||
if(cfg->invert[0] || cfg->invert[1] || cfg->invert[2]) {
|
||||
char flags[4] = {0}, *p = flags;
|
||||
if(cfg->invert[0]) *p++ = 'x';
|
||||
if(cfg->invert[1]) *p++ = 'y';
|
||||
if(cfg->invert[2]) *p = 'z';
|
||||
add_cfgopt(CFG_INVTRANS, 0, "invert-trans = %s", flags);
|
||||
} else {
|
||||
rm_cfgopt("invert-trans", RMCFG_ALL);
|
||||
}
|
||||
|
||||
if(cfg->invert[3] || cfg->invert[4] || cfg->invert[5]) {
|
||||
char flags[4] = {0}, *p = flags;
|
||||
if(cfg->invert[3]) *p++ = 'x';
|
||||
if(cfg->invert[4]) *p++ = 'y';
|
||||
if(cfg->invert[5]) *p = 'z';
|
||||
add_cfgopt(CFG_INVROT, 0, "invert-rot = %s", flags);
|
||||
} else {
|
||||
rm_cfgopt("invert-rot", RMCFG_ALL);
|
||||
}
|
||||
|
||||
if(cfg->swapyz) {
|
||||
add_cfgopt(CFG_SWAPYZ, 0, "swap-yz = true");
|
||||
} else {
|
||||
rm_cfgopt("swap-yz", RMCFG_ALL);
|
||||
}
|
||||
|
||||
for(i=0; i<MAX_BUTTONS; i++) {
|
||||
if(cfg->map_button[i] != i) {
|
||||
add_cfgopt(CFG_BNMAP_N, i, "bnmap%d = %d", i, cfg->map_button[i]);
|
||||
} else {
|
||||
sprintf(buf, "bnmap%d", i);
|
||||
rm_cfgopt(buf, RMCFG_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0; i<MAX_BUTTONS; i++) {
|
||||
if(cfg->bnact[i] != BNACT_NONE) {
|
||||
add_cfgopt(CFG_BNACT_N, i, "bnact%d = %s", i, bnact_name(cfg->bnact[i]));
|
||||
} else {
|
||||
sprintf(buf, "bnact%d", i);
|
||||
rm_cfgopt(buf, RMCFG_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0; i<MAX_BUTTONS; i++) {
|
||||
if(cfg->kbmap_str[i]) {
|
||||
add_cfgopt(CFG_KBMAP_N, i, "kbmap%d = %s", i, cfg->kbmap_str[i]);
|
||||
} else {
|
||||
sprintf(buf, "kbmap%d", i);
|
||||
rm_cfgopt(buf, RMCFG_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
if(cfg->led != def.led) {
|
||||
add_cfgopt(CFG_LED, 0, "led = %s", (cfg->led ? (cfg->led == LED_AUTO ? "auto" : "on") : "off"));
|
||||
} else {
|
||||
rm_cfgopt("led", RMCFG_OWN);
|
||||
}
|
||||
|
||||
if(cfg->grab_device != def.grab_device) {
|
||||
add_cfgopt(CFG_GRAB, 0, "grab = %s", cfg->grab_device ? "true" : "false");
|
||||
} else {
|
||||
rm_cfgopt("grab", RMCFG_OWN);
|
||||
}
|
||||
|
||||
if(cfg->serial_dev[0]) {
|
||||
add_cfgopt(CFG_SERIAL, 0, "serial = %s", cfg->serial_dev);
|
||||
} else {
|
||||
rm_cfgopt("serial", RMCFG_ALL);
|
||||
}
|
||||
|
||||
for(i=0; i<MAX_CUSTOM; i++) {
|
||||
if(cfg->devid[i][0] != -1 && cfg->devid[i][1] != -1) {
|
||||
add_cfgopt_devid(cfg->devid[i][0], cfg->devid[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* acquire 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);
|
||||
for(i=0; i<num_lines; i++) {
|
||||
if(!cfglines[i].str) continue;
|
||||
|
||||
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; i<MAX_BUTTONS; i++) {
|
||||
if(cfg->map_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(*cfglines[i].str) {
|
||||
fputs(cfglines[i].str, fp);
|
||||
}
|
||||
}
|
||||
if(wrote_comment) {
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
wrote_comment = 0;
|
||||
for(i=0; i<MAX_BUTTONS; i++) {
|
||||
if(cfg->kbmap_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);
|
||||
}
|
||||
|
||||
fprintf(fp, "# led status: on, off, or auto (turn on when a client is connected)\n");
|
||||
fprintf(fp, "led = %s\n\n", (cfg->led ? (cfg->led == LED_AUTO ? "auto" : "on") : "off"));
|
||||
|
||||
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\n");
|
||||
}
|
||||
|
||||
fprintf(fp, "List of additional USB devices to use (multiple devices can be listed)");
|
||||
for(i=0; i<MAX_CUSTOM; i++) {
|
||||
if(cfg->devid[i][0] != -1 && cfg->devid[i][1] != -1) {
|
||||
fprintf(fp, "device-id = %x:%x\n", cfg->devid[i][0], cfg->devid[i][1]);
|
||||
}
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
|
||||
/* unlock */
|
||||
flk.l_type = F_UNLCK;
|
||||
flk.l_start = flk.l_len = 0;
|
||||
|
@ -473,3 +679,137 @@ int write_cfg(const char *fname, struct cfg *cfg)
|
|||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
int act;
|
||||
} bnact_strtab[] = {
|
||||
{"none", BNACT_NONE},
|
||||
{"sensitivity-up", BNACT_SENS_INC},
|
||||
{"sensitivity-down", BNACT_SENS_DEC},
|
||||
{"sensitivity-reset", BNACT_SENS_RESET},
|
||||
{"disable-rotation", BNACT_DISABLE_ROTATION},
|
||||
{"disable-translation", BNACT_DISABLE_TRANSLATION},
|
||||
{"dominant-axis", BNACT_DOMINANT_AXIS},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static int parse_bnact(const char *s)
|
||||
{
|
||||
int i;
|
||||
for(i=0; bnact_strtab[i].name; i++) {
|
||||
if(strcmp(bnact_strtab[i].name, s) == 0) {
|
||||
return bnact_strtab[i].act;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *bnact_name(int bnact)
|
||||
{
|
||||
int i;
|
||||
for(i=0; bnact_strtab[i].name; i++) {
|
||||
if(bnact_strtab[i].act == bnact) {
|
||||
return bnact_strtab[i].name;
|
||||
}
|
||||
}
|
||||
return "none";
|
||||
}
|
||||
|
||||
static struct cfgline *find_cfgopt(int opt, int idx)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<num_lines; i++) {
|
||||
if(cfglines[i].str && cfglines[i].opt == opt && cfglines[i].idx == idx) {
|
||||
return cfglines + i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_cfgopt(int opt, int idx, const char *fmt, ...)
|
||||
{
|
||||
struct cfgline *lptr;
|
||||
char buf[512], *str;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if(!(str = strdup(buf))) return -1;
|
||||
|
||||
if(!(lptr = find_cfgopt(opt, idx))) {
|
||||
lptr = cfglines + num_lines++;
|
||||
lptr->own = 1;
|
||||
}
|
||||
free(lptr->str);
|
||||
lptr->str = str;
|
||||
lptr->opt = opt;
|
||||
lptr->idx = idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_cfgopt_devid(int vid, int pid)
|
||||
{
|
||||
int i;
|
||||
unsigned int dev[2];
|
||||
struct cfgline *lptr = 0;
|
||||
char *str, *val;
|
||||
|
||||
if(!(str = malloc(64))) return -1;
|
||||
sprintf(str, "device-id = %04x:%04x", vid, pid);
|
||||
|
||||
for(i=0; i<num_lines; i++) {
|
||||
if(!cfglines[i].str || cfglines[i].opt != CFG_DEVID) {
|
||||
continue;
|
||||
}
|
||||
if(!(val = strchr(cfglines[i].str, '='))) {
|
||||
continue;
|
||||
}
|
||||
if(sscanf(val + 1, "%x:%x", dev, dev + 1) == 2 && dev[0] == vid && dev[1] == pid) {
|
||||
lptr = cfglines + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!lptr) {
|
||||
num_lines++; /* leave an empty line */
|
||||
lptr = cfglines + num_lines++;
|
||||
}
|
||||
|
||||
free(lptr->str);
|
||||
lptr->str = str;
|
||||
lptr->opt = CFG_DEVID;
|
||||
lptr->idx = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm_cfgopt(const char *name, int mode)
|
||||
{
|
||||
int i;
|
||||
char *ptr, *endp;
|
||||
char buf[256];
|
||||
|
||||
for(i=0; i<num_lines; i++) {
|
||||
if(!cfglines[i].str || !*cfglines[i].str) continue;
|
||||
|
||||
strncpy(buf, cfglines[i].str, sizeof buf - 1);
|
||||
buf[sizeof buf - 1] = 0;
|
||||
|
||||
ptr = buf;
|
||||
while(*ptr && isspace(*ptr)) ptr++;
|
||||
if(!(endp = strchr(ptr, '='))) {
|
||||
continue;
|
||||
}
|
||||
while(endp > ptr && isspace(*--endp)) *endp = 0;
|
||||
if(strcmp(ptr, name) == 0) {
|
||||
if(mode != RMCFG_OWN || cfglines[i].own) {
|
||||
free(cfglines[i].str);
|
||||
cfglines[i].str = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -31,14 +31,29 @@ enum {
|
|||
LED_AUTO = 2
|
||||
};
|
||||
|
||||
/* button actions (XXX: must correspond to SPNAV_BNACT_* in libspnav) */
|
||||
enum {
|
||||
BNACT_NONE,
|
||||
BNACT_SENS_RESET,
|
||||
BNACT_SENS_INC,
|
||||
BNACT_SENS_DEC,
|
||||
BNACT_DISABLE_ROTATION,
|
||||
BNACT_DISABLE_TRANSLATION,
|
||||
BNACT_DOMINANT_AXIS,
|
||||
|
||||
MAX_BNACT
|
||||
};
|
||||
|
||||
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 bnact[MAX_BUTTONS];
|
||||
int kbmap[MAX_BUTTONS];
|
||||
char *kbmap_str[MAX_BUTTONS];
|
||||
int swapyz;
|
||||
int led, grab_device;
|
||||
char serial_dev[PATH_MAX];
|
||||
int repeat_msec;
|
||||
|
|
46
src/client.c
46
src/client.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -28,21 +28,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <X11/Xutil.h>
|
||||
#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) */
|
||||
|
||||
|
@ -63,7 +48,7 @@ struct client *add_client(int type, void *cdata)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if(!(client = malloc(sizeof *client))) {
|
||||
if(!(client = calloc(1, sizeof *client))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -75,9 +60,13 @@ struct client *add_client(int type, void *cdata)
|
|||
client->win = *(Window*)cdata;
|
||||
#endif
|
||||
}
|
||||
/* default to protocol version 0 until the client changes it */
|
||||
client->proto = 0;
|
||||
/* evmask for proto-v0 clients is just input events */
|
||||
client->evmask = EVMASK_MOTION | EVMASK_BUTTON;
|
||||
|
||||
client->sens = 1.0f;
|
||||
client->dev_idx = 0; /* default/first device */
|
||||
client->dev = 0; /* default/first device */
|
||||
|
||||
if(!client_list && cfg.led == LED_AUTO) {
|
||||
/* on first client, turn the led on */
|
||||
|
@ -96,7 +85,7 @@ void remove_client(struct client *client)
|
|||
|
||||
if(iter == client) {
|
||||
client_list = iter->next;
|
||||
free(iter);
|
||||
free_client(iter);
|
||||
iter = client_list;
|
||||
if(!iter) {
|
||||
if(cfg.led == LED_AUTO) {
|
||||
|
@ -110,13 +99,22 @@ void remove_client(struct client *client)
|
|||
if(iter->next == client) {
|
||||
struct client *tmp = iter->next;
|
||||
iter->next = tmp->next;
|
||||
free(tmp);
|
||||
free_client(tmp);
|
||||
} else {
|
||||
iter = iter->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void free_client(struct client *client)
|
||||
{
|
||||
if(client) {
|
||||
free(client->name);
|
||||
free(client->strbuf.buf);
|
||||
free(client);
|
||||
}
|
||||
}
|
||||
|
||||
int get_client_type(struct client *client)
|
||||
{
|
||||
return client->type;
|
||||
|
@ -144,14 +142,14 @@ float get_client_sensitivity(struct client *client)
|
|||
return client->sens;
|
||||
}
|
||||
|
||||
void set_client_device_index(struct client *client, int dev_idx)
|
||||
void set_client_device(struct client *client, struct device *dev)
|
||||
{
|
||||
client->dev_idx = dev_idx;
|
||||
client->dev = dev;
|
||||
}
|
||||
|
||||
int get_client_device_index(struct client *client)
|
||||
struct device *get_client_device(struct client *client)
|
||||
{
|
||||
return client->dev_idx;
|
||||
return client->dev ? client->dev : get_devices();
|
||||
}
|
||||
|
||||
struct client *first_client(void)
|
||||
|
|
44
src/client.h
44
src/client.h
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -25,17 +25,53 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
#include "proto.h"
|
||||
|
||||
/* client types */
|
||||
enum {
|
||||
CLIENT_X11, /* through the magellan X11 protocol */
|
||||
CLIENT_UNIX /* through the new UNIX domain socket */
|
||||
};
|
||||
|
||||
/* event selection (must match SPNAV_EVMASK* in libspnav/spnav.h) */
|
||||
enum {
|
||||
EVMASK_MOTION = 0x01,
|
||||
EVMASK_BUTTON = 0x02,
|
||||
EVMASK_DEV = 0x04,
|
||||
EVMASK_CFG = 0x08,
|
||||
EVMASK_RAWAXIS = 0x10,
|
||||
EVMASK_RAWBUTTON = 0x20
|
||||
};
|
||||
|
||||
struct client;
|
||||
struct device;
|
||||
|
||||
struct client {
|
||||
int type;
|
||||
|
||||
int sock; /* UNIX domain socket */
|
||||
int proto; /* protocol version */
|
||||
#ifdef USE_X11
|
||||
Window win; /* X11 client window */
|
||||
#endif
|
||||
|
||||
float sens; /* sensitivity */
|
||||
struct device *dev;
|
||||
|
||||
char *name; /* client name (not unique) */
|
||||
unsigned int evmask; /* event selection mask */
|
||||
|
||||
char reqbuf[64];
|
||||
int reqbytes;
|
||||
|
||||
/* protocol buffer for handling reception of strings in multiple packets */
|
||||
struct reqresp_strbuf strbuf;
|
||||
|
||||
struct client *next;
|
||||
};
|
||||
|
||||
struct client *add_client(int type, void *cdata);
|
||||
void remove_client(struct client *client);
|
||||
void free_client(struct client *client);
|
||||
|
||||
int get_client_type(struct client *client);
|
||||
int get_client_socket(struct client *client);
|
||||
|
@ -46,8 +82,8 @@ Window get_client_window(struct client *client);
|
|||
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);
|
||||
void set_client_device(struct client *client, struct device *dev);
|
||||
struct device *get_client_device(struct client *client);
|
||||
|
||||
/* these two can be used to iterate over all clients */
|
||||
struct client *first_client(void);
|
||||
|
|
358
src/dev.c
358
src/dev.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -19,40 +19,153 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "dev.h"
|
||||
#include "dev_usb.h"
|
||||
#include "dev_serial.h"
|
||||
#include "event.h" /* remove pending events upon device removal */
|
||||
#include "spnavd.h"
|
||||
#include "proto.h"
|
||||
#include "proto_unix.h"
|
||||
|
||||
#ifdef USE_X11
|
||||
#include "proto_x11.h"
|
||||
#endif
|
||||
|
||||
|
||||
/* The device flags are introduced to normalize input across all known
|
||||
* supported 6dof devices. Newer USB devices seem to use axis 1 as fwd/back and
|
||||
* axis 2 as up/down, while older serial devices (and possibly also the early
|
||||
* USB ones?) do the opposite. This discrepancy would mean the user has to
|
||||
* change the configuration back and forth when changing devices. With these
|
||||
* flags we attempt to make all known devices use the same input axes at the
|
||||
* lowest level, and let the user remap based on preference, and have their
|
||||
* choice persist across all known devices.
|
||||
*/
|
||||
enum {
|
||||
DF_SWAPYZ = 1,
|
||||
DF_INVYZ = 2
|
||||
};
|
||||
|
||||
/* The bnmap function pointer in the device table was introduced to deal with
|
||||
* certain USB devices which report a huge amount of buttons, and strange
|
||||
* disjointed button numbers. The function is expected to return the number of
|
||||
* actual buttons when a negative number is passed as argument, and the button
|
||||
* mapping otherwise.
|
||||
*/
|
||||
static struct usbdb_entry {
|
||||
int usbid[2];
|
||||
int type;
|
||||
unsigned int flags;
|
||||
int (*bnmap)(int); /* remap buttons on problematic devices */
|
||||
} usbdb[] = {
|
||||
{{0x046d, 0xc603}, DEV_PLUSXT, 0, 0}, /* spacemouse plus XT */
|
||||
{{0x046d, 0xc605}, DEV_CADMAN, DF_SWAPYZ | DF_INVYZ, 0}, /* cadman */
|
||||
{{0x046d, 0xc606}, DEV_SMCLASSIC, 0, 0}, /* spacemouse classic */
|
||||
{{0x046d, 0xc621}, DEV_SB5000, DF_SWAPYZ | DF_INVYZ, 0}, /* spaceball 5000 */
|
||||
{{0x046d, 0xc623}, DEV_STRAVEL, DF_SWAPYZ | DF_INVYZ, 0}, /* space traveller */
|
||||
{{0x046d, 0xc625}, DEV_SPILOT, DF_SWAPYZ | DF_INVYZ, 0}, /* space pilot */
|
||||
{{0x046d, 0xc626}, DEV_SNAV, DF_SWAPYZ | DF_INVYZ, 0}, /* space navigator */
|
||||
{{0x046d, 0xc627}, DEV_SEXP, DF_SWAPYZ | DF_INVYZ, 0}, /* space explorer */
|
||||
{{0x046d, 0xc628}, DEV_SNAVNB, DF_SWAPYZ | DF_INVYZ, 0}, /* space navigator for notebooks*/
|
||||
{{0x046d, 0xc629}, DEV_SPILOTPRO, DF_SWAPYZ | DF_INVYZ, 0}, /* space pilot pro*/
|
||||
{{0x046d, 0xc62b}, DEV_SMPRO, DF_SWAPYZ | DF_INVYZ, bnhack_smpro}, /* space mouse pro*/
|
||||
{{0x046d, 0xc640}, DEV_NULOOQ, 0, 0}, /* nulooq */
|
||||
{{0x256f, 0xc62e}, DEV_SMW, DF_SWAPYZ | DF_INVYZ, 0}, /* spacemouse wireless (USB cable) */
|
||||
{{0x256f, 0xc62f}, DEV_SMW, DF_SWAPYZ | DF_INVYZ, 0}, /* spacemouse wireless receiver */
|
||||
{{0x256f, 0xc631}, DEV_SMPROW, DF_SWAPYZ | DF_INVYZ, bnhack_smpro}, /* spacemouse pro wireless */
|
||||
{{0x256f, 0xc632}, DEV_SMPROW, DF_SWAPYZ | DF_INVYZ, bnhack_smpro}, /* spacemouse pro wireless receiver */
|
||||
{{0x256f, 0xc633}, DEV_SMENT, DF_SWAPYZ | DF_INVYZ, bnhack_sment}, /* spacemouse enterprise */
|
||||
{{0x256f, 0xc635}, DEV_SMCOMP, DF_SWAPYZ | DF_INVYZ, 0}, /* spacemouse compact */
|
||||
{{0x256f, 0xc636}, DEV_SMMOD, DF_SWAPYZ | DF_INVYZ, 0}, /* spacemouse module */
|
||||
{{0x256f, 0xc63a}, DEV_SMW, DF_SWAPYZ | DF_INVYZ, 0}, /* spacemouse wireless (Bluetooth) */
|
||||
{{-1, -1}, DEV_UNKNOWN, 0}
|
||||
};
|
||||
|
||||
/* 3Dconnexion devices which we don't want to match, because they are
|
||||
* not 6dof space-mice. reported by: Herbert Graeber in github pull request #4
|
||||
*/
|
||||
static int devid_blacklist[][2] = {
|
||||
{0x256f, 0xc650}, /* cadmouse */
|
||||
{0x256f, 0xc651}, /* cadmouse wireless */
|
||||
{0x256f, 0xc654}, /* CadMouse Pro Wireless */
|
||||
{0x256f, 0xc655}, /* CadMouse Compact */
|
||||
{0x256f, 0xc656}, /* CadMouse Pro */
|
||||
{0x256f, 0xc657}, /* CadMouse Pro Wireless Left */
|
||||
{0x256f, 0xc658}, /* CadMouse Compact Wireless */
|
||||
{0x256f, 0xc664}, /* Keyboard Pro */
|
||||
{0x256f, 0xc668}, /* Keyboard Pro (newer version)*/
|
||||
{0x256f, 0xc665}, /* Numpad Pro */
|
||||
{0x256f, 0xc62c}, /* lipari(?) */
|
||||
{0x256f, 0xc641}, /* scout(?) */
|
||||
|
||||
{-1, -1}
|
||||
};
|
||||
|
||||
|
||||
|
||||
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 int match_usbdev(const struct usb_dev_info *devinfo);
|
||||
static struct usbdb_entry *find_usbdb_entry(unsigned int vid, unsigned int pid);
|
||||
|
||||
static struct device *dev_list = NULL;
|
||||
static unsigned short last_id;
|
||||
|
||||
int init_devices(void)
|
||||
void init_devices(void)
|
||||
{
|
||||
init_devices_serial();
|
||||
init_devices_usb();
|
||||
}
|
||||
|
||||
void init_devices_serial(void)
|
||||
{
|
||||
struct stat st;
|
||||
struct device *dev;
|
||||
int i, device_added = 0;
|
||||
struct usb_device_info *usblist, *usbdev;
|
||||
spnav_event ev = {0};
|
||||
|
||||
/* 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)) {
|
||||
if(stat(cfg.serial_dev, &st) == -1) {
|
||||
logmsg(LOG_ERR, "Failed to stat serial device %s: %s\n",
|
||||
cfg.serial_dev, strerror(errno));
|
||||
return;
|
||||
}
|
||||
if(!S_ISCHR(st.st_mode)) {
|
||||
logmsg(LOG_ERR, "Ignoring configured serial device: %s: %s\n",
|
||||
cfg.serial_dev, "not a character device");
|
||||
return;
|
||||
}
|
||||
|
||||
dev = add_device();
|
||||
strcpy(dev->path, cfg.serial_dev);
|
||||
if(open_dev_serial(dev) == -1) {
|
||||
remove_device(dev);
|
||||
} else {
|
||||
strcpy(dev->name, "serial device");
|
||||
logmsg(LOG_INFO, "using device: %s\n", cfg.serial_dev);
|
||||
device_added++;
|
||||
return;
|
||||
}
|
||||
logmsg(LOG_INFO, "using device: %s\n", cfg.serial_dev);
|
||||
|
||||
/* new serial device added, send device change event */
|
||||
ev.dev.type = EVENT_DEV;
|
||||
ev.dev.op = DEV_ADD;
|
||||
ev.dev.id = dev->id;
|
||||
ev.dev.devtype = dev->type;
|
||||
broadcast_event(&ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int init_devices_usb(void)
|
||||
{
|
||||
int i;
|
||||
struct device *dev;
|
||||
struct usb_dev_info *usblist, *usbdev;
|
||||
struct usbdb_entry *uent;
|
||||
spnav_event ev = {0};
|
||||
char buf[256];
|
||||
|
||||
/* detect any supported USB devices */
|
||||
usblist = find_usb_devices(match_usbdev);
|
||||
|
@ -61,20 +174,50 @@ int init_devices(void)
|
|||
while(usbdev) {
|
||||
for(i=0; i<usbdev->num_devfiles; i++) {
|
||||
if((dev = dev_path_in_use(usbdev->devfiles[i]))) {
|
||||
if(verbose) {
|
||||
logmsg(LOG_WARNING, "already using device: %s (%s)\n", dev->name, dev->path);
|
||||
if(verbose > 1) {
|
||||
logmsg(LOG_WARNING, "already using device: %s (%s) (id: %d)\n", dev->name, dev->path, dev->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
uent = find_usbdb_entry(usbdev->vendorid, usbdev->productid);
|
||||
|
||||
dev = add_device();
|
||||
strcpy(dev->path, usbdev->devfiles[i]);
|
||||
dev->type = uent ? uent->type : DEV_UNKNOWN;
|
||||
dev->flags = uent ? uent->flags : 0;
|
||||
dev->bnhack = uent ? uent->bnmap : 0;
|
||||
dev->usbid[0] = usbdev->vendorid;
|
||||
dev->usbid[1] = usbdev->productid;
|
||||
|
||||
if(open_dev_usb(dev) == -1) {
|
||||
remove_device(dev);
|
||||
} else {
|
||||
/* add the 6dof remapping flags to every future 3dconnexion device */
|
||||
if(dev->usbid[0] == VID_3DCONN) {
|
||||
dev->flags |= DF_SWAPYZ | DF_INVYZ;
|
||||
}
|
||||
/* sanity-check the device flags */
|
||||
if((dev->flags & (DF_SWAPYZ | DF_INVYZ)) && dev->num_axes != 6) {
|
||||
logmsg(LOG_WARNING, "BUG: Tried to add 6dof device flags to a device with %d axes. Please report this as a bug\n", dev->num_axes);
|
||||
dev->flags &= ~(DF_SWAPYZ | DF_INVYZ);
|
||||
}
|
||||
logmsg(LOG_INFO, "using device: %s (%s)\n", dev->name, dev->path);
|
||||
device_added++;
|
||||
if(dev->flags) {
|
||||
strcpy(buf, " device flags:");
|
||||
if(dev->flags & DF_SWAPYZ) strcat(buf, " swap y-z");
|
||||
if(dev->flags & DF_INVYZ) strcat(buf, " invert y-z");
|
||||
logmsg(LOG_INFO, "%s\n", buf);
|
||||
}
|
||||
|
||||
/* new USB device added, send device change event */
|
||||
ev.dev.type = EVENT_DEV;
|
||||
ev.dev.op = DEV_ADD;
|
||||
ev.dev.id = dev->id;
|
||||
ev.dev.devtype = dev->type;
|
||||
ev.dev.usbid[0] = dev->usbid[0];
|
||||
ev.dev.usbid[1] = dev->usbid[1];
|
||||
broadcast_event(&ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -84,11 +227,15 @@ int init_devices(void)
|
|||
free_usb_devices_list(usblist);
|
||||
|
||||
if(!usblist) {
|
||||
logmsg(LOG_ERR, "failed to find any supported devices\n");
|
||||
if(verbose > 1) {
|
||||
logmsg(LOG_ERR, "failed to find any supported USB devices\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef USE_X11
|
||||
drop_xinput();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -101,12 +248,13 @@ static struct device *add_device(void)
|
|||
}
|
||||
memset(dev, 0, sizeof *dev);
|
||||
|
||||
logmsg(LOG_INFO, "adding device.\n");
|
||||
|
||||
dev->fd = -1;
|
||||
dev->id = last_id++;
|
||||
dev->next = dev_list;
|
||||
dev_list = dev;
|
||||
|
||||
logmsg(LOG_INFO, "adding device (id: %d).\n", dev->id);
|
||||
|
||||
return dev_list;
|
||||
}
|
||||
|
||||
|
@ -114,8 +262,9 @@ void remove_device(struct device *dev)
|
|||
{
|
||||
struct device dummy;
|
||||
struct device *iter;
|
||||
spnav_event ev;
|
||||
|
||||
logmsg(LOG_INFO, "removing device: %s\n", dev->name);
|
||||
logmsg(LOG_INFO, "removing device: %s (id: %d path: %s)\n", dev->name, dev->id, dev->path);
|
||||
|
||||
dummy.next = dev_list;
|
||||
iter = &dummy;
|
||||
|
@ -134,10 +283,20 @@ void remove_device(struct device *dev)
|
|||
if(dev->close) {
|
||||
dev->close(dev);
|
||||
}
|
||||
|
||||
/* send device change event to clients */
|
||||
ev.dev.type = EVENT_DEV;
|
||||
ev.dev.op = DEV_RM;
|
||||
ev.dev.id = dev->id;
|
||||
ev.dev.devtype = dev->type;
|
||||
ev.dev.usbid[0] = dev->usbid[0];
|
||||
ev.dev.usbid[1] = dev->usbid[1];
|
||||
broadcast_event(&ev);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
static struct device *dev_path_in_use(char const *dev_path)
|
||||
struct device *dev_path_in_use(const char *dev_path)
|
||||
{
|
||||
struct device *iter = dev_list;
|
||||
while(iter) {
|
||||
|
@ -173,7 +332,21 @@ int read_device(struct device *dev, struct dev_input *inp)
|
|||
if(dev->read == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return dev->read(dev, inp);
|
||||
|
||||
if(dev->read(dev, inp) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(inp->type == INP_MOTION) {
|
||||
if(dev->flags & DF_SWAPYZ) {
|
||||
static const int swap[] = {0, 2, 1, 3, 5, 4};
|
||||
inp->idx = swap[inp->idx];
|
||||
}
|
||||
if((dev->flags & DF_INVYZ) && inp->idx != 0 && inp->idx != 3) {
|
||||
inp->val = -inp->val;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_device_led(struct device *dev, int state)
|
||||
|
@ -197,47 +370,7 @@ struct device *get_devices(void)
|
|||
return dev_list;
|
||||
}
|
||||
|
||||
#define VENDOR_3DCONNEXION 0x256f
|
||||
|
||||
static int devid_list[][2] = {
|
||||
{0x046d, 0xc603}, /* spacemouse plus XT */
|
||||
{0x046d, 0xc605}, /* cadman */
|
||||
{0x046d, 0xc606}, /* spacemouse classic */
|
||||
{0x046d, 0xc621}, /* spaceball 5000 */
|
||||
{0x046d, 0xc623}, /* space traveller */
|
||||
{0x046d, 0xc625}, /* space pilot */
|
||||
{0x046d, 0xc626}, /* space navigator */
|
||||
{0x046d, 0xc627}, /* space explorer */
|
||||
{0x046d, 0xc628}, /* space navigator for notebooks*/
|
||||
{0x046d, 0xc629}, /* space pilot pro*/
|
||||
{0x046d, 0xc62b}, /* space mouse pro*/
|
||||
{0x046d, 0xc640}, /* nulooq */
|
||||
{0x256f, 0xc62e}, /* spacemouse wireless (USB cable) */
|
||||
{0x256f, 0xc62f}, /* spacemouse wireless receiver */
|
||||
{0x256f, 0xc631}, /* spacemouse pro wireless */
|
||||
{0x256f, 0xc632}, /* spacemouse pro wireless receiver */
|
||||
{0x256f, 0xc633}, /* spacemouse enterprise */
|
||||
{0x256f, 0xc635}, /* spacemouse compact */
|
||||
{0x256f, 0xc636}, /* spacemouse module */
|
||||
|
||||
{-1, -1}
|
||||
};
|
||||
|
||||
/* 3Dconnexion devices which we don't want to match, because they are
|
||||
* not 6dof space-mice. reported by: Herbert Graeber in github pull request #4
|
||||
*/
|
||||
static int devid_blacklist[][2] = {
|
||||
{0x256f, 0xc652},
|
||||
{0x256f, 0xc650}, /* cadmouse */
|
||||
{0x256f, 0xc651}, /* cadmouse wireless */
|
||||
{0x256f, 0xc62c}, /* lipari(?) */
|
||||
{0x256f, 0xc641}, /* scout(?) */
|
||||
|
||||
{-1, -1}
|
||||
};
|
||||
|
||||
|
||||
static int match_usbdev(const struct usb_device_info *devinfo)
|
||||
static int match_usbdev(const struct usb_dev_info *devinfo)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -265,13 +398,19 @@ static int match_usbdev(const struct usb_device_info *devinfo)
|
|||
}
|
||||
|
||||
/* match any device with the new 3Dconnexion device id */
|
||||
if(vid == VENDOR_3DCONNEXION) {
|
||||
if(vid == VID_3DCONN) {
|
||||
/* avoid matching and trying to grab the CAD mouse, when connected
|
||||
* on the same universal receiver as the spacemouse.
|
||||
*/
|
||||
if(pid == 0xc652 && strstr(devinfo->name, "Universal Receiver Mouse")) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* match any device in the devid_list */
|
||||
for(i=0; devid_list[i][0] > 0; i++) {
|
||||
if(vid == devid_list[i][0] && pid == devid_list[i][1]) {
|
||||
/* match any device in the usbdb */
|
||||
for(i=0; usbdb[i].usbid[0] > 0; i++) {
|
||||
if(vid == usbdb[i].usbid[0] && pid == usbdb[i].usbid[1]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -284,3 +423,94 @@ static int match_usbdev(const struct usb_device_info *devinfo)
|
|||
|
||||
return 0; /* no match */
|
||||
}
|
||||
|
||||
static struct usbdb_entry *find_usbdb_entry(unsigned int vid, unsigned int pid)
|
||||
{
|
||||
int i;
|
||||
for(i=0; usbdb[i].usbid[0] != -1; i++) {
|
||||
if(usbdb[i].usbid[0] == vid && usbdb[i].usbid[1] == pid) {
|
||||
return usbdb + i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* --- button remapping hack functions --- */
|
||||
|
||||
/* SpaceMouse Pro */
|
||||
int bnhack_smpro(int bn)
|
||||
{
|
||||
if(bn < 0) return 15; /* button count */
|
||||
|
||||
switch(bn) {
|
||||
case 256: return 4; /* menu */
|
||||
case 257: return 5; /* fit */
|
||||
case 258: return 6; /* [T] */
|
||||
case 260: return 7; /* [R] */
|
||||
case 261: return 8; /* [F] */
|
||||
case 264: return 9; /* [ ] */
|
||||
case 268: return 0; /* 1 */
|
||||
case 269: return 1; /* 2 */
|
||||
case 270: return 2; /* 3 */
|
||||
case 271: return 3; /* 4 */
|
||||
case 278: return 11; /* esc */
|
||||
case 279: return 12; /* alt */
|
||||
case 280: return 13; /* shift */
|
||||
case 281: return 14; /* ctrl */
|
||||
case 282: return 10; /* lock */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1; /* ignore all other events */
|
||||
}
|
||||
|
||||
/* SpaceMouse Enterprise */
|
||||
int bnhack_sment(int bn)
|
||||
{
|
||||
if(bn < 0) return 31; /* button count */
|
||||
|
||||
switch(bn) {
|
||||
case 256: return 12; /* menu */
|
||||
case 257: return 13; /* fit */
|
||||
case 258: return 14; /* [T] */
|
||||
case 260: return 15; /* [R] */
|
||||
case 261: return 16; /* [F] */
|
||||
case 264: return 17; /* [ ] */
|
||||
|
||||
case 266: return 30; /* iso */
|
||||
|
||||
case 268: return 0; /* 1 */
|
||||
case 269: return 1; /* 2 */
|
||||
case 270: return 2; /* 3 */
|
||||
case 271: return 3; /* 4 */
|
||||
case 272: return 4; /* 5 */
|
||||
case 273: return 5; /* 6 */
|
||||
case 274: return 6; /* 7 */
|
||||
case 275: return 7; /* 8 */
|
||||
case 276: return 8; /* 9 */
|
||||
case 277: return 9; /* 10 */
|
||||
|
||||
case 278: return 18; /* esc */
|
||||
case 279: return 19; /* alt */
|
||||
case 280: return 20; /* shift */
|
||||
case 281: return 21; /* ctrl */
|
||||
case 282: return 22; /* lock */
|
||||
|
||||
case 291: return 23; /* enter */
|
||||
case 292: return 24; /* delete */
|
||||
|
||||
case 332: return 10; /* 11 */
|
||||
case 333: return 11; /* 12 */
|
||||
|
||||
case 358: return 27; /* V1 */
|
||||
case 359: return 28; /* V2 */
|
||||
case 360: return 29; /* V3 */
|
||||
|
||||
case 430: return 25; /* tab */
|
||||
case 431: return 26; /* space */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1; /* ignore all other events */
|
||||
}
|
||||
|
|
13
src/dev.h
13
src/dev.h
|
@ -26,12 +26,17 @@ struct dev_input;
|
|||
#define MAX_DEV_NAME 256
|
||||
|
||||
struct device {
|
||||
int id;
|
||||
int fd;
|
||||
void *data;
|
||||
char name[MAX_DEV_NAME];
|
||||
char path[PATH_MAX];
|
||||
int type;
|
||||
unsigned int usbid[2]; /* vendor:product for USB devices */
|
||||
unsigned int flags;
|
||||
|
||||
int num_axes, num_buttons;
|
||||
int bnbase; /* button base (reported number of first button) */
|
||||
int *minval, *maxval; /* input value range (default: -500, 500) */
|
||||
int *fuzz; /* noise threshold */
|
||||
|
||||
|
@ -39,10 +44,14 @@ struct device {
|
|||
int (*read)(struct device*, struct dev_input*);
|
||||
void (*set_led)(struct device*, int);
|
||||
|
||||
int (*bnhack)(int bn);
|
||||
|
||||
struct device *next;
|
||||
};
|
||||
|
||||
int init_devices(void);
|
||||
void init_devices(void);
|
||||
void init_devices_serial(void);
|
||||
int init_devices_usb(void);
|
||||
|
||||
void remove_device(struct device *dev);
|
||||
|
||||
|
@ -55,4 +64,6 @@ void set_devices_led(int state);
|
|||
|
||||
struct device *get_devices(void);
|
||||
|
||||
struct device *dev_path_in_use(const char *dev_path);
|
||||
|
||||
#endif /* SPNAV_DEV_H_ */
|
||||
|
|
696
src/dev_serial.c
696
src/dev_serial.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2023 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -15,40 +15,720 @@ 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_ALLOCA_H
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "dev_serial.h"
|
||||
#include "dev.h"
|
||||
#include "event.h"
|
||||
#include "serial/sball.h"
|
||||
#include "logger.h"
|
||||
#include "proto.h"
|
||||
|
||||
#if defined(__i386__) || defined(__ia64__) || defined(WIN32) || \
|
||||
(defined(__alpha__) || defined(__alpha)) || \
|
||||
defined(__arm__) || \
|
||||
(defined(__mips__) && defined(__MIPSEL__)) || \
|
||||
defined(__SYMBIAN32__) || \
|
||||
defined(__x86_64__) || \
|
||||
defined(__LITTLE_ENDIAN__)
|
||||
#define SBALL_LITTLE_ENDIAN
|
||||
#else
|
||||
#define SBALL_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#define INP_BUF_SZ 256
|
||||
|
||||
#define EVQUEUE_SZ 64
|
||||
|
||||
enum {
|
||||
SB4000 = 1,
|
||||
FLIPXY = 2
|
||||
};
|
||||
|
||||
struct sball {
|
||||
int fd;
|
||||
unsigned int flags;
|
||||
|
||||
char buf[INP_BUF_SZ];
|
||||
int len;
|
||||
|
||||
short mot[6];
|
||||
unsigned int keystate, keymask;
|
||||
|
||||
struct termios saved_term;
|
||||
int saved_mstat;
|
||||
|
||||
struct dev_input evqueue[EVQUEUE_SZ];
|
||||
int evq_rd, evq_wr;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
int (*parse)(struct sball*, int, char*, int);
|
||||
};
|
||||
|
||||
|
||||
static void close_dev_serial(struct device *dev);
|
||||
static int read_dev_serial(struct device *dev, struct dev_input *inp);
|
||||
|
||||
static int stty_sball(struct sball *sb);
|
||||
static int stty_mag(struct sball *sb);
|
||||
static void stty_save(struct sball *sb);
|
||||
static void stty_restore(struct sball *sb);
|
||||
|
||||
static int proc_input(struct sball *sb);
|
||||
|
||||
static int mag_parsepkt(struct sball *sb, int id, char *data, int len);
|
||||
static int sball_parsepkt(struct sball *sb, int id, char *data, int len);
|
||||
|
||||
static int guess_num_buttons(struct device *dev, const char *verstr);
|
||||
|
||||
static void make_printable(char *buf, int len);
|
||||
static int read_timeout(int fd, char *buf, int bufsz, long tm_usec);
|
||||
|
||||
static void enqueue_motion(struct sball *sb, int axis, int val);
|
||||
static void gen_button_events(struct sball *sb, unsigned int prev);
|
||||
|
||||
static char *memstr(char *buf, int len, const char *str);
|
||||
|
||||
|
||||
int open_dev_serial(struct device *dev)
|
||||
{
|
||||
if(!(dev->data = sball_open(dev->path))) {
|
||||
int fd, sz;
|
||||
char buf[128];
|
||||
struct sball *sb = 0;
|
||||
|
||||
if((fd = open(dev->path, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) {
|
||||
logmsg(LOG_ERR, "open_dev_serial: failed to open device: %s: %s\n", dev->path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if(!isatty(fd)) {
|
||||
logmsg(LOG_ERR, "open_dev_serial: refusing to use %s: not a TTY\n", dev->path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
dev->fd = sball_get_fd(dev->data);
|
||||
|
||||
if(!(sb = calloc(1, sizeof *sb))) {
|
||||
logmsg(LOG_ERR, "open_dev_serial: failed to allocate sball object\n");
|
||||
goto err;
|
||||
}
|
||||
sb->dev = dev;
|
||||
dev->data = sb;
|
||||
dev->fd = sb->fd = fd;
|
||||
dev->num_axes = 6;
|
||||
dev->close = close_dev_serial;
|
||||
dev->read = read_dev_serial;
|
||||
return 0;
|
||||
|
||||
stty_save(sb);
|
||||
|
||||
if(stty_sball(sb) == -1) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Apparently some spaceballs take some time to initialize, and it's
|
||||
* necessary to wait for a little while before we start sending commands.
|
||||
*/
|
||||
sleep(1);
|
||||
|
||||
write(fd, "\r@RESET\r", 8);
|
||||
|
||||
if((sz = read_timeout(fd, buf, sizeof buf - 1, 2000000)) > 0 && memstr(buf, sz, "@1")) {
|
||||
/* we got a response, so it's a spaceball */
|
||||
make_printable(buf, sz);
|
||||
logmsg(LOG_INFO, "Spaceball detected: %s\n", buf);
|
||||
strcpy(dev->name, "Spaceball");
|
||||
|
||||
dev->num_buttons = guess_num_buttons(dev, buf);
|
||||
sb->keymask = 0xffff >> (16 - dev->num_buttons);
|
||||
logmsg(LOG_INFO, "%d buttons\n", dev->num_buttons);
|
||||
|
||||
/* set binary mode and enable automatic data packet sending. also request
|
||||
* a key event to find out as soon as possible if this is a 4000flx with
|
||||
* 12 buttons
|
||||
*/
|
||||
write(fd, "\rCB\rMSSV\rk\r", 11);
|
||||
|
||||
sb->parse = sball_parsepkt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try as a magellan spacemouse */
|
||||
if(stty_mag(sb) == -1) {
|
||||
goto err;
|
||||
}
|
||||
write(fd, "vQ\r", 3);
|
||||
|
||||
if((sz = read_timeout(fd, buf, sizeof buf - 1, 250000)) > 0 && buf[0] == 'v') {
|
||||
make_printable(buf, sz);
|
||||
logmsg(LOG_INFO, "Magellan SpaceMouse detected:\n%s\n", buf);
|
||||
strcpy(dev->name, "Magellan SpaceMouse");
|
||||
|
||||
dev->num_buttons = guess_num_buttons(dev, buf);
|
||||
sb->keymask = 0xffff >> (16 - dev->num_buttons);
|
||||
logmsg(LOG_INFO, "%d buttons\n", dev->num_buttons);
|
||||
|
||||
/* set 3D mode, not-dominant-axis, pass through motion and button packets */
|
||||
write(fd, "m3\r", 3);
|
||||
/* also attempt the compress mode-set command with extended keys enabled */
|
||||
write(fd, "c3B\r", 4);
|
||||
|
||||
sb->parse = mag_parsepkt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err:
|
||||
stty_restore(sb);
|
||||
close(fd);
|
||||
free(sb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void close_dev_serial(struct device *dev)
|
||||
{
|
||||
if(dev->data) {
|
||||
sball_close(dev->data);
|
||||
stty_restore(dev->data);
|
||||
close(dev->fd);
|
||||
}
|
||||
dev->data = 0;
|
||||
}
|
||||
|
||||
static int read_dev_serial(struct device *dev, struct dev_input *inp)
|
||||
{
|
||||
if(!dev->data || !sball_get_input(dev->data, inp)) {
|
||||
int sz;
|
||||
struct sball *sb = dev->data;
|
||||
|
||||
if(!sb) return -1;
|
||||
|
||||
while((sz = read(sb->fd, sb->buf + sb->len, INP_BUF_SZ - sb->len - 1)) > 0) {
|
||||
sb->len += sz;
|
||||
proc_input(sb);
|
||||
}
|
||||
|
||||
/* if we fill the input buffer, make a last attempt to parse it, and discard
|
||||
* it so we can receive more
|
||||
*/
|
||||
if(sb->len >= INP_BUF_SZ) {
|
||||
proc_input(sb);
|
||||
sb->len = 0;
|
||||
}
|
||||
|
||||
if(sb->evq_rd != sb->evq_wr) {
|
||||
*inp = sb->evqueue[sb->evq_rd];
|
||||
sb->evq_rd = (sb->evq_rd + 1) & (EVQUEUE_SZ - 1);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Labtec spaceball: 9600 8n1 XON/XOFF
|
||||
* Can't use canonical mode to assemble input into lines for the spaceball,
|
||||
* because binary data received for motion events can include newlines which
|
||||
* would be eaten up by the line discipline. Therefore we'll rely on VTIME=1 to
|
||||
* hopefully get more than 1 byte at a time. Alternatively we could request
|
||||
* printable reports, but I don't feel like implementing that.
|
||||
*/
|
||||
static int stty_sball(struct sball *sb)
|
||||
{
|
||||
int mstat;
|
||||
struct termios term;
|
||||
|
||||
term = sb->saved_term;
|
||||
term.c_oflag = 0;
|
||||
term.c_lflag = 0;
|
||||
term.c_cc[VMIN] = 0;
|
||||
term.c_cc[VTIME] = 1;
|
||||
|
||||
term.c_cflag = CLOCAL | CREAD | CS8 | HUPCL;
|
||||
term.c_iflag = IGNBRK | IGNPAR | IXON | IXOFF;
|
||||
|
||||
cfsetispeed(&term, B9600);
|
||||
cfsetospeed(&term, B9600);
|
||||
|
||||
if(tcsetattr(sb->fd, TCSAFLUSH, &term) == -1) {
|
||||
perror("open_dev_serial: tcsetattr");
|
||||
return -1;
|
||||
}
|
||||
tcflush(sb->fd, TCIOFLUSH);
|
||||
|
||||
mstat = sb->saved_mstat | TIOCM_DTR | TIOCM_RTS;
|
||||
ioctl(sb->fd, TIOCMGET, &mstat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Logicad magellan spacemouse: 9600 8n2 CTS/RTS
|
||||
* Since the magellan devices don't seem to send any newlines, we can rely on
|
||||
* canonical mode to feed us nice whole lines at a time.
|
||||
*/
|
||||
static int stty_mag(struct sball *sb)
|
||||
{
|
||||
int mstat;
|
||||
struct termios term;
|
||||
|
||||
term = sb->saved_term;
|
||||
term.c_oflag = 0;
|
||||
term.c_lflag = ICANON;
|
||||
term.c_cc[VMIN] = 0;
|
||||
term.c_cc[VTIME] = 0;
|
||||
term.c_cc[VEOF] = 0;
|
||||
term.c_cc[VEOL] = '\r';
|
||||
term.c_cc[VEOL2] = 0;
|
||||
term.c_cc[VERASE] = 0;
|
||||
term.c_cc[VKILL] = 0;
|
||||
|
||||
term.c_cflag = CLOCAL | CREAD | CS8 | CSTOPB | HUPCL;
|
||||
#ifdef CCTS_OFLOW
|
||||
term.c_cflag |= CCTS_OFLOW;
|
||||
#elif defined(CRTSCTS)
|
||||
term.c_cflag |= CRTSCTS;
|
||||
#endif
|
||||
term.c_iflag = IGNBRK | IGNPAR;
|
||||
|
||||
cfsetispeed(&term, B9600);
|
||||
cfsetospeed(&term, B9600);
|
||||
|
||||
if(tcsetattr(sb->fd, TCSAFLUSH, &term) == -1) {
|
||||
perror("open_dev_serial: tcsetattr");
|
||||
return -1;
|
||||
}
|
||||
tcflush(sb->fd, TCIOFLUSH);
|
||||
|
||||
mstat = sb->saved_mstat | TIOCM_DTR | TIOCM_RTS;
|
||||
ioctl(sb->fd, TIOCMGET, &mstat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stty_save(struct sball *sb)
|
||||
{
|
||||
tcgetattr(sb->fd, &sb->saved_term);
|
||||
ioctl(sb->fd, TIOCMGET, &sb->saved_mstat);
|
||||
}
|
||||
|
||||
static void stty_restore(struct sball *sb)
|
||||
{
|
||||
tcsetattr(sb->fd, TCSAFLUSH, &sb->saved_term);
|
||||
tcflush(sb->fd, TCIOFLUSH);
|
||||
ioctl(sb->fd, TIOCMSET, &sb->saved_mstat);
|
||||
}
|
||||
|
||||
|
||||
static int proc_input(struct sball *sb)
|
||||
{
|
||||
int sz;
|
||||
char *bptr = sb->buf;
|
||||
char *start = sb->buf;
|
||||
char *end = sb->buf + sb->len;
|
||||
|
||||
/* see if we have a CR in the buffer */
|
||||
while(bptr < end) {
|
||||
if(*bptr == '\r') {
|
||||
*bptr = 0;
|
||||
sb->parse(sb, *start, start + 1, bptr - start - 1);
|
||||
start = ++bptr;
|
||||
} else {
|
||||
bptr++;
|
||||
}
|
||||
}
|
||||
|
||||
sz = start - sb->buf;
|
||||
if(sz > 0) {
|
||||
memmove(sb->buf, start, sz);
|
||||
sb->len -= sz;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mag_parsepkt(struct sball *sb, int id, char *data, int len)
|
||||
{
|
||||
int i, prev, motion_pending = 0;
|
||||
unsigned int prev_key;
|
||||
|
||||
/*logmsg(LOG_DEBUG, "magellan packet: %c - %s (%d bytes)\n", (char)id, data, len);*/
|
||||
|
||||
switch(id) {
|
||||
case 'd':
|
||||
if(len != 24) {
|
||||
logmsg(LOG_WARNING, "magellan: invalid data packet, expected 24 bytes, got: %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
for(i=0; i<6; i++) {
|
||||
prev = sb->mot[i];
|
||||
sb->mot[i] = ((((int)data[0] & 0xf) << 12) | (((int)data[1] & 0xf) << 8) |
|
||||
(((int)data[2] & 0xf) << 4) | (data[3] & 0xf)) - 0x8000;
|
||||
data += 4;
|
||||
|
||||
/* flip the Z axis sign to match the spaceball */
|
||||
if(i == 2 || i == 5) {
|
||||
sb->mot[i] = -sb->mot[i];
|
||||
}
|
||||
|
||||
if(sb->mot[i] != prev) {
|
||||
enqueue_motion(sb, i, sb->mot[i]);
|
||||
motion_pending++;
|
||||
}
|
||||
}
|
||||
if(motion_pending) {
|
||||
enqueue_motion(sb, -1, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
if(len < 3) {
|
||||
logmsg(LOG_WARNING, "magellan: invalid keyboard pakcet, expected 3 bytes, got: %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
prev_key = sb->keystate;
|
||||
sb->keystate = (data[0] & 0xf) | ((data[1] & 0xf) << 4) | (((unsigned int)data[2] & 0xf) << 8);
|
||||
if(len > 3) {
|
||||
sb->keystate |= ((unsigned int)data[3] & 0xf) << 12;
|
||||
}
|
||||
|
||||
if(sb->keystate != prev_key) {
|
||||
gen_button_events(sb, prev_key);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
if(data[0] == 1) {
|
||||
logmsg(LOG_WARNING, "magellan error: illegal command: %c%c\n", data[1], data[2]);
|
||||
} else if(data[0] == 2) {
|
||||
logmsg(LOG_WARNING, "magellan error: framing error\n");
|
||||
} else {
|
||||
logmsg(LOG_WARNING, "magellan error: unknown device error\n");
|
||||
}
|
||||
return -1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sball_parsepkt(struct sball *sb, int id, char *data, int len)
|
||||
{
|
||||
int i, prev, motion_pending = 0;
|
||||
char c, *rd, *wr;
|
||||
unsigned int prev_key;
|
||||
char *errbuf, *errbuf_end;
|
||||
|
||||
errbuf = alloca(len * 16 + 32);
|
||||
|
||||
/* decode data packet, replacing escaped values with the correct ones */
|
||||
rd = wr = data;
|
||||
while(rd < data + len) {
|
||||
if((c = *rd++) == '^') {
|
||||
switch(*rd++) {
|
||||
case 'Q':
|
||||
*wr++ = 0x11; /* XON */
|
||||
break;
|
||||
case 'S':
|
||||
*wr++ = 0x13; /* XOFF */
|
||||
break;
|
||||
case 'M':
|
||||
*wr++ = 13; /* CR */
|
||||
break;
|
||||
case '^':
|
||||
*wr++ = '^';
|
||||
break;
|
||||
default:
|
||||
logmsg(LOG_WARNING, "sball decode: ignoring invalid escape code: %xh\n", (unsigned int)c);
|
||||
}
|
||||
} else {
|
||||
*wr++ = c;
|
||||
}
|
||||
}
|
||||
len = wr - data; /* update the decoded length */
|
||||
|
||||
switch(id) {
|
||||
case 'D':
|
||||
if(len != 14) {
|
||||
logmsg(LOG_WARNING, "sball: invalid data packet, expected 14 bytes, got: %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef SBALL_BIG_ENDIAN
|
||||
rd = data;
|
||||
for(i=0; i<6; i++) {
|
||||
rd += 2;
|
||||
c = rd[0];
|
||||
rd[0] = rd[1];
|
||||
rd[1] = c;
|
||||
}
|
||||
#endif
|
||||
|
||||
for(i=0; i<6; i++) {
|
||||
char *dest = (char*)(sb->mot + i);
|
||||
data += 2;
|
||||
prev = sb->mot[i];
|
||||
*dest++ = data[0];
|
||||
*dest++ = data[1];
|
||||
|
||||
if(sb->mot[i] != prev) {
|
||||
enqueue_motion(sb, i, sb->mot[i]);
|
||||
motion_pending++;
|
||||
}
|
||||
}
|
||||
if(motion_pending) {
|
||||
enqueue_motion(sb, -1, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
if(len != 2) {
|
||||
logmsg(LOG_WARNING, "sball: invalid key packet, expected 2 bytes, got: %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
if(sb->flags & SB4000) break; /* ignore K packets from spaceball 4000 devices */
|
||||
|
||||
prev_key = sb->keystate;
|
||||
/* data[1] bits 0-3 -> buttons 0,1,2,3
|
||||
* data[1] bits 4,5 (3003 L/R) -> buttons 0, 1
|
||||
* data[0] bits 0-2 -> buttons 4,5,6
|
||||
* data[0] bit 4 is (2003 pick) -> button 7
|
||||
*/
|
||||
sb->keystate = ((data[1] & 0xf) | ((data[1] >> 4) & 3) | ((data[0] & 7) << 4) |
|
||||
((data[0] & 0x10) << 3)) & sb->keymask;
|
||||
|
||||
if(sb->keystate != prev_key) {
|
||||
gen_button_events(sb, prev_key);
|
||||
}
|
||||
break;
|
||||
|
||||
case '.':
|
||||
if(len != 2) {
|
||||
logmsg(LOG_WARNING, "sball: invalid sb4k key packet, expected 2 bytes, got: %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
/* spaceball 4000 key packet */
|
||||
if(!(sb->flags & SB4000)) {
|
||||
logmsg(LOG_INFO, "Switching to spaceball 4000flx/5000flx-a mode (12 buttons) \n");
|
||||
sb->flags |= SB4000;
|
||||
sb->dev->num_buttons = 12; /* might have guessed 8 before */
|
||||
sb->keymask = 0xfff;
|
||||
strcpy(sb->dev->name, "Spaceball 4000FLX");
|
||||
sb->dev->type = DEV_SB4000;
|
||||
}
|
||||
/* update orientation flag (actually don't bother) */
|
||||
/*
|
||||
if(data[0] & 0x20) {
|
||||
sb->flags |= FLIPXY;
|
||||
} else {
|
||||
sb->flags &= ~FLIPXY;
|
||||
}
|
||||
*/
|
||||
|
||||
prev_key = sb->keystate;
|
||||
/* data[1] bits 0-5 -> buttons 0,1,2,3,4,5
|
||||
* data[1] bit 7 -> button 6
|
||||
* data[0] bits 0-4 -> buttons 7,8,9,10,11
|
||||
*/
|
||||
sb->keystate = (data[1] & 0x3f) | ((data[1] & 0x80) >> 1) | ((data[0] & 0x1f) << 7);
|
||||
if(sb->keystate != prev_key) {
|
||||
gen_button_events(sb, prev_key);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
strcpy(errbuf, "sball: error:");
|
||||
errbuf_end = errbuf + 13;
|
||||
for(i=0; i<len; i++) {
|
||||
if(isprint((int)data[i])) {
|
||||
errbuf_end += sprintf(errbuf_end, " %c", data[i]);
|
||||
} else {
|
||||
errbuf_end += sprintf(errbuf_end, " %02xh", (unsigned int)data[i]);
|
||||
}
|
||||
}
|
||||
logmsg(LOG_WARNING, errbuf);
|
||||
break;
|
||||
|
||||
case 'M': /* ignore MSS responses */
|
||||
case '?': /* ignore unrecognized command errors */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* DEBUG */
|
||||
errbuf_end = errbuf + sprintf(errbuf, "sball: got '%c' packet:", (char)id);
|
||||
for(i=0; i<len; i++) {
|
||||
errbuf_end += sprintf(errbuf_end, " %02x", (unsigned int)data[i]);
|
||||
}
|
||||
strcpy(errbuf_end, "\n");
|
||||
logmsg(LOG_WARNING, errbuf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int guess_num_buttons(struct device *dev, const char *verstr)
|
||||
{
|
||||
int major, minor;
|
||||
const char *s;
|
||||
|
||||
if((s = strstr(verstr, "Firmware version"))) { /* spaceball */
|
||||
/* try to guess based on firmware number */
|
||||
if(sscanf(s + 17, "%d.%d", &major, &minor) == 2 && major == 2) {
|
||||
if(minor == 35 || minor == 62 || minor == 63) {
|
||||
dev->type = DEV_SB3003;
|
||||
strcpy(dev->name, "Spaceball 3003/3003C");
|
||||
return 2; /* spaceball 3003/3003C */
|
||||
}
|
||||
if(minor == 43 || minor == 45) {
|
||||
dev->type = DEV_SB4000;
|
||||
strcpy(dev->name, "Spaceball 4000FLX/5000FLX-A");
|
||||
return 12; /* spaceball 4000flx/5000flx-a */
|
||||
}
|
||||
if(minor == 2 || minor == 13 || minor == 15 || minor == 42) {
|
||||
/* 2.42 is also used by spaceball 4000flx. we'll guess 2003c for
|
||||
* now, and change the buttons to 12 first time we get a '.'
|
||||
* packet. I'll also request a key report during init to make
|
||||
* sure this happens as soon as possible, before clients have a
|
||||
* chance to connect.
|
||||
*/
|
||||
dev->type = DEV_SB2003;
|
||||
strcpy(dev->name, "Spaceball 1003/2003/2003C");
|
||||
return 8; /* spaceball 1003/2003/2003c */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(strstr(verstr, "MAGELLAN")) {
|
||||
dev->type = DEV_SM;
|
||||
strcpy(dev->name, "Magellan SpaceMouse");
|
||||
return 11; /* magellan spacemouse (assume ext buttons on plus/xt) */
|
||||
}
|
||||
|
||||
if(strstr(verstr, "SPACEBALL")) {
|
||||
dev->type = DEV_SM5000;
|
||||
strcpy(dev->name, "Spaceball 5000");
|
||||
return 12; /* spaceball 5000 */
|
||||
}
|
||||
|
||||
if(strstr(verstr, "CadMan")) {
|
||||
dev->type = DEV_SMCADMAN;
|
||||
strcpy(dev->name, "CadMan");
|
||||
return 4;
|
||||
}
|
||||
|
||||
logmsg(LOG_DEBUG, "Can't guess number of buttons, default to 8, report this as a bug!\n");
|
||||
return 8;
|
||||
}
|
||||
|
||||
static void make_printable(char *buf, int len)
|
||||
{
|
||||
int i, c;
|
||||
char *wr = buf;
|
||||
|
||||
for(i=0; i<len; i++) {
|
||||
c = *buf++;
|
||||
if(c == '\r') {
|
||||
*wr++ = '\n';
|
||||
while(*buf == '\n' || *buf == '\r') buf++;
|
||||
} else {
|
||||
*wr++ = c;
|
||||
}
|
||||
}
|
||||
*wr = 0;
|
||||
}
|
||||
|
||||
static int read_timeout(int fd, char *buf, int bufsz, long tm_usec)
|
||||
{
|
||||
int res;
|
||||
long usec, sz = 0;
|
||||
struct timeval tv0, tv;
|
||||
fd_set rdset;
|
||||
|
||||
if(!buf || bufsz <= 0) return -1;
|
||||
|
||||
usec = tm_usec;
|
||||
gettimeofday(&tv0, 0);
|
||||
|
||||
while(sz < bufsz && usec > 0) {
|
||||
tv.tv_sec = usec / 1000000;
|
||||
tv.tv_usec = usec % 1000000;
|
||||
|
||||
FD_ZERO(&rdset);
|
||||
FD_SET(fd, &rdset);
|
||||
if((res = select(fd + 1, &rdset, 0, 0, &tv)) > 0 && FD_ISSET(fd, &rdset)) {
|
||||
sz += read(fd, buf + sz, bufsz - sz);
|
||||
buf[sz] = 0;
|
||||
tm_usec = usec = 128000; /* wait 128ms for the rest of the message to appear */
|
||||
gettimeofday(&tv0, 0);
|
||||
continue;
|
||||
}
|
||||
if(res == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
|
||||
break;
|
||||
}
|
||||
gettimeofday(&tv, 0);
|
||||
usec = tm_usec - ((tv.tv_sec - tv0.tv_sec) * 1000000 + (tv.tv_usec - tv0.tv_usec));
|
||||
}
|
||||
|
||||
return sz > 0 ? sz : -1;
|
||||
}
|
||||
|
||||
static void enqueue_motion(struct sball *sb, int axis, int val)
|
||||
{
|
||||
struct dev_input *inp = sb->evqueue + sb->evq_wr;
|
||||
|
||||
sb->evq_wr = (sb->evq_wr + 1) & (EVQUEUE_SZ - 1);
|
||||
if(sb->evq_wr == sb->evq_rd) {
|
||||
/* overflow, drop the oldest event */
|
||||
sb->evq_rd = (sb->evq_rd + 1) & (EVQUEUE_SZ - 1);
|
||||
}
|
||||
|
||||
if(axis >= 0) {
|
||||
inp->type = INP_MOTION;
|
||||
inp->idx = axis;
|
||||
inp->val = val;
|
||||
} else {
|
||||
inp->type = INP_FLUSH;
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_button_events(struct sball *sb, unsigned int prev)
|
||||
{
|
||||
int i;
|
||||
unsigned int bit = 1;
|
||||
unsigned int diff = sb->keystate ^ prev;
|
||||
struct dev_input *inp;
|
||||
|
||||
for(i=0; i<16; i++) {
|
||||
if(diff & bit) {
|
||||
inp = sb->evqueue + sb->evq_wr;
|
||||
sb->evq_wr = (sb->evq_wr + 1) & (EVQUEUE_SZ - 1);
|
||||
if(sb->evq_wr == sb->evq_rd) {
|
||||
/* overflow, drop the oldest event */
|
||||
sb->evq_rd = (sb->evq_rd + 1) & (EVQUEUE_SZ - 1);
|
||||
}
|
||||
|
||||
inp->type = INP_BUTTON;
|
||||
inp->idx = i;
|
||||
inp->val = sb->keystate & bit ? 1 : 0;
|
||||
}
|
||||
bit <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static char *memstr(char *buf, int len, const char *str)
|
||||
{
|
||||
int i, slen = strlen(str);
|
||||
for(i=0; i<len - slen; i++) {
|
||||
if(memcmp(buf + i, str, slen) == 0) {
|
||||
return buf + i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -28,11 +30,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "logger.h"
|
||||
|
||||
|
||||
void free_usb_devices_list(struct usb_device_info *list)
|
||||
void free_usb_devices_list(struct usb_dev_info *list)
|
||||
{
|
||||
while(list) {
|
||||
int i;
|
||||
struct usb_device_info *tmp = list;
|
||||
struct usb_dev_info *tmp = list;
|
||||
list = list->next;
|
||||
|
||||
free(tmp->name);
|
||||
|
@ -43,7 +45,7 @@ void free_usb_devices_list(struct usb_device_info *list)
|
|||
}
|
||||
}
|
||||
|
||||
void print_usb_device_info(struct usb_device_info *devinfo)
|
||||
void print_usb_device_info(struct usb_dev_info *devinfo)
|
||||
{
|
||||
int i, sz = 64;
|
||||
char *devname, *buf, *s;
|
||||
|
|
|
@ -18,23 +18,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#ifndef SPNAV_DEV_USB_H_
|
||||
#define SPNAV_DEV_USB_H_
|
||||
|
||||
#define VID_3DCONN 0x256f
|
||||
#define PID_WIRELESS 0xc652
|
||||
|
||||
struct device;
|
||||
|
||||
int open_dev_usb(struct device *dev);
|
||||
|
||||
/* USB device enumeration and matching */
|
||||
#define MAX_USB_DEV_FILES 16
|
||||
struct usb_device_info {
|
||||
struct usb_dev_info {
|
||||
char *name;
|
||||
int num_devfiles;
|
||||
char *devfiles[MAX_USB_DEV_FILES];
|
||||
int vendorid, productid;
|
||||
|
||||
struct usb_device_info *next;
|
||||
struct usb_dev_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);
|
||||
struct usb_dev_info *find_usb_devices(int (*match)(const struct usb_dev_info*));
|
||||
void free_usb_devices_list(struct usb_dev_info *list);
|
||||
void print_usb_device_info(struct usb_dev_info *devinfo);
|
||||
|
||||
/* see usbdb_entry table bnmap field in dev.c */
|
||||
int bnhack_smpro(int bn);
|
||||
int bnhack_sment(int bn);
|
||||
|
||||
#endif /* SPNAV_DEV_USB_H_ */
|
||||
|
|
|
@ -32,10 +32,10 @@ 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_dev_info *find_usb_devices(int (*match)(const struct usb_dev_info*))
|
||||
{
|
||||
struct usb_device_info *devlist = 0;
|
||||
struct usb_device_info devinfo;
|
||||
struct usb_dev_info *devlist = 0;
|
||||
struct usb_dev_info devinfo;
|
||||
/*static const int vendor_id = 1133;*/ /* 3dconnexion */
|
||||
static char dev_path[512];
|
||||
io_object_t dev;
|
||||
|
@ -70,7 +70,7 @@ struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_in
|
|||
/* TODO retrieve vendor id and product id */
|
||||
|
||||
if(!match || match(&devinfo)) {
|
||||
struct usb_device_info *node = malloc(sizeof *node);
|
||||
struct usb_dev_info *node = malloc(sizeof *node);
|
||||
if(node) {
|
||||
if(verbose) {
|
||||
logmsg(LOG_INFO, "found usb device: ");
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
#include "config.h"
|
||||
#include <dev/usb/usb_ioctl.h>
|
||||
#include <dev/usb/usbhid.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <glob.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "spnavd.h"
|
||||
#include "client.h"
|
||||
#include "dev.h"
|
||||
#include "dev_usb.h"
|
||||
#include "event.h"
|
||||
|
||||
#define AXES 6
|
||||
#define IS_DEV_OPEN(dev) ((dev)->fd >= 0)
|
||||
|
||||
static void close_hid(struct device *dev)
|
||||
{
|
||||
if(IS_DEV_OPEN(dev)) {
|
||||
dev->set_led(dev, 0);
|
||||
close(dev->fd);
|
||||
dev->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_led_hid(struct device *dev, int state)
|
||||
{
|
||||
struct usb_gen_descriptor d = {0};
|
||||
uint8_t buf[2];
|
||||
|
||||
/*
|
||||
* TODO: Get stuff from report descriptor:
|
||||
* - Is there an LED?
|
||||
* - How big is the message?
|
||||
* - What bits should be set?
|
||||
*/
|
||||
|
||||
if(!IS_DEV_OPEN(dev))
|
||||
return;
|
||||
|
||||
buf[0] = 4;
|
||||
buf[1] = state ? 15 : 0;
|
||||
d.ugd_data = buf;
|
||||
d.ugd_maxlen = sizeof buf;
|
||||
d.ugd_report_type = UHID_OUTPUT_REPORT;
|
||||
|
||||
if(ioctl(dev->fd, USB_SET_REPORT, &d) == -1) {
|
||||
logmsg(LOG_ERR, "Unable to set LED: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t button_event(struct dev_input *inp, uint32_t last, uint32_t curr)
|
||||
{
|
||||
uint32_t new;
|
||||
int b;
|
||||
|
||||
new = last ^ curr;
|
||||
b = ffs(new) - 1;
|
||||
|
||||
if(new) {
|
||||
inp->type = INP_BUTTON;
|
||||
inp->idx = b;
|
||||
inp->val = (curr >> b) & 1;
|
||||
last ^= (1 << b);
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
static uint32_t axis_event(struct dev_input *inp, int16_t *curr, unsigned int *flush)
|
||||
{
|
||||
int axis;
|
||||
|
||||
if((axis = ffs(*flush)) > 0) {
|
||||
axis--;
|
||||
*flush &= ~(1 << axis);
|
||||
if(axis < AXES) {
|
||||
inp->type = INP_MOTION;
|
||||
inp->idx = axis;
|
||||
inp->val = curr[axis];
|
||||
return 0;
|
||||
}
|
||||
inp->type = INP_FLUSH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int read_hid(struct device *dev, struct dev_input *inp)
|
||||
{
|
||||
uint8_t iev[1024];
|
||||
int rdbytes;
|
||||
int i;
|
||||
static uint32_t last_buttons;
|
||||
static uint32_t curr_buttons;
|
||||
static int16_t curr_pos[AXES];
|
||||
static unsigned int flush = 0;
|
||||
|
||||
if(!IS_DEV_OPEN(dev))
|
||||
return -1;
|
||||
|
||||
if(last_buttons != curr_buttons) {
|
||||
last_buttons = button_event(inp, last_buttons, curr_buttons);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(axis_event(inp, curr_pos, &flush) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
rdbytes = read(dev->fd, &iev, sizeof iev);
|
||||
} while(rdbytes == -1 && errno == EINTR);
|
||||
|
||||
/* disconnect? */
|
||||
if(rdbytes == -1) {
|
||||
if(errno != EAGAIN) {
|
||||
logmsg(LOG_ERR, "read error: %s\n", strerror(errno));
|
||||
remove_device(dev);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(rdbytes > 0) {
|
||||
switch(iev[0]) {
|
||||
case 1: /* Three axis... X, Y, Z */
|
||||
flush = 0x40;
|
||||
for(i=0; i<rdbytes/2 && i<AXES; i++) {
|
||||
flush |= (1 << i);
|
||||
curr_pos[i] = iev[i * 2 + 1] | (iev[i * 2 + 2] << 8);
|
||||
}
|
||||
return axis_event(inp, curr_pos, &flush);
|
||||
case 2: /* Three axis... rX, rY, rZ */
|
||||
flush = 0x78;
|
||||
curr_pos[3] = iev[1] | (iev[2] << 8);
|
||||
curr_pos[4] = iev[3] | (iev[4] << 8);
|
||||
curr_pos[5] = iev[5] | (iev[6] << 8);
|
||||
return axis_event(inp, curr_pos, &flush);
|
||||
case 3: /* Button change event. */
|
||||
flush = 1;
|
||||
curr_buttons = iev[1] | (iev[2] << 8) | (iev[3] << 16);
|
||||
if(last_buttons != curr_buttons) {
|
||||
last_buttons = button_event(inp, last_buttons, curr_buttons);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
case 23: /* Battery char level */
|
||||
logmsg(LOG_INFO, "Battery level: %%%d\n", iev[1]);
|
||||
break;
|
||||
default:
|
||||
if(verbose) {
|
||||
logmsg(LOG_DEBUG, "unhandled event: %d\n", iev[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int open_dev_usb(struct device *dev)
|
||||
{
|
||||
if((dev->fd = open(dev->path, O_RDWR | O_NONBLOCK)) == -1) {
|
||||
if((dev->fd = open(dev->path, O_RDONLY | O_NONBLOCK)) == -1) {
|
||||
logmsg(LOG_ERR, "failed to open device: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
logmsg(LOG_WARNING, "opened device read-only, LEDs won't work\n");
|
||||
}
|
||||
|
||||
if(cfg.led == LED_ON || (cfg.led == LED_AUTO && first_client())) {
|
||||
set_led_hid(dev, 1);
|
||||
} else {
|
||||
/* Some devices start with the LED enabled, make sure to turn it off
|
||||
* explicitly if necessary.
|
||||
*
|
||||
* XXX G.Ebner reports that some devices (SpaceMouse Compact at least)
|
||||
* fail to turn their LED off at startup if it's not turned explicitly
|
||||
* on first. We'll need to investigate further, but it doesn't seem to
|
||||
* cause any visible blinking, so let's leave the redundant call to
|
||||
* enable it first for now. See github pull request #39:
|
||||
* https://github.com/FreeSpacenav/spacenavd/pull/39
|
||||
*/
|
||||
set_led_hid(dev, 1);
|
||||
set_led_hid(dev, 0);
|
||||
}
|
||||
|
||||
/* fill the device function pointers */
|
||||
dev->close = close_hid;
|
||||
dev->read = read_hid;
|
||||
dev->set_led = set_led_hid;
|
||||
|
||||
/* TODO until we flesh out the USB code on FreeBSD, let's fill the structure
|
||||
* with fake but plausible information.
|
||||
*/
|
||||
dev->bnbase = 0;
|
||||
dev->num_buttons = 2;
|
||||
dev->num_axes = 6;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct usb_dev_info *find_usb_devices(int (*match)(const struct usb_dev_info*))
|
||||
{
|
||||
struct usb_dev_info *node, *devlist = 0;
|
||||
struct usb_device_info devinfo;
|
||||
glob_t gl;
|
||||
size_t si;
|
||||
int fd;
|
||||
|
||||
if(verbose) {
|
||||
logmsg(LOG_INFO, "Device detection, checking \"/dev/uhid*\"\n");
|
||||
}
|
||||
|
||||
if(glob("/dev/uhid*", 0, NULL, &gl) != 0) {
|
||||
return devlist;
|
||||
}
|
||||
|
||||
for(si=0; si<gl.gl_pathc; si++) {
|
||||
logmsg(LOG_INFO, "checking \"%s\"... ", gl.gl_pathv[si]);
|
||||
|
||||
if((fd = open(gl.gl_pathv[si], O_RDWR)) == -1) {
|
||||
logmsg(LOG_ERR, "Failed to open \"%s\": %s\n", gl.gl_pathv[si], strerror(errno));
|
||||
continue;
|
||||
}
|
||||
if(ioctl(fd, USB_GET_DEVICEINFO, &devinfo) != -1) {
|
||||
if((node = calloc(1, sizeof *node))) {
|
||||
node->vendorid = devinfo.udi_vendorNo;
|
||||
node->productid = devinfo.udi_productNo;
|
||||
node->name = strdup(devinfo.udi_product);
|
||||
if(node->name != NULL) {
|
||||
node->devfiles[0] = strdup(gl.gl_pathv[si]);
|
||||
if(node->devfiles[0] != NULL) {
|
||||
node->num_devfiles = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!node || !node->num_devfiles) {
|
||||
logmsg(LOG_ERR, "failed to allocate usb device info node: %s\n", strerror(errno));
|
||||
free_usb_devices_list(node);
|
||||
} else if(verbose) {
|
||||
logmsg(LOG_INFO, "found usb device [%x:%x]: \"%s\" (%s) \n", node->vendorid, node->productid,
|
||||
node->name ? node->name : "unknown", node->devfiles[0]);
|
||||
}
|
||||
if(!match || match(node)) {
|
||||
if(verbose) {
|
||||
logmsg(LOG_INFO, "found usb device: ");
|
||||
print_usb_device_info(node);
|
||||
}
|
||||
|
||||
node->next = devlist;
|
||||
devlist = node;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
globfree(&gl);
|
||||
|
||||
return devlist;
|
||||
}
|
||||
|
||||
#else
|
||||
int spacenavd_dev_usb_freebsd_silence_empty_warning;
|
||||
#endif /* __FreeBSD__ */
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -62,7 +62,6 @@ 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, axes_rel = 0, axes_abs = 0;
|
||||
|
@ -110,6 +109,15 @@ int open_dev_usb(struct device *dev)
|
|||
}
|
||||
dev->num_axes = axes_rel + axes_abs;
|
||||
if(!dev->num_axes) {
|
||||
if(dev->usbid[0] == VID_3DCONN && dev->usbid[1] == PID_WIRELESS) {
|
||||
/* a wireless 3Dconnexion device without axes is probably one of the
|
||||
* CadMouse products, drop it.
|
||||
*/
|
||||
logmsg(LOG_DEBUG, "No axes detected, probably a CadMouse, dropping\n");
|
||||
close(dev->fd);
|
||||
dev->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
logmsg(LOG_WARNING, "failed to retrieve number of axes. assuming 6\n");
|
||||
dev->num_axes = 6;
|
||||
} else {
|
||||
|
@ -119,6 +127,7 @@ int open_dev_usb(struct device *dev)
|
|||
}
|
||||
|
||||
/* get number of buttons */
|
||||
dev->bnbase = -1;
|
||||
dev->num_buttons = 0;
|
||||
if(ioctl(dev->fd, EVIOCGBIT(EV_KEY, sizeof evtype_mask), evtype_mask) != -1) {
|
||||
for(i=0; i<KEY_CNT; i++) {
|
||||
|
@ -126,18 +135,52 @@ int open_dev_usb(struct device *dev)
|
|||
int bit = i % 8;
|
||||
|
||||
if(evtype_mask[idx] & (1 << bit)) {
|
||||
if(dev->bnbase < 0) {
|
||||
dev->bnbase = idx * 8 + bit;
|
||||
}
|
||||
dev->num_buttons++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logmsg(LOG_DEBUG, "EVIOCGBIT(EV_KEY) ioctl failed: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
if(dev->bnbase == -1) {
|
||||
dev->bnbase = 0;
|
||||
}
|
||||
|
||||
/* for devices marked for disjointed button range remapping, log the button
|
||||
* count reported before applying the remapping function
|
||||
*/
|
||||
if(dev->bnhack) {
|
||||
logmsg(LOG_DEBUG, "Device %04x:%04x reports %d buttons before disjointed button remapping\n",
|
||||
dev->usbid[0], dev->usbid[1], dev->num_buttons);
|
||||
}
|
||||
|
||||
if(dev->bnhack) {
|
||||
dev->num_buttons = dev->bnhack(-1);
|
||||
} else {
|
||||
if(dev->usbid[0] == VID_3DCONN && dev->usbid[1] == PID_WIRELESS) {
|
||||
/* Wireless devices use the same dongle, try to guess which actual
|
||||
* device this is, and apply the button hack if it's a SpcMouse Pro
|
||||
*/
|
||||
if(dev->num_buttons > 2) {
|
||||
dev->type = DEV_SMPROW;
|
||||
dev->bnhack = bnhack_smpro;
|
||||
dev->num_buttons = bnhack_smpro(-1);
|
||||
strcpy(dev->name, "3Dconnexion SpaceMouse Pro Wireless (guess)");
|
||||
} else {
|
||||
dev->type = DEV_SMW;
|
||||
strcpy(dev->name, "3Dconnexion SpaceMouse Wireless (guess)");
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!dev->num_buttons) {
|
||||
logmsg(LOG_WARNING, "failed to retrieve number of buttons, will default to 2\n");
|
||||
dev->num_buttons = 2;
|
||||
} else {
|
||||
if(verbose) {
|
||||
logmsg(LOG_INFO, " Number of buttons: %d\n", dev->num_buttons);
|
||||
logmsg(LOG_INFO, " Number of buttons: %d (evdev offset: %d)\n", dev->num_buttons, dev->bnbase);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,8 +221,21 @@ int open_dev_usb(struct device *dev)
|
|||
/* set non-blocking */
|
||||
fcntl(dev->fd, F_SETFL, fcntl(dev->fd, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
if(cfg.led == 1 || (cfg.led == 2 && first_client())) {
|
||||
if(cfg.led == LED_ON || (cfg.led == LED_AUTO && first_client())) {
|
||||
set_led_evdev(dev, 1);
|
||||
} else {
|
||||
/* Some devices start with the LED enabled, make sure to turn it off
|
||||
* explicitly if necessary.
|
||||
*
|
||||
* XXX G.Ebner reports that some devices (SpaceMouse Compact at least)
|
||||
* fail to turn their LED off at startup if it's not turned explicitly
|
||||
* on first. We'll need to investigate further, but it doesn't seem to
|
||||
* cause any visible blinking, so let's leave the redundant call to
|
||||
* enable it first for now. See github pull request #39:
|
||||
* https://github.com/FreeSpacenav/spacenavd/pull/39
|
||||
*/
|
||||
set_led_evdev(dev, 1);
|
||||
set_led_evdev(dev, 0);
|
||||
}
|
||||
|
||||
/* fill the device function pointers */
|
||||
|
@ -234,8 +290,6 @@ static int read_evdev(struct device *dev, struct dev_input *inp)
|
|||
}
|
||||
|
||||
if(rdbytes > 0) {
|
||||
inp->tm = iev.time;
|
||||
|
||||
switch(iev.type) {
|
||||
case EV_REL:
|
||||
inp->type = INP_MOTION;
|
||||
|
@ -253,8 +307,16 @@ static int read_evdev(struct device *dev, struct dev_input *inp)
|
|||
|
||||
case EV_KEY:
|
||||
inp->type = INP_BUTTON;
|
||||
inp->idx = iev.code - BTN_0;
|
||||
if(dev->bnhack) {
|
||||
/* for problematic devices, remap button numbers to a contiguous range */
|
||||
if((inp->idx = dev->bnhack(iev.code)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
inp->idx = iev.code - dev->bnbase;
|
||||
}
|
||||
inp->val = iev.value;
|
||||
/*logmsg(LOG_DEBUG, "EV_KEY c:%d (%d) v:%d\n", iev.code, inp->idx, iev.value);*/
|
||||
break;
|
||||
|
||||
case EV_SYN:
|
||||
|
@ -262,8 +324,14 @@ static int read_evdev(struct device *dev, struct dev_input *inp)
|
|||
/*printf("[%s] EV_SYN\n", dev->name);*/
|
||||
break;
|
||||
|
||||
case EV_MSC:
|
||||
/* don't know what to do with these MSC events, the spacemouse enterprise
|
||||
* sends them on every button press. Silently ignore them for now.
|
||||
*/
|
||||
return -1;
|
||||
|
||||
default:
|
||||
if(verbose) {
|
||||
if(verbose > 1) {
|
||||
logmsg(LOG_DEBUG, "unhandled event: %d\n", iev.type);
|
||||
}
|
||||
return -1;
|
||||
|
@ -291,9 +359,9 @@ static void set_led_evdev(struct device *dev, int state)
|
|||
}
|
||||
|
||||
#define PROC_DEV "/proc/bus/input/devices"
|
||||
struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_info*))
|
||||
struct usb_dev_info *find_usb_devices(int (*match)(const struct usb_dev_info*))
|
||||
{
|
||||
struct usb_device_info *devlist = 0, devinfo;
|
||||
struct usb_dev_info *devlist = 0, devinfo;
|
||||
int i, buf_used, buf_len, bytes_read;
|
||||
char buf[1024];
|
||||
char *buf_pos, *section_start, *next_section = 0, *cur_line, *next_line;
|
||||
|
@ -301,7 +369,7 @@ struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_in
|
|||
DIR *dir;
|
||||
struct dirent *dent;
|
||||
|
||||
if(verbose) {
|
||||
if(verbose > 1) {
|
||||
logmsg(LOG_INFO, "Device detection, parsing " PROC_DEV "\n");
|
||||
}
|
||||
|
||||
|
@ -336,7 +404,7 @@ struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_in
|
|||
/* break to read from file again */
|
||||
break;
|
||||
}
|
||||
/* set second newline to teminating null */
|
||||
/* set second newline to terminating null */
|
||||
next_section[1] = 0;
|
||||
/* point to start of next section */
|
||||
next_section += 2;
|
||||
|
@ -408,9 +476,9 @@ struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_in
|
|||
*/
|
||||
if(devinfo.num_devfiles > 0 && (!match || match(&devinfo))) {
|
||||
/* add it to the list */
|
||||
struct usb_device_info *node = malloc(sizeof *node);
|
||||
struct usb_dev_info *node = malloc(sizeof *node);
|
||||
if(node) {
|
||||
if(verbose) {
|
||||
if(verbose > 1 || (verbose && !dev_path_in_use(devinfo.devfiles[0]))) {
|
||||
logmsg(LOG_INFO, "found usb device [%x:%x]: \"%s\" (%s) \n", devinfo.vendorid, devinfo.productid,
|
||||
devinfo.name ? devinfo.name : "unknown", devinfo.devfiles[0]);
|
||||
}
|
||||
|
@ -445,7 +513,7 @@ struct usb_device_info *find_usb_devices(int (*match)(const struct usb_device_in
|
|||
/* otherwise try the alternative detection in case it finds something... */
|
||||
|
||||
alt_detect:
|
||||
if(verbose) {
|
||||
if(verbose > 1) {
|
||||
logmsg(LOG_INFO, "trying alternative detection, querying /dev/input/ devices...\n");
|
||||
}
|
||||
|
||||
|
@ -472,8 +540,8 @@ alt_detect:
|
|||
sprintf(devinfo.devfiles[0], "/dev/input/%s", dent->d_name);
|
||||
devinfo.num_devfiles = 1;
|
||||
|
||||
if(verbose) {
|
||||
logmsg(LOG_INFO, " trying \"%s\" ... ", devinfo.devfiles[0]);
|
||||
if(verbose > 1) {
|
||||
logmsg(LOG_INFO, " trying \"%s\" ... \n", devinfo.devfiles[0]);
|
||||
}
|
||||
|
||||
if(stat(devinfo.devfiles[0], &st) == -1 || !S_ISCHR(st.st_mode)) {
|
||||
|
@ -502,9 +570,9 @@ alt_detect:
|
|||
}
|
||||
|
||||
if(!match || match(&devinfo)) {
|
||||
struct usb_device_info *node = malloc(sizeof *node);
|
||||
struct usb_dev_info *node = malloc(sizeof *node);
|
||||
if(node) {
|
||||
if(verbose) {
|
||||
if(verbose > 1 || (verbose && !dev_path_in_use(devinfo.devfiles[0]))) {
|
||||
logmsg(LOG_INFO, "found usb device [%x:%x]: \"%s\" (%s) \n", devinfo.vendorid, devinfo.productid,
|
||||
devinfo.name ? devinfo.name : "unknown", devinfo.devfiles[0]);
|
||||
}
|
||||
|
@ -528,4 +596,6 @@ alt_detect:
|
|||
return devlist;
|
||||
}
|
||||
|
||||
#else
|
||||
int spacenavd_dev_usb_linux_silence_empty_warning;
|
||||
#endif /* __linux__ */
|
||||
|
|
|
@ -15,7 +15,7 @@ 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if !defined(__linux__) && !(defined(__APPLE__) && defined(__MACH__))
|
||||
#if !defined(__linux__) && !(defined(__APPLE__) && defined(__MACH__)) && !defined(__FreeBSD__)
|
||||
|
||||
#include <stdio.h>
|
||||
#include "logger.h"
|
||||
|
@ -25,16 +25,12 @@ 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*))
|
||||
struct usb_dev_info *find_usb_devices(int (*match)(const struct usb_dev_info*))
|
||||
{
|
||||
logmsg(LOG_ERR, message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_usb_devices_list(struct usb_device_info *list)
|
||||
{
|
||||
}
|
||||
|
||||
int open_dev_usb(struct device *dev)
|
||||
{
|
||||
return -1;
|
||||
|
|
141
src/event.c
141
src/event.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2013 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2023 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "event.h"
|
||||
#include "client.h"
|
||||
#include "proto_unix.h"
|
||||
|
@ -34,6 +35,11 @@ enum {
|
|||
MOT_RX, MOT_RY, MOT_RZ
|
||||
};
|
||||
|
||||
enum {
|
||||
BTN_RELEASE = 0,
|
||||
BTN_PRESS = 1
|
||||
};
|
||||
|
||||
struct dev_event {
|
||||
spnav_event event;
|
||||
struct timeval timeval;
|
||||
|
@ -44,12 +50,17 @@ struct dev_event {
|
|||
|
||||
static struct dev_event *add_dev_event(struct device *dev);
|
||||
static struct dev_event *device_event_in_use(struct device *dev);
|
||||
static void handle_button_action(int act, int val);
|
||||
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 int disable_translation, disable_rotation, dom_axis_mode;
|
||||
static int cur_axis_mag[6], cur_dom_axis;
|
||||
|
||||
|
||||
static struct dev_event *add_dev_event(struct device *dev)
|
||||
{
|
||||
struct dev_event *dev_ev, *iter;
|
||||
|
@ -116,6 +127,21 @@ static struct dev_event *device_event_in_use(struct device *dev)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline int map_axis(int devaxis)
|
||||
{
|
||||
const static int swaptab[] = {0, 2, 1, 3, 5, 4};
|
||||
|
||||
int axis = cfg.map_axis[devaxis];
|
||||
if(axis < 0 || axis >= 6) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(cfg.swapyz) {
|
||||
return swaptab[axis];
|
||||
}
|
||||
return axis;
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
@ -124,19 +150,41 @@ static struct dev_event *device_event_in_use(struct device *dev)
|
|||
*/
|
||||
void process_input(struct device *dev, struct dev_input *inp)
|
||||
{
|
||||
int sign;
|
||||
int sign, axis, abs_val;
|
||||
struct dev_event *dev_ev;
|
||||
float sens_rot, sens_trans, axis_sens;
|
||||
spnav_event ev;
|
||||
|
||||
switch(inp->type) {
|
||||
case INP_MOTION:
|
||||
inp->idx = cfg.map_axis[inp->idx];
|
||||
ev.type = EVENT_RAWAXIS;
|
||||
ev.axis.idx = inp->idx;
|
||||
ev.axis.value = inp->val;
|
||||
broadcast_event(&ev);
|
||||
|
||||
if(abs(inp->val) < cfg.dead_threshold[inp->idx] ) {
|
||||
abs_val = abs(inp->val);
|
||||
|
||||
if(abs_val < cfg.dead_threshold[inp->idx] ) {
|
||||
inp->val = 0;
|
||||
}
|
||||
sign = cfg.invert[inp->idx] ? -1 : 1;
|
||||
if((axis = map_axis(inp->idx)) == -1) {
|
||||
break;
|
||||
}
|
||||
sign = cfg.invert[axis] ? -1 : 1;
|
||||
|
||||
inp->val = (int)((float)inp->val * cfg.sensitivity * (inp->idx < 3 ? cfg.sens_trans[inp->idx] : cfg.sens_rot[inp->idx - 3]));
|
||||
sens_rot = disable_rotation ? 0 : cfg.sens_rot[axis - 3];
|
||||
sens_trans = disable_translation ? 0 : cfg.sens_trans[axis];
|
||||
axis_sens = axis < 3 ? sens_trans : sens_rot;
|
||||
|
||||
if(dom_axis_mode && axis < 6) {
|
||||
if(abs_val > cur_axis_mag[cur_dom_axis]) {
|
||||
cur_dom_axis = axis;
|
||||
} else {
|
||||
inp->val = 0;
|
||||
}
|
||||
cur_axis_mag[axis] = abs_val;
|
||||
}
|
||||
inp->val = (int)((float)inp->val * cfg.sensitivity * axis_sens);
|
||||
|
||||
dev_ev = device_event_in_use(dev);
|
||||
if(verbose && dev_ev == NULL)
|
||||
|
@ -147,11 +195,22 @@ void process_input(struct device *dev, struct dev_input *inp)
|
|||
}
|
||||
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->event.motion.data[axis] = sign * inp->val;
|
||||
dev_ev->pending = 1;
|
||||
break;
|
||||
|
||||
case INP_BUTTON:
|
||||
ev.type = EVENT_RAWBUTTON;
|
||||
ev.button.press = inp->val;
|
||||
ev.button.bnum = inp->idx;
|
||||
broadcast_event(&ev);
|
||||
|
||||
/* check to see if the button has been bound to an action */
|
||||
if(cfg.bnact[inp->idx] > 0) {
|
||||
handle_button_action(cfg.bnact[inp->idx], inp->val);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef USE_X11
|
||||
/* check to see if we must emulate a keyboard event instead of a
|
||||
* retular button event for this button
|
||||
|
@ -206,6 +265,41 @@ void process_input(struct device *dev, struct dev_input *inp)
|
|||
}
|
||||
}
|
||||
|
||||
static void handle_button_action(int act, int pressed)
|
||||
{
|
||||
if(pressed) return; /* perform all actions on release */
|
||||
|
||||
switch(act) {
|
||||
case BNACT_SENS_INC:
|
||||
cfg.sensitivity *= 1.1f;
|
||||
broadcast_cfg_event(REQ_GCFG_SENS, *(int*)&cfg.sensitivity);
|
||||
break;
|
||||
case BNACT_SENS_DEC:
|
||||
cfg.sensitivity *= 0.9f;
|
||||
broadcast_cfg_event(REQ_GCFG_SENS, *(int*)&cfg.sensitivity);
|
||||
break;
|
||||
case BNACT_SENS_RESET:
|
||||
cfg.sensitivity = 1.0f;
|
||||
broadcast_cfg_event(REQ_GCFG_SENS, *(int*)&cfg.sensitivity);
|
||||
break;
|
||||
case BNACT_DISABLE_ROTATION:
|
||||
disable_rotation = !disable_rotation;
|
||||
if(disable_rotation) {
|
||||
disable_translation = 0;
|
||||
}
|
||||
break;
|
||||
case BNACT_DISABLE_TRANSLATION:
|
||||
disable_translation = !disable_translation;
|
||||
if(disable_translation) {
|
||||
disable_rotation = 0;
|
||||
}
|
||||
break;
|
||||
case BNACT_DOMINANT_AXIS:
|
||||
dom_axis_mode = !dom_axis_mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int in_deadzone(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
@ -230,7 +324,7 @@ void repeat_last_event(struct device *dev)
|
|||
static void dispatch_event(struct dev_event *dev_ev)
|
||||
{
|
||||
struct client *c, *client_iter;
|
||||
int dev_idx;
|
||||
struct device *client_dev;
|
||||
|
||||
if(dev_ev->event.type == EVENT_MOTION) {
|
||||
struct timeval tv;
|
||||
|
@ -240,16 +334,43 @@ static void dispatch_event(struct dev_event *dev_ev)
|
|||
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 == */
|
||||
|
||||
/* if the client has selected a single device to get input from, then
|
||||
* don't send the event if it originates from a different device
|
||||
*/
|
||||
client_dev = get_client_device(c);
|
||||
if(!client_dev || client_dev == dev_ev->dev) {
|
||||
send_event(&dev_ev->event, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void broadcast_event(spnav_event *ev)
|
||||
{
|
||||
struct client *c;
|
||||
|
||||
c = first_client();
|
||||
while(c) {
|
||||
/* event masks will be checked at the protocol level (send_uevent) */
|
||||
send_event(ev, c);
|
||||
c = c->next;
|
||||
}
|
||||
}
|
||||
|
||||
void broadcast_cfg_event(int cfg, int val)
|
||||
{
|
||||
spnav_event ev = {0};
|
||||
|
||||
ev.type = EVENT_CFG;
|
||||
ev.cfg.cfg = cfg;
|
||||
ev.cfg.data[0] = val;
|
||||
broadcast_event(&ev);
|
||||
}
|
||||
|
||||
static void send_event(spnav_event *ev, struct client *c)
|
||||
{
|
||||
switch(get_client_type(c)) {
|
||||
|
|
41
src/event.h
41
src/event.h
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -25,9 +25,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
enum {
|
||||
EVENT_MOTION,
|
||||
EVENT_BUTTON /* includes both press and release */
|
||||
EVENT_BUTTON, /* includes both press and release */
|
||||
|
||||
/* protocol v1 events */
|
||||
EVENT_DEV, /* device change */
|
||||
EVENT_CFG, /* configuration change */
|
||||
|
||||
EVENT_RAWAXIS,
|
||||
EVENT_RAWBUTTON
|
||||
};
|
||||
|
||||
enum { DEV_ADD, DEV_RM };
|
||||
|
||||
struct event_motion {
|
||||
int type;
|
||||
int x, y, z;
|
||||
|
@ -42,10 +51,33 @@ struct event_button {
|
|||
int bnum;
|
||||
};
|
||||
|
||||
struct event_dev {
|
||||
int type;
|
||||
int op;
|
||||
int id;
|
||||
int devtype;
|
||||
int usbid[2];
|
||||
};
|
||||
|
||||
struct event_cfg {
|
||||
int type;
|
||||
int cfg;
|
||||
int data[6];
|
||||
};
|
||||
|
||||
struct event_axis {
|
||||
int type;
|
||||
int idx;
|
||||
int value;
|
||||
};
|
||||
|
||||
typedef union spnav_event {
|
||||
int type;
|
||||
struct event_motion motion;
|
||||
struct event_button button;
|
||||
struct event_dev dev;
|
||||
struct event_cfg cfg;
|
||||
struct event_axis axis;
|
||||
} spnav_event;
|
||||
|
||||
enum {
|
||||
|
@ -56,7 +88,6 @@ enum {
|
|||
|
||||
struct dev_input {
|
||||
int type;
|
||||
struct timeval tm;
|
||||
int idx;
|
||||
int val;
|
||||
};
|
||||
|
@ -71,5 +102,9 @@ int in_deadzone(struct device *dev);
|
|||
/* dispatches the last event */
|
||||
void repeat_last_event(struct device *dev);
|
||||
|
||||
/* broadcasts an event to all clients */
|
||||
void broadcast_event(spnav_event *ev);
|
||||
|
||||
void broadcast_cfg_event(int cfg, int val);
|
||||
|
||||
#endif /* EVENT_H_ */
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
/*
|
||||
serialmagellan - decoding serial magellan spaceball data.
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -16,13 +15,27 @@ 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
#ifndef SERIAL_CONSTANTS_H_
|
||||
#define SERIAL_CONSTANTS_H_
|
||||
int init_hotplug(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define MAXPACKETSIZE 16
|
||||
#define VERSION_STRING_MAX 512
|
||||
#define DEVICE_NAME_MAX 64
|
||||
#define MAXREADSIZE 512
|
||||
void shutdown_hotplug(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
int get_hotplug_fd(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int handle_hotplug(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#else
|
||||
int spacenavd_hotplug_freebsd_shut_up_empty_source_warning;
|
||||
#endif /* __FreeBSD */
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef USE_NETLINK
|
||||
#include <sys/types.h>
|
||||
|
@ -36,10 +37,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "cfgfile.h"
|
||||
|
||||
static int con_hotplug(void);
|
||||
static void delay_timeout(int sig);
|
||||
static void poll_timeout(int sig);
|
||||
|
||||
static int hotplug_fd = -1;
|
||||
static int poll_time, poll_pipe;
|
||||
static int poll_time, poll_pipe = -1;
|
||||
static int delay_pending, delay_pipe[2] = {-1, -1};
|
||||
|
||||
int init_hotplug(void)
|
||||
{
|
||||
|
@ -65,6 +68,11 @@ int init_hotplug(void)
|
|||
poll_time = 1;
|
||||
signal(SIGALRM, poll_timeout);
|
||||
alarm(poll_time);
|
||||
} else {
|
||||
if(pipe(delay_pipe) == -1) {
|
||||
logmsg(LOG_ERR, "failed to open hotplug delay self-pipe: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return hotplug_fd;
|
||||
|
@ -81,39 +89,63 @@ void shutdown_hotplug(void)
|
|||
close(poll_pipe);
|
||||
poll_pipe = -1;
|
||||
}
|
||||
|
||||
if(delay_pipe[0] != -1) {
|
||||
close(delay_pipe[0]);
|
||||
close(delay_pipe[1]);
|
||||
delay_pipe[0] = delay_pipe[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int get_hotplug_fd(void)
|
||||
{
|
||||
return hotplug_fd;
|
||||
return delay_pending ? delay_pipe[0] : hotplug_fd;
|
||||
}
|
||||
|
||||
int handle_hotplug(void)
|
||||
{
|
||||
char buf[512];
|
||||
read(hotplug_fd, buf, sizeof buf);
|
||||
char buf[64];
|
||||
|
||||
if(verbose) {
|
||||
logmsg(LOG_DEBUG, "\nhandle_hotplug called\n");
|
||||
if(poll_pipe != -1 || delay_pending) {
|
||||
delay_pending = 0;
|
||||
|
||||
read(delay_pipe[0], buf, sizeof buf);
|
||||
|
||||
if(verbose > 1) {
|
||||
logmsg(LOG_DEBUG, "handle_hotplug: init_devices_usb\n");
|
||||
}
|
||||
|
||||
if(init_devices_usb() == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(init_devices() == -1) {
|
||||
return -1;
|
||||
while(read(hotplug_fd, buf, sizeof buf) > 0);
|
||||
|
||||
if(verbose > 1) {
|
||||
logmsg(LOG_DEBUG, "handle_hotplug: schedule delayed activation in 1 sec\n");
|
||||
}
|
||||
|
||||
/* schedule a delayed trigger to avoid multiple hotplug activations in a row */
|
||||
delay_pending = 1;
|
||||
signal(SIGALRM, delay_timeout);
|
||||
alarm(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int con_hotplug(void)
|
||||
{
|
||||
int s = -1;
|
||||
|
||||
#ifdef USE_NETLINK
|
||||
int s;
|
||||
struct sockaddr_nl addr;
|
||||
|
||||
if((s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)) == -1) {
|
||||
logmsg(LOG_ERR, "failed to open hotplug netlink socket: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.nl_family = AF_NETLINK;
|
||||
|
@ -125,9 +157,15 @@ static int con_hotplug(void)
|
|||
close(s);
|
||||
return -1;
|
||||
}
|
||||
#endif /* USE_NETLINK */
|
||||
|
||||
return s;
|
||||
#else
|
||||
return -1;
|
||||
#endif /* USE_NETLINK */
|
||||
}
|
||||
|
||||
static void delay_timeout(int sig)
|
||||
{
|
||||
write(delay_pipe[1], &sig, 1);
|
||||
}
|
||||
|
||||
static void poll_timeout(int sig)
|
||||
|
@ -145,4 +183,6 @@ static void poll_timeout(int sig)
|
|||
|
||||
|
||||
|
||||
#else
|
||||
int spacenavd_hotplug_linux_shut_up_empty_source_warning;
|
||||
#endif /* __linux__ */
|
||||
|
|
35
src/kbemu.c
35
src/kbemu.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2021 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -21,13 +21,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#ifdef USE_X11
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "logger.h"
|
||||
#include "kbemu.h"
|
||||
|
||||
#ifdef HAVE_XTEST_H
|
||||
#include <X11/extensions/XTest.h>
|
||||
static int use_xtest;
|
||||
#endif
|
||||
|
||||
static Display *dpy;
|
||||
|
||||
void kbemu_set_display(Display *d)
|
||||
{
|
||||
dpy = d;
|
||||
|
||||
if(d) {
|
||||
#ifdef HAVE_XTEST_H
|
||||
int tmp;
|
||||
use_xtest = XTestQueryExtension(dpy, &tmp, &tmp, &tmp, &tmp);
|
||||
|
||||
if(use_xtest)
|
||||
logmsg(LOG_DEBUG, "Using XTEST to send key events\n");
|
||||
else
|
||||
#endif
|
||||
logmsg(LOG_DEBUG, "Using XSendEvent to send key events\n");
|
||||
}
|
||||
}
|
||||
|
||||
KeySym kbemu_keysym(const char *str)
|
||||
|
@ -35,6 +53,11 @@ KeySym kbemu_keysym(const char *str)
|
|||
return XStringToKeysym(str);
|
||||
}
|
||||
|
||||
const char *kbemu_keyname(KeySym sym)
|
||||
{
|
||||
return XKeysymToString(sym);
|
||||
}
|
||||
|
||||
void send_kbevent(KeySym key, int press)
|
||||
{
|
||||
XEvent xevent;
|
||||
|
@ -43,6 +66,14 @@ void send_kbevent(KeySym key, int press)
|
|||
|
||||
if(!dpy) return;
|
||||
|
||||
#ifdef HAVE_XTEST_H
|
||||
if(use_xtest) {
|
||||
XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, key), press, 0);
|
||||
XFlush(dpy);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
XGetInputFocus(dpy, &win, &rev_state);
|
||||
|
||||
xevent.type = press ? KeyPress : KeyRelease;
|
||||
|
@ -59,4 +90,6 @@ void send_kbevent(KeySym key, int press)
|
|||
XSendEvent(dpy, win, True, press ? KeyPressMask : KeyReleaseMask, &xevent);
|
||||
XFlush(dpy);
|
||||
}
|
||||
#else
|
||||
int spacenavd_kbemu_shut_up_empty_source_warning;
|
||||
#endif /* USE_X11 */
|
||||
|
|
|
@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
void kbemu_set_display(Display *dpy);
|
||||
KeySym kbemu_keysym(const char *str);
|
||||
const char *kbemu_keyname(KeySym sym);
|
||||
|
||||
void send_kbevent(KeySym key, int press);
|
||||
|
||||
|
|
12
src/logger.c
12
src/logger.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2021 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -30,7 +30,7 @@ int start_logfile(const char *fname)
|
|||
return -1;
|
||||
}
|
||||
setvbuf(logfile, 0, _IONBF, 0);
|
||||
return 0;
|
||||
return fileno(logfile);
|
||||
}
|
||||
|
||||
int start_syslog(const char *id)
|
||||
|
@ -44,14 +44,12 @@ void logmsg(int prio, const char *fmt, ...)
|
|||
{
|
||||
va_list ap;
|
||||
|
||||
if(logfile) {
|
||||
va_start(ap, fmt);
|
||||
vfprintf(logfile, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
if(use_syslog) {
|
||||
va_start(ap, fmt);
|
||||
vsyslog(prio, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
vfprintf(logfile ? logfile : stdout, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
|
|
@ -1,423 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#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<input.rbuf_sz; i++) {
|
||||
if(input.rbuf[i] == '\n' || input.rbuf[i] == '\r') {
|
||||
input.packet_buf[input.packet_buf_pos] = 0; /* terminate string */
|
||||
|
||||
if(input.packet_buf[0] == 'd' && input.packet_buf_pos == 15) {
|
||||
proc_disp_packet();
|
||||
} else if(input.packet_buf[0] == 'k' && input.packet_buf_pos == 4) {
|
||||
proc_bn_k_packet();
|
||||
} else if(input.packet_buf[0] == 'c' && input.packet_buf_pos == 3) {
|
||||
proc_bn_c_packet();
|
||||
} else if(input.packet_buf[0] == 'n' && input.packet_buf_pos == 2) {
|
||||
proc_bn_n_packet();
|
||||
} else if(input.packet_buf[0] == 'q' && input.packet_buf_pos == 3) {
|
||||
proc_bn_q_packet();
|
||||
} else {
|
||||
fprintf(stderr, "unknown packet %s\n", input.packet_buf);
|
||||
}
|
||||
input.packet_buf_pos = 0;
|
||||
} else {
|
||||
input.packet_buf[input.packet_buf_pos] = input.rbuf[i];
|
||||
input.packet_buf_pos++;
|
||||
if(input.packet_buf_pos == MAXPACKETSIZE) {
|
||||
input.packet_buf_pos = 0;
|
||||
fprintf(stderr, "packet buffer overrun\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int open_smag(const char *devfile)
|
||||
{
|
||||
if((dev_fd = smag_open_device(devfile)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
smag_set_port_magellan(dev_fd);
|
||||
smag_init_device(dev_fd);
|
||||
clean_input();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int close_smag()
|
||||
{
|
||||
smag_write(dev_fd, "l000", 4);
|
||||
close(dev_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_smag(struct dev_input *inp)
|
||||
{
|
||||
/*need to return 1 if we fill in inp or 0 if no events */
|
||||
struct smag_event *ev;
|
||||
|
||||
input.rbuf_sz = smag_read(dev_fd, input.rbuf, MAXREADSIZE);
|
||||
if(input.rbuf_sz > 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;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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);
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#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<sz; i++) {
|
||||
write(fd, buf + i, 1);
|
||||
usleep(SMAG_DELAY_USEC);
|
||||
}
|
||||
write(fd, "\r", 1);
|
||||
usleep(LONG_DELAY);
|
||||
}
|
||||
|
||||
int smag_read(int fd, char *buf, int sz)
|
||||
{
|
||||
int bytesrd = read(fd, buf, sz - 1);
|
||||
if(bytesrd < 1) {
|
||||
return 0;
|
||||
}
|
||||
buf[bytesrd] = 0;
|
||||
return bytesrd;
|
||||
}
|
||||
|
||||
int smag_wait_read(int fd, char *buf, int sz, int wait_sec)
|
||||
{
|
||||
int res;
|
||||
fd_set set;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&set);
|
||||
FD_SET(fd, &set);
|
||||
|
||||
tv.tv_sec = wait_sec;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
do {
|
||||
res = select(fd + 1, &set, 0, 0, &tv);
|
||||
} while(res == -1 && errno == EINTR);
|
||||
|
||||
return res == -1 ? -1 : smag_read(fd, buf, sz);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#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;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef SMAG_DETECT_H_
|
||||
#define SMAG_DETECT_H_
|
||||
|
||||
int smag_detect(const char *fname, char *buf, int sz);
|
||||
|
||||
#endif /* SMAG_DETECT_H_ */
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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_ */
|
|
@ -0,0 +1,75 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#define DEF_PROTO_REQ_NAMES
|
||||
#include "proto.h"
|
||||
|
||||
|
||||
int spnav_send_str(int fd, int req, const char *str)
|
||||
{
|
||||
int len;
|
||||
struct reqresp rr = {0};
|
||||
|
||||
if(fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = str ? strlen(str) : 0;
|
||||
rr.type = req;
|
||||
rr.data[6] = len;
|
||||
|
||||
do {
|
||||
if(str) {
|
||||
memcpy(rr.data, str, len > REQSTR_CHUNK_SIZE ? REQSTR_CHUNK_SIZE : len);
|
||||
}
|
||||
write(fd, &rr, sizeof rr);
|
||||
str += REQSTR_CHUNK_SIZE;
|
||||
len -= REQSTR_CHUNK_SIZE;
|
||||
rr.data[6] = len | REQSTR_CONT_BIT;
|
||||
} while(len > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spnav_recv_str(struct reqresp_strbuf *sbuf, struct reqresp *rr)
|
||||
{
|
||||
int len;
|
||||
|
||||
if(rr->data[6] < 0) return -1;
|
||||
len = REQSTR_REMLEN(rr);
|
||||
|
||||
if(REQSTR_FIRST(rr)) {
|
||||
/* first packet, allocate buffer */
|
||||
free(sbuf->buf);
|
||||
sbuf->expect = len;
|
||||
sbuf->size = sbuf->expect + 1;
|
||||
if(!(sbuf->buf = malloc(sbuf->size))) {
|
||||
return -1;
|
||||
}
|
||||
sbuf->endp = sbuf->buf;
|
||||
}
|
||||
|
||||
if(!sbuf->size || !sbuf->buf || !sbuf->endp) {
|
||||
return -1;
|
||||
}
|
||||
if(sbuf->endp < sbuf->buf || sbuf->endp >= sbuf->buf + sbuf->size) {
|
||||
return -1;
|
||||
}
|
||||
if(sbuf->expect > sbuf->size) return -1;
|
||||
if(len != sbuf->expect) return -1;
|
||||
|
||||
if(len > REQSTR_CHUNK_SIZE) {
|
||||
len = REQSTR_CHUNK_SIZE;
|
||||
}
|
||||
memcpy(sbuf->endp, rr->data, len);
|
||||
sbuf->endp += len;
|
||||
sbuf->expect -= len;
|
||||
|
||||
if(sbuf->expect < 0) return -1;
|
||||
|
||||
if(!sbuf->expect) {
|
||||
*sbuf->endp = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
#ifndef PROTO_H_
|
||||
#define PROTO_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#elif defined(HAVE_INTTYPES_H)
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
/* maximum supported protocol version */
|
||||
#define MAX_PROTO_VER 1
|
||||
|
||||
enum {
|
||||
UEV_MOTION,
|
||||
UEV_PRESS,
|
||||
UEV_RELEASE,
|
||||
UEV_DEV,
|
||||
UEV_CFG,
|
||||
UEV_RAWAXIS,
|
||||
UEV_RAWBUTTON,
|
||||
|
||||
MAX_UEV
|
||||
};
|
||||
|
||||
struct reqresp {
|
||||
int32_t type;
|
||||
int32_t data[7];
|
||||
};
|
||||
|
||||
struct reqresp_strbuf {
|
||||
char *buf, *endp;
|
||||
int size;
|
||||
int expect;
|
||||
};
|
||||
|
||||
#define REQ_TAG 0x7faa0000
|
||||
#define REQ_BASE 0x1000
|
||||
|
||||
/* REQ_S* are set requests, REQ_G* are get requests.
|
||||
* Quick-reference for request-response data in the comments next to each
|
||||
* request: Q[n] defines request data item n, R[n] defines response data item n
|
||||
*
|
||||
* status responses are 0 for success, non-zero for failure
|
||||
*
|
||||
* "remaining length" fields in string transfers have 16 valid bits. Bit 16 is
|
||||
* used as a flag: 0 for the first packet, 1 for continuation packets.
|
||||
*/
|
||||
enum {
|
||||
/* per-client settings */
|
||||
REQ_SET_NAME = REQ_BASE,/* set client name: Q[0-5] next 24 bytes Q[6] remaining length - R[6] status */
|
||||
REQ_SET_SENS, /* set client sensitivity: Q[0] float - R[6] status */
|
||||
REQ_GET_SENS, /* get client sensitivity: R[0] float R[6] status */
|
||||
REQ_SET_EVMASK, /* set event mask: Q[0] mask - R[6] status */
|
||||
REQ_GET_EVMASK, /* get event mask: R[0] mask R[6] status */
|
||||
|
||||
/* device queries */
|
||||
REQ_DEV_NAME = 0x2000, /* get device name: R[0-5] next 24 bytes R[6] remaining length or -1 for failure */
|
||||
REQ_DEV_PATH, /* get device path: same as above */
|
||||
REQ_DEV_NAXES, /* get number of axes: R[0] num axes R[6] status */
|
||||
REQ_DEV_NBUTTONS, /* get number of buttons: same as above */
|
||||
REQ_DEV_USBID, /* get USB id: R[0] vend R[1] prod R[6] status */
|
||||
REQ_DEV_TYPE, /* get device type: R[0] type enum R[6] status */
|
||||
/* TODO: features like LCD, LEDs ... */
|
||||
|
||||
/* configuration settings */
|
||||
REQ_SCFG_SENS = 0x3000, /* set global sensitivity: Q[0] float - R[6] status */
|
||||
REQ_GCFG_SENS, /* get global sens: R[0] float R[6] status */
|
||||
REQ_SCFG_SENS_AXIS, /* set per-axis sens/ty: Q[0-5] values - R[6] status */
|
||||
REQ_GCFG_SENS_AXIS, /* get per-axis sens/ty: R[0-5] values R[6] status */
|
||||
REQ_SCFG_DEADZONE, /* set deadzones: Q[0] dev axis Q[1] deadzone - R[6] status */
|
||||
REQ_GCFG_DEADZONE, /* get deadzones: R[0] dev axis - R[0] dev axis R[1] deadzone R[6] status */
|
||||
REQ_SCFG_INVERT, /* set invert axes: Q[0-5] invert - R[6] status */
|
||||
REQ_GCFG_INVERT, /* get invert axes: R[0-5] invert R[6] status */
|
||||
REQ_SCFG_AXISMAP, /* set axis mapping: Q[0] dev axis Q[1] mapping - R[6] status */
|
||||
REQ_GCFG_AXISMAP, /* get axis mapping: Q[0] dev axis - R[0] dev axis R[1] mapping R[6] status */
|
||||
REQ_SCFG_BNMAP, /* set button mapping: Q[0] dev bidx Q[1] map bidx - R[6] status */
|
||||
REQ_GCFG_BNMAP, /* get button mapping: Q[0] dev bidx - R[0] dev bidx R[1] map bidx R[6] status */
|
||||
REQ_SCFG_BNACTION, /* set button action: Q[0] bidx Q[1] action - R[6] status */
|
||||
REQ_GCFG_BNACTION, /* get button action: Q[0] bidx - R[0] bidx R[1] action R[6] status */
|
||||
REQ_SCFG_KBMAP, /* set keyboard mapping: Q[0] bidx Q[1] keysym - R[6] status */
|
||||
REQ_GCFG_KBMAP, /* get keyboard mapping: Q[0] bidx - R[0] bidx R[1] keysym R[6] status */
|
||||
REQ_SCFG_SWAPYZ, /* set Y-Z axis swap: Q[0] swap - R[6] status */
|
||||
REQ_GCFG_SWAPYZ, /* get Y-Z axis swap: R[0] swap R[6] status */
|
||||
REQ_SCFG_LED, /* set LED state: Q[0] state - R[6] status */
|
||||
REQ_GCFG_LED, /* get LED state: R[0] state R[6] status */
|
||||
REQ_SCFG_GRAB, /* set device grabbing: Q[0] state - R[6] status */
|
||||
REQ_GCFG_GRAB, /* get device grabbing: R[0] state R[6] status */
|
||||
REQ_SCFG_SERDEV, /* set serial device path: Q[0-5] next 24 bytes Q[6] remaining length - R[6] status */
|
||||
REQ_GCFG_SERDEV, /* get serial device path: R[0-5] next 24 bytes R[6] remaining length or -1 for failure */
|
||||
REQ_SCFG_REPEAT, /* set repeat interval: Q[0] interval (msec) - R[6] status */
|
||||
REQ_GCFG_REPEAT, /* get repeat interval: R[0] interval (msec) R[6] status */
|
||||
/* TODO ... more */
|
||||
REQ_CFG_SAVE = 0x3ffe, /* save config file: R[6] status */
|
||||
REQ_CFG_RESTORE, /* load config from file: R[6] status */
|
||||
REQ_CFG_RESET, /* reset to default config: R[6] status */
|
||||
|
||||
REQ_CHANGE_PROTO = 0x5500
|
||||
};
|
||||
|
||||
/* XXX keep in sync with SPNAV_DEV_* in spnav.h (libspnav) */
|
||||
enum {
|
||||
DEV_UNKNOWN,
|
||||
/* serial devices */
|
||||
DEV_SB2003 = 0x100, /* Spaceball 1003/2003/2003C */
|
||||
DEV_SB3003, /* Spaceball 3003/3003C */
|
||||
DEV_SB4000, /* Spaceball 4000FLX/5000FLX */
|
||||
DEV_SM, /* Magellan SpaceMouse */
|
||||
DEV_SM5000, /* Spaceball 5000 (spacemouse protocol) */
|
||||
DEV_SMCADMAN, /* 3Dconnexion CadMan (spacemouse protocol) */
|
||||
/* USB devices */
|
||||
DEV_PLUSXT = 0x200, /* SpaceMouse Plus XT */
|
||||
DEV_CADMAN, /* 3Dconnexion CadMan (USB version) */
|
||||
DEV_SMCLASSIC, /* SpaceMouse Classic */
|
||||
DEV_SB5000, /* Spaceball 5000 (USB version) */
|
||||
DEV_STRAVEL, /* Space Traveller */
|
||||
DEV_SPILOT, /* Space Pilot */
|
||||
DEV_SNAV, /* Space Navigator */
|
||||
DEV_SEXP, /* Space Explorer */
|
||||
DEV_SNAVNB, /* Space Navigator for Notebooks */
|
||||
DEV_SPILOTPRO, /* Space Pilot pro */
|
||||
DEV_SMPRO, /* SpaceMouse Pro */
|
||||
DEV_NULOOQ, /* Nulooq */
|
||||
DEV_SMW, /* SpaceMouse Wireless */
|
||||
DEV_SMPROW, /* SpaceMouse Pro Wireless */
|
||||
DEV_SMENT, /* SpaceMouse Enterprise */
|
||||
DEV_SMCOMP, /* SpaceMouse Compact */
|
||||
DEV_SMMOD /* SpaceMouse Module */
|
||||
};
|
||||
|
||||
|
||||
#define REQSTR_CHUNK_SIZE 24
|
||||
#define REQSTR_CONT_BIT 0x10000
|
||||
#define REQSTR_FIRST(rr) (((rr)->data[6] & REQSTR_CONT_BIT) == 0)
|
||||
#define REQSTR_REMLEN(rr) ((rr)->data[6] & 0xffff)
|
||||
|
||||
int spnav_send_str(int fd, int req, const char *str);
|
||||
int spnav_recv_str(struct reqresp_strbuf *sbuf, struct reqresp *rr);
|
||||
|
||||
#ifdef DEF_PROTO_REQ_NAMES
|
||||
const char *spnav_reqnames_1000[] = {
|
||||
"SET_NAME",
|
||||
"SET_SENS",
|
||||
"GET_SENS",
|
||||
"SET_EVMASK",
|
||||
"GET_EVMASK"
|
||||
};
|
||||
const char *spnav_reqnames_2000[] = {
|
||||
"DEV_NAME",
|
||||
"DEV_PATH",
|
||||
"DEV_NAXES",
|
||||
"DEV_NBUTTONS",
|
||||
"DEV_USBID",
|
||||
"DEV_TYPE"
|
||||
};
|
||||
const char *spnav_reqnames_3000[] = {
|
||||
"SCFG_SENS",
|
||||
"GCFG_SENS",
|
||||
"SCFG_SENS_AXIS",
|
||||
"GCFG_SENS_AXIS",
|
||||
"SCFG_DEADZONE",
|
||||
"GCFG_DEADZONE",
|
||||
"SCFG_INVERT",
|
||||
"GCFG_INVERT",
|
||||
"SCFG_AXISMAP",
|
||||
"GCFG_AXISMAP",
|
||||
"SCFG_BNMAP",
|
||||
"GCFG_BNMAP",
|
||||
"SCFG_BNACTION",
|
||||
"GCFG_BNACTION",
|
||||
"SCFG_KBMAP",
|
||||
"GCFG_KBMAP",
|
||||
"SCFG_SWAPYZ",
|
||||
"GCFG_SWAPYZ",
|
||||
"SCFG_LED",
|
||||
"GCFG_LED",
|
||||
"SCFG_GRAB",
|
||||
"GCFG_GRAB",
|
||||
"SCFG_SERDEV",
|
||||
"GCFG_SERDEV",
|
||||
"SCFG_REPEAT",
|
||||
"GCFG_REPEAT"
|
||||
};
|
||||
|
||||
const int spnav_reqnames_1000_size = sizeof spnav_reqnames_1000 / sizeof *spnav_reqnames_1000;
|
||||
const int spnav_reqnames_2000_size = sizeof spnav_reqnames_2000 / sizeof *spnav_reqnames_2000;
|
||||
const int spnav_reqnames_3000_size = sizeof spnav_reqnames_3000 / sizeof *spnav_reqnames_3000;
|
||||
#else
|
||||
extern const char *spnav_reqnames_1000[];
|
||||
extern const char *spnav_reqnames_2000[];
|
||||
extern const char *spnav_reqnames_3000[];
|
||||
extern const int spnav_reqnames_1000_size;
|
||||
extern const int spnav_reqnames_2000_size;
|
||||
extern const int spnav_reqnames_3000_size;
|
||||
#endif /* DEF_PROTO_REQ_NAMES */
|
||||
|
||||
#endif /* PROTO_H_ */
|
549
src/proto_unix.c
549
src/proto_unix.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2022 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -20,22 +20,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include "proto.h"
|
||||
#include "proto_unix.h"
|
||||
#include "spnavd.h"
|
||||
#ifdef USE_X11
|
||||
#include "kbemu.h"
|
||||
#endif
|
||||
|
||||
enum {
|
||||
UEV_TYPE_MOTION,
|
||||
UEV_TYPE_PRESS,
|
||||
UEV_TYPE_RELEASE
|
||||
};
|
||||
#ifndef isfinite
|
||||
#define isfinite(x) (!isnan(x))
|
||||
#endif
|
||||
|
||||
static int lsock = -1;
|
||||
|
||||
|
||||
static int handle_request(struct client *c, struct reqresp *req);
|
||||
static const char *reqstr(int req);
|
||||
|
||||
int init_unix(void)
|
||||
{
|
||||
int s;
|
||||
|
@ -93,30 +101,71 @@ int get_unix_socket(void)
|
|||
|
||||
void send_uevent(spnav_event *ev, struct client *c)
|
||||
{
|
||||
int i, data[8] = {0};
|
||||
int i;
|
||||
int32_t data[8] = {0};
|
||||
float motion_mul;
|
||||
|
||||
if(lsock == -1) return;
|
||||
|
||||
switch(ev->type) {
|
||||
case EVENT_MOTION:
|
||||
data[0] = UEV_TYPE_MOTION;
|
||||
if(!(c->evmask & EVMASK_MOTION)) return;
|
||||
|
||||
data[0] = UEV_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[i + 1] = (int32_t)val;
|
||||
}
|
||||
data[7] = ev->motion.period;
|
||||
break;
|
||||
|
||||
case EVENT_RAWAXIS:
|
||||
if(!(c->evmask & EVMASK_RAWAXIS)) return;
|
||||
|
||||
data[0] = UEV_RAWAXIS;
|
||||
data[1] = ev->axis.idx;
|
||||
data[2] = ev->axis.value;
|
||||
break;
|
||||
|
||||
case EVENT_BUTTON:
|
||||
data[0] = ev->button.press ? UEV_TYPE_PRESS : UEV_TYPE_RELEASE;
|
||||
if(!(c->evmask & EVMASK_BUTTON)) return;
|
||||
|
||||
data[0] = ev->button.press ? UEV_PRESS : UEV_RELEASE;
|
||||
data[1] = ev->button.bnum;
|
||||
data[2] = ev->button.press;
|
||||
break;
|
||||
|
||||
case EVENT_RAWBUTTON:
|
||||
if(!(c->evmask & EVMASK_RAWBUTTON)) return;
|
||||
|
||||
data[0] = UEV_RAWBUTTON;
|
||||
data[1] = ev->button.bnum;
|
||||
data[2] = ev->button.press;
|
||||
break;
|
||||
|
||||
case EVENT_DEV:
|
||||
if(!(c->evmask & EVMASK_DEV)) return;
|
||||
|
||||
data[0] = UEV_DEV;
|
||||
data[1] = ev->dev.op;
|
||||
data[2] = ev->dev.id;
|
||||
data[3] = ev->dev.devtype;
|
||||
data[4] = ev->dev.usbid[0];
|
||||
data[5] = ev->dev.usbid[1];
|
||||
break;
|
||||
|
||||
case EVENT_CFG:
|
||||
if(!(c->evmask & EVMASK_CFG)) return;
|
||||
|
||||
data[0] = UEV_CFG;
|
||||
data[1] = ev->cfg.cfg;
|
||||
memcpy(data + 2, ev->cfg.data, sizeof ev->cfg.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
while(write(get_client_socket(c), data, sizeof data) == -1 && errno == EINTR);
|
||||
|
@ -125,6 +174,7 @@ void send_uevent(spnav_event *ev, struct client *c)
|
|||
int handle_uevents(fd_set *rset)
|
||||
{
|
||||
struct client *citer;
|
||||
struct reqresp *req;
|
||||
|
||||
if(lsock == -1) {
|
||||
return -1;
|
||||
|
@ -137,6 +187,9 @@ int handle_uevents(fd_set *rset)
|
|||
if((s = accept(lsock, 0, 0)) == -1) {
|
||||
logmsg(LOG_ERR, "error while accepting connection on the UNIX socket: %s\n", strerror(errno));
|
||||
} else {
|
||||
/* set socket as non-blocking and add client to the list */
|
||||
fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
if(!add_client(CLIENT_UNIX, &s)) {
|
||||
logmsg(LOG_ERR, "failed to add client: %s\n", strerror(errno));
|
||||
}
|
||||
|
@ -154,22 +207,478 @@ int handle_uevents(fd_set *rset)
|
|||
|
||||
if(FD_ISSET(s, rset)) {
|
||||
int rdbytes;
|
||||
int32_t msg;
|
||||
float sens;
|
||||
|
||||
/* got a request from a client, decode and execute it */
|
||||
/* XXX currently only sensitivity comes from clients */
|
||||
/* handle client requests */
|
||||
switch(c->proto) {
|
||||
case 0:
|
||||
while((rdbytes = read(s, &msg, sizeof msg)) < 0 && errno == EINTR);
|
||||
if(rdbytes <= 0) { /* something went wrong... disconnect client */
|
||||
close(get_client_socket(c));
|
||||
remove_client(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
/* handle magic NaN protocol change requests */
|
||||
if((msg & 0xffffff00) == (REQ_TAG | REQ_CHANGE_PROTO)) {
|
||||
c->proto = msg & 0xff;
|
||||
|
||||
/* if the client requests a protocol version higher than the
|
||||
* daemon supports, return the maximum supported version and
|
||||
* switch to that.
|
||||
*/
|
||||
if(c->proto > MAX_PROTO_VER) {
|
||||
c->proto = MAX_PROTO_VER;
|
||||
msg = REQ_TAG | REQ_CHANGE_PROTO | MAX_PROTO_VER;
|
||||
}
|
||||
write(s, &msg, sizeof msg);
|
||||
|
||||
if(c->proto > 0) {
|
||||
/* set default event mask for proto-v1 clients */
|
||||
c->evmask = EVMASK_MOTION | EVMASK_BUTTON | EVMASK_DEV;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* protocol v0: only sensitivity comes from clients */
|
||||
sens = *(float*)&msg;
|
||||
if(isfinite(sens)) {
|
||||
set_client_sensitivity(c, sens);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* protocol v1: accumulate request bytes, and process */
|
||||
while((rdbytes = read(s, c->reqbuf + c->reqbytes, sizeof *req - c->reqbytes)) < 0 && errno == EINTR);
|
||||
if(rdbytes <= 0) {
|
||||
close(s);
|
||||
remove_client(c);
|
||||
continue;
|
||||
}
|
||||
c->reqbytes += rdbytes;
|
||||
if(c->reqbytes >= sizeof *req) {
|
||||
req = (struct reqresp*)c->reqbuf;
|
||||
c->reqbytes = 0;
|
||||
if(handle_request(c, req) == -1) {
|
||||
close(s);
|
||||
remove_client(c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
set_client_sensitivity(c, sens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sendresp(struct client *c, struct reqresp *rr, int status)
|
||||
{
|
||||
rr->data[6] = status;
|
||||
return write(get_client_socket(c), rr, sizeof *rr);
|
||||
}
|
||||
|
||||
#define AXIS_VALID(x) ((x) >= 0 && (x) < MAX_AXES)
|
||||
#define BN_VALID(x) ((x) >= 0 && (x) < MAX_BUTTONS)
|
||||
#define BNACT_VALID(x) ((x) >= 0 && (x) < MAX_BNACT)
|
||||
|
||||
static int handle_request(struct client *c, struct reqresp *req)
|
||||
{
|
||||
int i, idx, res;
|
||||
float fval, fvec[6];
|
||||
struct device *dev;
|
||||
const char *str = 0;
|
||||
|
||||
logmsg(LOG_DEBUG, "request %s - %x %x %x %x %x %x\n", reqstr(req->type), req->data[0],
|
||||
req->data[1], req->data[2], req->data[3], req->data[4], req->data[5], req->data[6]);
|
||||
|
||||
switch(req->type & 0xffff) {
|
||||
case REQ_SET_NAME:
|
||||
if((res = spnav_recv_str(&c->strbuf, req)) == -1) {
|
||||
logmsg(LOG_ERR, "SET_NAME: failed to receive string\n");
|
||||
break;
|
||||
}
|
||||
if(res) {
|
||||
c->name = c->strbuf.buf;
|
||||
c->strbuf.buf = 0;
|
||||
logmsg(LOG_INFO, "client name: %s\n", c->name);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_SET_SENS:
|
||||
fval = *(float*)req->data;
|
||||
if(isfinite(fval)) {
|
||||
set_client_sensitivity(c, fval);
|
||||
sendresp(c, req, 0);
|
||||
} else {
|
||||
logmsg(LOG_WARNING, "client attempted to set invalid client sensitivity\n");
|
||||
sendresp(c, req, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_GET_SENS:
|
||||
fval = get_client_sensitivity(c);
|
||||
req->data[0] = *(int*)&fval;
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SET_EVMASK:
|
||||
c->evmask = req->data[0];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GET_EVMASK:
|
||||
req->data[0] = c->evmask;
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_DEV_NAME:
|
||||
if((dev = get_client_device(c))) {
|
||||
spnav_send_str(get_client_socket(c), req->type, dev->name);
|
||||
} else {
|
||||
sendresp(c, req, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_DEV_PATH:
|
||||
if((dev = get_client_device(c))) {
|
||||
spnav_send_str(get_client_socket(c), req->type, dev->path);
|
||||
} else {
|
||||
sendresp(c, req, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_DEV_NAXES:
|
||||
if((dev = get_client_device(c))) {
|
||||
req->data[0] = dev->num_axes;
|
||||
sendresp(c, req, 0);
|
||||
} else {
|
||||
sendresp(c, req, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_DEV_NBUTTONS:
|
||||
if((dev = get_client_device(c))) {
|
||||
req->data[0] = dev->num_buttons;
|
||||
sendresp(c, req, 0);
|
||||
} else {
|
||||
sendresp(c, req, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_DEV_USBID:
|
||||
if((dev = get_client_device(c)) && dev->usbid[0] && dev->usbid[1]) {
|
||||
req->data[0] = dev->usbid[0];
|
||||
req->data[1] = dev->usbid[1];
|
||||
sendresp(c, req, 0);
|
||||
} else {
|
||||
sendresp(c, req, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_DEV_TYPE:
|
||||
if((dev = get_client_device(c))) {
|
||||
req->data[0] = dev->type;
|
||||
sendresp(c, req, 0);
|
||||
} else {
|
||||
sendresp(c, req, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_SCFG_SENS:
|
||||
fval = *(float*)req->data;
|
||||
if(isfinite(fval)) {
|
||||
cfg.sensitivity = fval;
|
||||
sendresp(c, req, 0);
|
||||
} else {
|
||||
logmsg(LOG_WARNING, "client attempted to set invalid global sensitivity\n");
|
||||
sendresp(c, req, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_GCFG_SENS:
|
||||
req->data[0] = *(int*)&cfg.sensitivity;
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_SENS_AXIS:
|
||||
for(i=0; i<6; i++) {
|
||||
fvec[i] = ((float*)req->data)[i];
|
||||
if(!isfinite(fvec[i])) {
|
||||
logmsg(LOG_WARNING, "client attempted to set invalid axis %d sensitivity\n", i);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for(i=0; i<3; i++) {
|
||||
cfg.sens_trans[i] = fvec[i];
|
||||
cfg.sens_rot[i] = fvec[i + 3];
|
||||
}
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GCFG_SENS_AXIS:
|
||||
for(i=0; i<3; i++) {
|
||||
req->data[i] = *(int*)(cfg.sens_trans + i);
|
||||
req->data[i + 3] = *(int*)(cfg.sens_rot + i);
|
||||
}
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_DEADZONE:
|
||||
if(!AXIS_VALID(req->data[0])) {
|
||||
logmsg(LOG_WARNING, "client attempted to set invalid axis deadzone: %d\n", req->data[0]);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
cfg.dead_threshold[req->data[0]] = req->data[1];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GCFG_DEADZONE:
|
||||
if(!AXIS_VALID(req->data[0])) {
|
||||
logmsg(LOG_WARNING, "client requested invalid axis deadzone: %d\n", req->data[0]);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
req->data[1] = cfg.dead_threshold[req->data[0]];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_INVERT:
|
||||
for(i=0; i<6; i++) {
|
||||
cfg.invert[i] = req->data[i] ? 1 : 0;
|
||||
}
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GCFG_INVERT:
|
||||
memcpy(req->data, cfg.invert, 6 * sizeof(int));
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_AXISMAP:
|
||||
if(!AXIS_VALID(req->data[0]) || req->data[1] < -1 || req->data[1] >= 6) {
|
||||
logmsg(LOG_WARNING, "client attempted to set invalid axis mapping: %d -> %d\n",
|
||||
req->data[0], req->data[1]);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
cfg.map_axis[req->data[0]] = req->data[1];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GCFG_AXISMAP:
|
||||
if(!AXIS_VALID(req->data[0])) {
|
||||
logmsg(LOG_WARNING, "client queried mapping of invalid axis: %d\n",
|
||||
req->data[0]);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
req->data[1] = cfg.map_axis[req->data[0]];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_BNMAP:
|
||||
if(!BN_VALID(req->data[0]) || !BN_VALID(req->data[1])) {
|
||||
logmsg(LOG_WARNING, "client attempted to set invalid button mapping: %d -> %d\n",
|
||||
req->data[0], req->data[1]);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
cfg.map_button[req->data[0]] = req->data[1];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GCFG_BNMAP:
|
||||
if(!BN_VALID(req->data[0])) {
|
||||
logmsg(LOG_WARNING, "client queried mapping of invalid button: %d\n", req->data[0]);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
req->data[1] = cfg.map_button[req->data[0]];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_BNACTION:
|
||||
if(!BN_VALID(req->data[0]) || !BNACT_VALID(req->data[1])) {
|
||||
logmsg(LOG_WARNING, "client attempted to set invalid button action: %d -> %d\n",
|
||||
req->data[0], req->data[1]);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
cfg.bnact[req->data[0]] = req->data[1];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GCFG_BNACTION:
|
||||
if(!BN_VALID(req->data[0])) {
|
||||
logmsg(LOG_WARNING, "client queried action bound to invalid button: %d\n", req->data[0]);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
req->data[1] = cfg.bnact[req->data[0]];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_KBMAP:
|
||||
#ifdef USE_X11
|
||||
idx = req->data[0];
|
||||
if(!BN_VALID(idx) || (req->data[1] > 0 && !(str = kbemu_keyname(req->data[1])))) {
|
||||
logmsg(LOG_WARNING, "client attempted to set invalid key map: %d -> %x\n",
|
||||
idx, (unsigned int)req->data[1]);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
cfg.kbmap[idx] = req->data[1];
|
||||
free(cfg.kbmap_str[idx]);
|
||||
cfg.kbmap_str[idx] = req->data[1] > 0 ? strdup(str) : 0;
|
||||
sendresp(c, req, 0);
|
||||
#else
|
||||
logmsg(LOG_WARNING, "unable to set keyboard mappings, daemon compiled without X11 support\n");
|
||||
sendresp(c, req, -1);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case REQ_GCFG_KBMAP:
|
||||
#ifdef USE_X11
|
||||
idx = req->data[0];
|
||||
if(!BN_VALID(idx)) {
|
||||
logmsg(LOG_WARNING, "client queried keyboard mapping for invalid button: %d\n", idx);
|
||||
sendresp(c, req, -1);
|
||||
return 0;
|
||||
}
|
||||
if(cfg.kbmap_str[idx]) {
|
||||
if(!cfg.kbmap[idx]) {
|
||||
cfg.kbmap[idx] = kbemu_keysym(cfg.kbmap_str[idx]);
|
||||
}
|
||||
req->data[1] = cfg.kbmap[idx];
|
||||
} else {
|
||||
req->data[1] = 0;
|
||||
}
|
||||
sendresp(c, req, 0);
|
||||
#else
|
||||
logmsg(LOG_WARNING, "unable to query keyboard mappings, daemon compiled without X11 support\n");
|
||||
sendresp(c, req, -1);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case REQ_SCFG_SWAPYZ:
|
||||
cfg.swapyz = req->data[0] ? 1 : 0;
|
||||
sendresp(c, req, 0);
|
||||
|
||||
case REQ_GCFG_SWAPYZ:
|
||||
req->data[0] = cfg.swapyz;
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_LED:
|
||||
if(req->data[0] < 0 || req->data[0] >= 3) {
|
||||
sendresp(c, req, -1);
|
||||
break;
|
||||
}
|
||||
cfg.led = req->data[0];
|
||||
cfg_changed();
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GCFG_LED:
|
||||
req->data[0] = cfg.led;
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_GRAB:
|
||||
cfg.grab_device = req->data[0] ? 1 : 0;
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GCFG_GRAB:
|
||||
req->data[0] = cfg.grab_device;
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_SERDEV:
|
||||
if((res = spnav_recv_str(&c->strbuf, req)) == -1) {
|
||||
logmsg(LOG_ERR, "SCFG_SERDEV: failed to receive string\n");
|
||||
break;
|
||||
}
|
||||
if(res) {
|
||||
strncpy(cfg.serial_dev, c->strbuf.buf, sizeof cfg.serial_dev - 1);
|
||||
cfg.serial_dev[sizeof cfg.serial_dev - 1] = 0;
|
||||
cfg_changed();
|
||||
}
|
||||
break;
|
||||
|
||||
case REQ_GCFG_SERDEV:
|
||||
spnav_send_str(c->sock, req->type, cfg.serial_dev);
|
||||
break;
|
||||
|
||||
case REQ_SCFG_REPEAT:
|
||||
cfg.repeat_msec = req->data[0];
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_GCFG_REPEAT:
|
||||
req->data[0] = cfg.repeat_msec;
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_CFG_SAVE:
|
||||
sendresp(c, req, write_cfg(cfgfile, &cfg));
|
||||
break;
|
||||
|
||||
case REQ_CFG_RESTORE:
|
||||
if(read_cfg(cfgfile, &cfg) == -1) {
|
||||
logmsg(LOG_INFO, "config restore requested but failed to read %s, restoring defaults instead\n",
|
||||
cfgfile);
|
||||
default_cfg(&cfg);
|
||||
}
|
||||
cfg_changed();
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
case REQ_CFG_RESET:
|
||||
default_cfg(&cfg);
|
||||
cfg_changed();
|
||||
sendresp(c, req, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
logmsg(LOG_WARNING, "invalid client request: %s\n", reqstr(req->type));
|
||||
sendresp(c, req, -1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *reqstr(int req)
|
||||
{
|
||||
static char buf[8];
|
||||
|
||||
req &= 0xffff;
|
||||
|
||||
if(req >= 0x1000 && req < 0x1000 + spnav_reqnames_1000_size) {
|
||||
return spnav_reqnames_1000[req - 0x1000];
|
||||
}
|
||||
if(req >= 0x2000 && req < 0x2000 + spnav_reqnames_2000_size) {
|
||||
return spnav_reqnames_2000[req - 0x2000];
|
||||
}
|
||||
if(req >= 0x3000 && req < 0x3000 + spnav_reqnames_3000_size) {
|
||||
return spnav_reqnames_3000[req - 0x3000];
|
||||
}
|
||||
switch(req) {
|
||||
case REQ_CFG_SAVE:
|
||||
return "CFG_SAVE";
|
||||
case REQ_CFG_RESTORE:
|
||||
return "CFG_RESTORE";
|
||||
case REQ_CFG_RESET:
|
||||
return "CFG_RESET";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sprintf(buf, "0x%04x", req);
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,8 @@ 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;
|
||||
static Atom xa_event_motion, xa_event_bpress, xa_event_brelease;
|
||||
static Atom xa_event_devdisc, 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
|
||||
|
@ -137,6 +138,7 @@ int init_x11(void)
|
|||
xa_event_motion = XInternAtom(dpy, "MotionEvent", False);
|
||||
xa_event_bpress = XInternAtom(dpy, "ButtonPressEvent", False);
|
||||
xa_event_brelease = XInternAtom(dpy, "ButtonReleaseEvent", False);
|
||||
xa_event_devdisc = XInternAtom(dpy, "DeviceDisconnectEvent", False);
|
||||
xa_event_cmd = XInternAtom(dpy, "CommandEvent", False);
|
||||
|
||||
/* Create a dummy window, so that clients are able to send us events
|
||||
|
@ -257,7 +259,7 @@ void send_xevent(spnav_event *ev, struct client *c)
|
|||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
XSendEvent(dpy, get_client_window(c), False, 0, &xevent);
|
||||
|
@ -396,18 +398,19 @@ static int xerr(Display *dpy, XErrorEvent *err)
|
|||
{
|
||||
char buf[512];
|
||||
|
||||
if(verbose) {
|
||||
logmsg(LOG_ERR, "xerr(%p, %p)\n", (void*)dpy, (void*)err);
|
||||
}
|
||||
|
||||
if(err->error_code == BadWindow) {
|
||||
if(verbose) {
|
||||
logmsg(LOG_INFO, "Caught BadWindow, dropping client with window: %x\n",
|
||||
(unsigned int)err->resourceid);
|
||||
}
|
||||
/* 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);
|
||||
logmsg(LOG_ERR, "Caught unexpected X error: %s\n", buf);
|
||||
logmsg(LOG_ERR, "Caught unexpected X error: %s [op: %d,%d, res: %u]\n", buf,
|
||||
(int)err->request_code, (int)err->minor_code, (unsigned int)err->resourceid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,746 +0,0 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,177 +0,0 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#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 */
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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);
|
236
src/spnavd.c
236
src/spnavd.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2021 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -37,7 +37,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "proto_x11.h"
|
||||
#endif
|
||||
|
||||
static void print_usage(const char *argv0);
|
||||
static void cleanup(void);
|
||||
static void redir_log(int fallback_syslog);
|
||||
static void daemonize(void);
|
||||
static int write_pid_file(void);
|
||||
static int find_running_daemon(void);
|
||||
|
@ -45,63 +47,85 @@ static void handle_events(fd_set *rset);
|
|||
static void sig_handler(int s);
|
||||
static char *fix_path(char *str);
|
||||
|
||||
static char *cfgfile = DEF_CFGFILE;
|
||||
char *cfgfile = DEF_CFGFILE;
|
||||
static char *logfile = DEF_LOGFILE;
|
||||
static int pfd[2];
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i, pid, ret, become_daemon = 1;
|
||||
int force_logfile = 0;
|
||||
|
||||
for(i=1; i<argc; i++) {
|
||||
if(argv[i][0] == '-' && argv[i][2] == 0) {
|
||||
switch(argv[i][1]) {
|
||||
case 'd':
|
||||
become_daemon = !become_daemon;
|
||||
break;
|
||||
if(argv[i][0] == '-') {
|
||||
if(argv[i][1] == 'v') {
|
||||
char *p = argv[i] + 1;
|
||||
while(*p++) verbose++;
|
||||
|
||||
case 'c':
|
||||
if(!argv[++i]) {
|
||||
fprintf(stderr, "-c must be followed by the config file name\n");
|
||||
return 1;
|
||||
}
|
||||
cfgfile = fix_path(argv[i]);
|
||||
break;
|
||||
} else if(argv[i][2] == 0) {
|
||||
switch(argv[i][1]) {
|
||||
case 'd':
|
||||
become_daemon = !become_daemon;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
if(!argv[++i]) {
|
||||
fprintf(stderr, "-l must be followed by a logfile name or \"syslog\"\n");
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(argv[i], "syslog") == 0) {
|
||||
logfile = 0;
|
||||
} else {
|
||||
logfile = fix_path(argv[i]);
|
||||
if(strcmp(logfile, argv[i]) != 0) {
|
||||
printf("logfile: %s\n", logfile);
|
||||
case 'c':
|
||||
if(!argv[++i]) {
|
||||
fprintf(stderr, "-c must be followed by the config file name\n");
|
||||
return 1;
|
||||
}
|
||||
cfgfile = fix_path(argv[i]);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
if(!argv[++i]) {
|
||||
fprintf(stderr, "-l must be followed by a logfile name or \"syslog\"\n");
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(argv[i], "syslog") == 0) {
|
||||
logfile = 0;
|
||||
} else {
|
||||
logfile = fix_path(argv[i]);
|
||||
if(strcmp(logfile, argv[i]) != 0) {
|
||||
printf("logfile: %s\n", logfile);
|
||||
}
|
||||
/* when the user specifies a log file in the command line
|
||||
* the expectation is to use it, regardless of whether
|
||||
* spacenavd is started daemonized or not.
|
||||
*/
|
||||
force_logfile = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
printf("spacenavd " VERSION "\n");
|
||||
return 0;
|
||||
|
||||
case 'h':
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "invalid option: %s\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
printf("usage: %s [options]\n", argv[0]);
|
||||
printf("options:\n");
|
||||
printf(" -d: do not daemonize\n");
|
||||
printf(" -c <file>: config file path (default: " DEF_CFGFILE ")\n");
|
||||
printf(" -l <file>|syslog: log file path or log to syslog (default: " DEF_LOGFILE ")\n");
|
||||
printf(" -v: verbose output\n");
|
||||
printf(" -h: print usage information and exit\n");
|
||||
} else if(strcmp(argv[i], "-version") == 0) {
|
||||
printf("spacenavd " VERSION "\n");
|
||||
return 0;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "unrecognized argument: %s\n", argv[i]);
|
||||
} else if(strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "invalid option: %s\n\n", argv[i]);
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "unexpected argument: %s\n", argv[i]);
|
||||
fprintf(stderr, "unexpected argument: %s\n\n", argv[i]);
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -113,12 +137,18 @@ int main(int argc, char **argv)
|
|||
|
||||
if(become_daemon) {
|
||||
daemonize();
|
||||
} else {
|
||||
if(force_logfile) {
|
||||
redir_log(0);
|
||||
}
|
||||
}
|
||||
write_pid_file();
|
||||
|
||||
logmsg(LOG_INFO, "Spacenav daemon " VERSION "\n");
|
||||
|
||||
read_cfg(cfgfile, &cfg);
|
||||
prev_cfg = cfg;
|
||||
pipe(pfd);
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
|
@ -126,6 +156,7 @@ int main(int argc, char **argv)
|
|||
signal(SIGHUP, sig_handler);
|
||||
signal(SIGUSR1, sig_handler);
|
||||
signal(SIGUSR2, sig_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
init_devices();
|
||||
init_hotplug();
|
||||
|
@ -186,6 +217,10 @@ int main(int argc, char **argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* also the self-pipe read-end for safe SIGHUP handling */
|
||||
FD_SET(pfd[0], &rset);
|
||||
if(pfd[0] > max_fd) max_fd = fd;
|
||||
|
||||
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
|
||||
|
@ -224,6 +259,18 @@ int main(int argc, char **argv)
|
|||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
static void print_usage(const char *argv0)
|
||||
{
|
||||
printf("usage: %s [options]\n", argv0);
|
||||
printf("options:\n");
|
||||
printf(" -d: do not daemonize\n");
|
||||
printf(" -c <file>: config file path (default: " DEF_CFGFILE ")\n");
|
||||
printf(" -l <file>|syslog: log file path or log to syslog (default: " DEF_LOGFILE ")\n");
|
||||
printf(" -v: verbose output (use multiple times for greater effect)\n");
|
||||
printf(" -V,-version: print version number and exit\n");
|
||||
printf(" -h,-help: print usage information and exit\n");
|
||||
}
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
struct device *dev;
|
||||
|
@ -245,30 +292,45 @@ static void cleanup(void)
|
|||
remove(PIDFILE);
|
||||
}
|
||||
|
||||
static void daemonize(void)
|
||||
static void redir_log(int fallback_syslog)
|
||||
{
|
||||
int i, pid;
|
||||
int i, fd = -1;
|
||||
|
||||
chdir("/");
|
||||
|
||||
/* redirect standard input/output/error
|
||||
* best effort attempt to make either the logfile or the syslog socket
|
||||
* accessible through stdout/stderr, just in case any printfs survived
|
||||
* the logmsg conversion.
|
||||
*/
|
||||
for(i=0; i<3; i++) {
|
||||
close(i);
|
||||
if(logfile) {
|
||||
fd = start_logfile(logfile);
|
||||
}
|
||||
|
||||
open("/dev/zero", O_RDONLY);
|
||||
if(fd >= 0 || fallback_syslog) {
|
||||
/* redirect standard input/output/error
|
||||
* best effort attempt to make either the logfile or the syslog socket
|
||||
* accessible through stdout/stderr, just in case any printfs survived
|
||||
* the logmsg conversion.
|
||||
*/
|
||||
for(i=0; i<3; i++) {
|
||||
close(i);
|
||||
}
|
||||
|
||||
if(!logfile || start_logfile(logfile) == -1) {
|
||||
start_syslog(SYSLOG_ID);
|
||||
open("/dev/zero", O_RDONLY);
|
||||
|
||||
if(fd == -1) {
|
||||
fd = start_syslog(SYSLOG_ID);
|
||||
dup(1); /* not guaranteed to work */
|
||||
} else {
|
||||
dup(fd);
|
||||
}
|
||||
}
|
||||
dup(1);
|
||||
|
||||
setvbuf(stdout, 0, _IOLBF, 0);
|
||||
setvbuf(stderr, 0, _IONBF, 0);
|
||||
}
|
||||
|
||||
static void daemonize(void)
|
||||
{
|
||||
int pid;
|
||||
|
||||
chdir("/");
|
||||
|
||||
redir_log(1);
|
||||
|
||||
/* release controlling terminal */
|
||||
if((pid = fork()) == -1) {
|
||||
|
@ -334,6 +396,15 @@ static void handle_events(fd_set *rset)
|
|||
struct device *dev;
|
||||
struct dev_input inp;
|
||||
|
||||
/* handle signal pipe */
|
||||
if(FD_ISSET(pfd[0], rset)) {
|
||||
int tmp;
|
||||
read(pfd[0], &tmp, sizeof tmp); /* eat up the junk char */
|
||||
|
||||
read_cfg(cfgfile, &cfg);
|
||||
cfg_changed();
|
||||
}
|
||||
|
||||
/* handle anything coming through the UNIX socket */
|
||||
handle_uevents(rset);
|
||||
|
||||
|
@ -356,6 +427,9 @@ static void handle_events(fd_set *rset)
|
|||
/* ... and process it, possibly dispatching a spacenav event to clients */
|
||||
process_input(dev, &inp);
|
||||
}
|
||||
/* flush any pending events if we run out of input */
|
||||
inp.type = INP_FLUSH;
|
||||
process_input(dev, &inp);
|
||||
}
|
||||
dev = next;
|
||||
}
|
||||
|
@ -367,28 +441,48 @@ static void handle_events(fd_set *rset)
|
|||
}
|
||||
}
|
||||
|
||||
void cfg_changed(void)
|
||||
{
|
||||
if(cfg.led != prev_cfg.led) {
|
||||
struct device *dev = get_devices();
|
||||
while(dev) {
|
||||
if(is_device_valid(dev)) {
|
||||
if(verbose) {
|
||||
logmsg(LOG_INFO, "led %s, device: %s\n", cfg.led ? (cfg.led == LED_AUTO ? "auto" : "on"): "off", dev->name);
|
||||
}
|
||||
if(cfg.led == LED_ON || (cfg.led == LED_AUTO && first_client())) {
|
||||
set_device_led(dev, 1);
|
||||
} else {
|
||||
set_device_led(dev, 0);
|
||||
}
|
||||
}
|
||||
dev = dev->next;
|
||||
}
|
||||
}
|
||||
|
||||
if(strcmp(cfg.serial_dev, prev_cfg.serial_dev) != 0) {
|
||||
struct device *dev, *iter = get_devices();
|
||||
while(iter) {
|
||||
dev = iter;
|
||||
iter = iter->next;
|
||||
if(strcmp(dev->path, prev_cfg.serial_dev) == 0) {
|
||||
remove_device(dev);
|
||||
}
|
||||
}
|
||||
init_devices_serial();
|
||||
}
|
||||
|
||||
prev_cfg = cfg;
|
||||
}
|
||||
|
||||
/* 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(cfgfile, &cfg);
|
||||
if(cfg.led != prev_led) {
|
||||
struct device *dev = get_devices();
|
||||
while(dev) {
|
||||
if(is_device_valid(dev)) {
|
||||
if(verbose) {
|
||||
logmsg(LOG_INFO, "turn led %s, device: %s\n", cfg.led ? "on": "off", dev->name);
|
||||
}
|
||||
set_device_led(dev, cfg.led);
|
||||
}
|
||||
dev = dev->next;
|
||||
}
|
||||
}
|
||||
write(pfd[1], &s, 1); /* write *something* to the pipe to trigger a re-read */
|
||||
break;
|
||||
|
||||
case SIGSEGV:
|
||||
|
|
|
@ -23,7 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "cfgfile.h"
|
||||
#include "logger.h"
|
||||
|
||||
#define DEF_CFGFILE "/etc/spnavrc"
|
||||
#define DEF_CFGFILE CFGDIR "/spnavrc"
|
||||
#define DEF_LOGFILE "/var/log/spnavd.log"
|
||||
|
||||
#define SOCK_NAME "/var/run/spnav.sock"
|
||||
|
@ -49,7 +49,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
|
||||
|
||||
struct cfg cfg;
|
||||
struct cfg cfg, prev_cfg;
|
||||
extern char *cfgfile; /* defined in spnavd.c */
|
||||
|
||||
int verbose;
|
||||
|
||||
void cfg_changed(void);
|
||||
|
||||
#endif /* SPNAVD_H_ */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2023 John Tsiombikas <nuclear@member.fsf.org>
|
||||
|
||||
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
|
||||
|
@ -16,33 +16,29 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "smag_event.h"
|
||||
/* this must be the inverse of all the other xdetect_*.c ifdefs */
|
||||
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__APPLE__)
|
||||
#include <sys/select.h>
|
||||
#include "xdetect.h"
|
||||
|
||||
int evpool_size = 0;
|
||||
static struct smag_event *ev_free_list;
|
||||
|
||||
struct smag_event *alloc_event(void)
|
||||
int xdet_start(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;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void free_event(struct smag_event *ev)
|
||||
void xdet_stop(void)
|
||||
{
|
||||
if(evpool_size > 512) {
|
||||
free(ev);
|
||||
evpool_size--;
|
||||
} else {
|
||||
ev->next = ev_free_list;
|
||||
ev_free_list = ev;
|
||||
}
|
||||
}
|
||||
|
||||
int xdet_get_fd(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int handle_xdet_events(fd_set *rset)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
int spacenav_xdetect_none_shut_up_empty_source_warning;
|
||||
#endif
|
Ładowanie…
Reference in New Issue