Porównaj commity

...

150 Commity
v0.7 ... master

Autor SHA1 Wiadomość Data
John Tsiombikas d7e6f2c7ba Change the wireless device guess heuristic
The wireless device guess heuristic chooses between a spacemouse
wireless, or a spacemouse wireless pro. Since the spacemouse wireless
only has 2 buttons, it's best to choose pro if we get anything beyond
that.

See: https://github.com/FreeSpacenav/spnavcfg/issues/45
2024-07-12 18:55:53 +03:00
John Tsiombikas c6f8a6bf4a fix typo in conditional include stopping stdint.h from being included
fixes gitub issue #105
2024-06-16 20:10:26 +03:00
John Tsiombikas 9751030bfc implemented dominant axis mode properly. fixes github issue #84 2024-06-03 04:25:18 +03:00
John Tsiombikas ed8d255c27 linux evdev: Stop using the time field of input_event
Fixes github issue #104.

The time field is being removed from input_event (evdev) for 32bit
systems, to prepare for the year 2038 32bit timestamp rollover. This
breaks the build for spacenavd on 32bit linux systems.

Turns out that value was only ever used by the incorrect dominant axis
implementation which relied on timeouts (see github issue #84).
Therefore the easy fix for the missing time field is to just drop it
altogether and temporarilly disable the dominant axis code altogether
until it's fixed.
2024-06-02 05:37:54 +03:00
Rui Carmo 39856625a6 add ID for new Bluetooth variant of spacemouse wireless 2024-05-28 21:03:07 +03:00
John Tsiombikas 4ee37e2b75 update github actions 2024-05-22 20:22:16 +03:00
John Tsiombikas 251cf549e4 fixed configure mistake from the last commit: mis-typed search path 2024-05-22 20:10:17 +03:00
John Tsiombikas 92e7f9c67b improve X11 header/lib path search during build 2024-05-22 19:47:54 +03:00
Jaime van Kessel 3000236988 Add another version of keyboard pro to blacklist 2024-05-13 21:25:50 +03:00
John Tsiombikas 5d0a9639c4 enable extended buttons on magellan spacemice 2024-03-20 06:48:03 +02:00
John Tsiombikas 1d6fde6676 added swap/invyz flags to the CadMan USB and SB5000 USB
Resolves github issue #93
2024-02-16 15:37:03 +02:00
John Tsiombikas a2d121af16 update FreeBSD auto-build 2023-11-10 14:56:53 +02:00
John Tsiombikas 9b46aebfee added missing set/get requests for the repeat interval 2023-07-17 17:02:14 +03:00
John Tsiombikas 15e6848716 build on IRIX 2023-04-02 04:53:11 +03:00
Rickard Lind 220436cd32 Ignore Keyboard Pro/Numpad Pro (untested!)
Finishing off with adding Keyboard Pro and Numpad Pro for completeness.
Added from this list (which I think has got all handled IDs listed except c62c, c636, c640, and c641): https://3dconnexion.com/uk/support/faq/how-can-i-check-if-my-usb-3d-mouse-is-recognized-by-windows/
2023-03-25 12:56:53 +02:00
Rickard Lind 6df0c8d7d3 Ignore CadMouse Compact Wireless (untested!) 2023-03-23 16:47:57 +02:00
Rickard Lind 54e8333c6e Ignore CadMouse Pro* (untested!)
I have taken the liberty of adding the rest of the CadMouse Pro listed here: https://3dconnexion.com/cadmouse/
The USB IDs are untested and compiled from usb.ids and https://github.com/guillaumechauvat/cadmousectl
2023-03-23 16:43:28 +02:00
Rickard Lind 83d45dd3a5 Ignore CadMouse Compact 2023-03-22 23:21:57 +02:00
John Meacham edeb9bf3e2
Handle NULs sent when serial device powers up correctly. (#79)
The serial IBM spaceball 4000FLX may produce serial data before the @RESET\r
that contains nuls causing the strstr to end early.

---------

Co-authored-by: John Tsiombikas <nuclear@member.fsf.org>
2023-02-27 04:18:14 +02:00
John Tsiombikas d186c5d6ae minor dominant axis code fixes
- fixed scope of new variables to static.
  - fixed C99 struct initialization.
  - removed unnecessary timecmp/timediff and oneline threshold function.
2023-02-22 20:15:04 +02:00
Alberto Fanjul 3bcaf90519 Select dominant rotation/translation
Based on a movement treshold and allowing to reset after some inactivity
2023-02-22 20:15:04 +02:00
John Tsiombikas d537c9b002 fix button mapping for SpaceMouse Pro
I expected the SpaceMouse Pro to report 256 buttons like the SpaceMouse
Enterprise, and was too cautious in dropping the remapping otherwise. It
turns out it reports 15 buttons, but still produces disjointed button
numbers. So remove the check and just log the reported number of buttons
before remapping for future debugging.
2022-12-11 21:27:43 +02:00
John Tsiombikas 0469e67524 added FreeBSD automated github build 2022-11-30 02:56:51 +02:00
John Tsiombikas 1723bcb3f7 added automated github build status on the readme 2022-11-30 01:59:34 +02:00
John Tsiombikas a9c7cce212 actually disable X11 in no-x11 automated build jobs 2022-11-30 01:54:44 +02:00
John Tsiombikas 6640a155b0 added no-x11 build to macosx automated build script 2022-11-30 01:51:29 +02:00
John Tsiombikas 2e4909cd51 merged automated GNU/Linux build with and without X11 support into one 2022-11-30 01:34:50 +02:00
John Tsiombikas 6bc733eaae added MacOSX automated build file 2022-11-30 01:26:59 +02:00
John Tsiombikas 9b8d20e52d Adding automated github build files 2022-11-30 01:22:40 +02:00
John Tsiombikas aebe1999a6 FreeBSD USB: added fake hardcoded device parameters in lieu of actual info
The FreeBSD USB code is incomplete, and doesn't really to device
parameter detection yet, leaving the number of buttons and axes 0. This
might cause problems with programs expecting (reasonably) 6 axes, and
"some" buttons. So let's hardcode 6 and 2 respectively until the code is
improved.
2022-11-30 01:02:47 +02:00
John Tsiombikas d8f6de4697 Improved serial spaceball compatibility
Some times, or some devices do not seem to send a CR before the first
initialization line (@1), and would not get picked up by spacenavd,
which was matching "\r@1". Removing the CR from the test should not
introduce false positives, as I don't think magellan devices send any
similar strings, so it should be safe to just look for "@1".

Also some devices seem to take a little too long to initialize and miss
our initialization string. Adding a 1second sleep after opening the
device seems to help.

Thanks to Benjamin Bergman for debugging and reporting this.
2022-11-29 19:34:19 +02:00
John Tsiombikas b26464190f Fixed incorrect number of stop bits for serial magellan devices.
Closes issue #75
2022-11-29 18:53:23 +02:00
John Tsiombikas b748b4d980 added example spnavrc with SpaceMouse Enterprise button bindings 2022-08-26 22:54:36 +03:00
John Tsiombikas ea02aee368 fix crash when saving configuration, and the file did not already exist 2022-08-26 22:53:20 +03:00
John Tsiombikas 919a86f6b5 linux evdev: detect the button base instead of hardcoding 256
Some old USB devices report themselves as joysticks, which prompts linux
to start their button number from BN_JOYSTICK (0x120) instead of BN_MISC
(0x100). Spacenavd previously assumed the later and subtracted 256 from
button numbers, which causes an offset of 32 to all button events on the
Magellan SpaceMouse USB. This fix adds a bnbase field to the device
structure, which is detected and set automatically in the button
counting loop.
2022-08-26 17:16:59 +03:00
John Tsiombikas b6351158fa added example spnavrc with SpacePilot button mappings 2022-08-26 16:45:48 +03:00
John Tsiombikas 3e1cbc52ef Wireless device detection, and CadMouse avoidance logic
New 3dconnexion devices seem to all use the same dongle with USB id
256f:c526. That includes at least the SpaceMouse Wireless and the
CadMouse Wireless. This is a set of hacks to guess what's connected
and act accordingly:

  - Drop the CadMouse (num_axes == 0)
  - Enable the button hack if it's a SpaceMouse Pro Wireless
  - Detect SpaceMouse Wirless and update name/type.

Also added a test for the assumption that devices marked for button
remapping always report 255-256 buttons. If that's not the case undo the
button-hack and ask the user to report it as a bug.
2022-08-26 16:39:26 +03:00
John Tsiombikas 21d90d0db6 linux hotplug: delayed activation to avoid multiple hotplug triggers 2022-08-26 15:39:07 +03:00
John Tsiombikas 8a3c9617a5 Button remapping hack for devices with non-contiguous button numbers
Some devices are reporting arbitrary non-contiguous button numbers,
while spacenavd expects a contiguous range. Those devices also report
256 buttons regardless of the true number.

Introduced a button remapping hack for problematic devices, which
hardcode the correct number of buttons and arbitrarilly remaps them to a
continuous 0-base range. This should fix the button number issues with
the SpaceMouse Enterprise and SpaceMouse Pro.

Also added a second level of verbosity, to lighten the logging output
under normal `-v` conditions.
2022-08-26 15:06:38 +03:00
John Tsiombikas a067ab7a2d we have to link libm since we're using isfinite() 2022-08-22 03:57:38 +03:00
John Tsiombikas 2c5a443bf5 allow passing a null pointer to send_string, and send an empty string 2022-04-26 17:23:17 +03:00
John Tsiombikas 8383f8d493 fixed compatibility with newer magellan clients 2022-04-09 06:51:22 +03:00
John Tsiombikas 01634327c3 README: button names wiki call for help 2022-04-02 15:19:20 +03:00
luz paz 601817f03f README: fix typo 2022-04-01 21:57:55 +03:00
John Tsiombikas 223d170aef updated README 2022-04-01 05:19:11 +03:00
John Tsiombikas 8355311364 fixed config save routine: was missing bnact handling 2022-04-01 04:07:42 +03:00
John Tsiombikas 3a005aa21b now that the serial device path can be changed through the API by any
unpriviledged client, we can't trust it blindly any more. Added checks
to make sure it's a TTY before trying to use it.
2022-03-30 23:04:53 +03:00
John Tsiombikas c58eaa3bba better handling of config file updates 2022-03-28 19:05:24 +03:00
John Tsiombikas b19e9c5bb1 stop sending invalid X events 2022-03-27 18:56:24 +03:00
John Tsiombikas 9020967ddb - fix memory leak when re-reading config file
- better logging for X errors
2022-03-27 13:15:59 +03:00
John Tsiombikas 0f1f40f38e proto: better SCFG_KBMAP handling 2022-03-26 23:23:58 +02:00
John Tsiombikas b922cb75f8 - added raw axis/button events
- fixed dropping events when the client's event mask doesn't match
2022-03-26 15:06:03 +02:00
John Tsiombikas 3fb72ed7ef proto: send response after save/load commands complete 2022-03-26 13:19:47 +02:00
John Tsiombikas 945fba993b proto: allow sending empty strings 2022-03-25 12:18:57 +02:00
John Tsiombikas 60cb7f6f13 handle axis mappings consistently across all known devices, and drop
swap-by-default logic. Whether the low level axis mappings are
consistent for some of the devices are just guesswork at this point
until we get more data.
2022-03-25 11:53:10 +02:00
John Tsiombikas 743d5eb3f1 proto.h/proto.c files are shared with libspnav, add spnav_ prefixes to
all global symbols to avoid namespace pollution there.
2022-03-24 17:52:38 +02:00
John Tsiombikas a6e9f4b2ca improved string handling in the wire protocol 2022-03-23 22:16:53 +02:00
John Tsiombikas 34adbc5799 - made all protocol structures explicitly carry int32_t instead of int
- fixed serial_dev_open over-writing the device name with "serial device"
- hotplug does not need to call init_devices now that we split them up,
  it's best to call init_serial_devices instead.
- preparing for reworking the string passing mechanism in protocol v1
2022-03-23 11:42:08 +02:00
John Tsiombikas 88585ca7d1 - fixed axis/button reporting (AF_UNIX protocol) for serial devices.
- changed the meaning of swap-yz to make it more intuitive, I'll fix the
  USB devices handling which are backwards later at the device level.
- implemented SCFG_SERDEV/GCFG_SERDEV requests (AF_UNIX protocol).
2022-03-23 03:18:13 +02:00
John Tsiombikas 6aaf85f598 - fixed bug in AF_UNIX protocol request handling (both versions):
spacenavd would enter an infinite loop if at the time when a client
  disconnected, errno happened to be EINTR.
- allow axis mappings to be -1, to unmap a device axis.
2022-03-22 16:35:55 +02:00
John Tsiombikas 01e59bed7a - introduced explicit swap-yz config state, which acts separately to the
axis mapping. This way mappings won't swap around by themselves when
  swap is turned on or off.
- fixed automatic LED handling.
2022-03-22 12:29:36 +02:00
John Tsiombikas bea696f24f - client event mask (event type selection)
- added unique device ids (unique during spacenavd lifetime)
- send device change events
- added provision for configuration change events
2022-03-21 08:50:36 +02:00
John Tsiombikas 881feaed74 added reset request 2022-03-20 21:55:49 +02:00
John Tsiombikas 59b0be26f8 Change deadzone config to affect the device axes, not the output axes.
Makes much more sense like that.
2022-03-20 20:06:12 +02:00
John Tsiombikas b4dc387931 store and make available more information about the devices 2022-03-16 16:21:18 +02:00
John Tsiombikas cf397150a8 populate device name field for serial devices 2022-03-16 14:08:26 +02:00
John Tsiombikas 84c17ea404 written the new writecfg routine. untested until I also make something
that triggers it.
2022-03-13 14:42:22 +02:00
John Tsiombikas 3cfc60b80e preparing for the smarter config file writer 2022-03-12 10:09:37 +02:00
John Tsiombikas 8f062901b0 forgot to send status on REQ_CFG_RESTORE. also added logging if config
read fails and we fallback to restoring defaults.
2022-03-12 08:29:30 +02:00
John Tsiombikas 2d0db49d0f - implemented save/restore cfg protocol handling.
- made SIGHUP handling safer, by moving the cfg re-read into the select
  loop, and triggering it with a self-pipe write.
2022-03-12 08:20:08 +02:00
John Tsiombikas 4505d67bb7 fixed missing cfg protocol handlers (led, grab) 2022-03-01 01:34:12 +02:00
John Tsiombikas 04a883da90 fixed bug in new protocol handlers 2022-02-28 23:47:19 +02:00
John Tsiombikas 4ec6e2abcb more additions to the new protocol 2022-02-13 02:32:40 +02:00
John Tsiombikas 8f09c30e2d protocol v1: more cfg options 2022-02-12 19:12:22 +02:00
John Tsiombikas caaf31d759 handle client dropping out in protocol v1 2022-02-12 17:24:57 +02:00
John Tsiombikas 62cb5389c4 fixed protocol v1 handling bugs 2022-02-12 16:47:09 +02:00
John Tsiombikas 7145788c5e started working on the spacenavd protocol v1 2022-02-10 12:58:33 +02:00
John Tsiombikas 946a0874d2 removed the global disable-translation/disable-rotation options, and
moved the state variables out of the cfg structure.
2022-02-10 07:05:56 +02:00
John Tsiombikas 6e458f1676 Merge branch 'bnev_dis_motion' 2022-02-10 06:49:30 +02:00
Alberto Fanjul 4f7989d04b Actions for buttons to enable/disable rotation/translation 2022-02-09 20:43:24 +01:00
John Tsiombikas eb0287f6e8
Merge pull request #57 from luzpaz/typos
Fix trivial source comment typos
2022-02-06 05:00:19 +02:00
luz paz bf0ea1ecf9 Fix trivial source comment typos
Found via `codespell -q 3 -L mot`
2022-02-05 10:22:09 -05:00
John Tsiombikas ab4751c4ea
Merge pull request #55 from RealDeuce/fix-index-type
Use size_t when iterating gl_pathc
2022-01-29 20:56:35 +02:00
John Tsiombikas 7689859318
Merge pull request #54 from RealDeuce/unused-header
Remove unused header
2022-01-29 20:55:40 +02:00
Stephen Hurd 07d8a39075 Use size_t when iterating gl_pathc
Since gl_pathc is a size_t, the size and signedness is likely to
be different than int.
2022-01-29 13:43:03 -05:00
Stephen Hurd 816335c963 Remove unused header 2022-01-29 13:40:01 -05:00
John Tsiombikas e2c809ee36 configure: added --cfgdir= option to change the default config file path 2022-01-29 11:40:33 +02:00
John Tsiombikas 58bc16dd0c minor C89-compliance and code style fixes in dev_usb_freebsd.c 2022-01-29 08:41:49 +02:00
John Tsiombikas 9f0870ed51 Merge branch 'freebsdusb' 2022-01-29 08:21:46 +02:00
Stephen Hurd 9c5976d41c Merge branch 'master' into freebsd-port 2022-01-29 01:05:22 -05:00
Stephen Hurd 02a9806b9d Open uhid device non-blocking
The main loop is driven by the read function returning -1.  If we
always return 0, processing gets stuck.
2022-01-29 00:54:24 -05:00
John Tsiombikas d56c0e502c
Merge pull request #53 from RealDeuce/fix-no-x11-warning
Add warning suppression when --disable-x11 is used
2022-01-29 07:53:59 +02:00
John Tsiombikas 2a86295910
Merge pull request #52 from RealDeuce/silence_xtest_warning
Declare use_xtest in #ifdef HAVE_XTEST_H
2022-01-29 07:51:40 +02:00
Stephen Hurd 4cc34b78fe Return event for every message
Previously, translation and rotation events were only returned when
the axis value changed.  This causes issues when the space mouse is
held still in a movment position.

Now, every message received via USB is translated to events for the
daemon to handle.
2022-01-28 23:35:02 -05:00
Stephen Hurd 8d3ab7e514 Add warning suppression when --disable-x11 is used 2022-01-28 14:03:34 -05:00
Stephen Hurd d0469c5fb6 Add -I/usr/local/include to cpp flags
On FreeBSD, X11 headers are in /usr/local/include/X11, not
/usr/include/X11.  since -I/usr/local/include is added to CFLAGS
in Makefile.in, the test should also include that.

This allows FreeBSD to properly test XInput2 and XTest headers.
2022-01-28 13:56:34 -05:00
Stephen Hurd 8cdfa0c282 Fix some glaring style inconsistences. 2022-01-28 13:32:30 -05:00
Stephen Hurd 1b3d9770fe Fix warning silencing declaration name 2022-01-28 13:28:34 -05:00
Stephen Hurd 5f91c9c107 Declare use_xtest in #ifdef HAVE_XTEST_H
Prevents harmless unused variable warning.
2022-01-28 13:27:05 -05:00
Stephen Hurd f94c344a75 Silent warning for hotplug as well. 2022-01-28 13:26:24 -05:00
Stephen Hurd e4d1cdfd15 Change warning silencer hack to more appropriate name 2022-01-28 13:24:20 -05:00
Stephen Hurd 1b5cd4538a Add missing globfree() call 2022-01-28 13:23:30 -05:00
Stephen Hurd 794a6b8e1a Remove unused #defines 2022-01-28 13:19:26 -05:00
Stephen Hurd 4fd3dc3c13 Remove change missed during rebase 2022-01-28 13:18:17 -05:00
Stephen Hurd 9506c0e295 Address feedback 2022-01-28 13:16:26 -05:00
Stephen Hurd dafaf8c4c7 Handle battery charge level message 2022-01-28 13:09:59 -05:00
Stephen Hurd a5c51ded53 Default to /usr/local/etc/spnavrc for FreeBSD
/etc is for things that ship as part of the system.
2022-01-28 13:09:59 -05:00
Stephen Hurd 2ad357f13c Add support for devices that send all 6 axes in report 1 2022-01-28 13:09:59 -05:00
Stephen Hurd d90fa571ea Use AXES instead of MAX_AXES for hard-coded number of axes. 2022-01-28 13:09:59 -05:00
Stephen Hurd 8bb892b5a1 Initial FreeBSD port
Only tested with SP1 USB
Hard-codes a bunch of the HID stuff, ignoring the descriptor completely
2022-01-28 13:09:56 -05:00
John Tsiombikas 83b6124188 - take CC environment variable into account when building
- use cp and mkdir in the install rule instead of using the install
   utility
2022-01-28 19:34:43 +02:00
Stephen Hurd ccafde465a Ignore SIGPIPE
Fixes issue where spacenavd would terminate when Blender was closed.
2022-01-28 18:49:11 +02:00
John Tsiombikas c370884e65 configure script improvements:
- provide feedback for dependency checking
  - warn about building without xtest support
  - eliminate redundant check_header invocations for the same file

Closes github issue #50
2022-01-23 09:16:20 +02:00
John Tsiombikas 9f748d16fe Added mechanism to bind buttons to actions.
Currently implemented actions: sensitivity up/down/reset.
2021-09-23 23:38:22 +03:00
John Tsiombikas c5288167d1 - always respect the -l <logfile> argument regardless of whether we
start daemonized or not.
- converted a bunch of printfs in the recently added dev_serial.c to
  logmsg calls.
2021-07-15 13:53:58 +03:00
John Tsiombikas 4c503c2d1d Merge branch 'xtest' 2021-06-24 21:35:05 +03:00
John Tsiombikas 80e1eab670 XTEST improvements
- test for presence of the XTest.h header file in configure and
   conditionally compile XTEST support
 - check for availability of the XTEST extension when connecting to the
   X server, and fallback to the old XSendEvent code if it's not
   available.
 - fix formatting and code style issues with the original patch
2021-06-24 21:32:17 +03:00
Gaël Écorchard d79a2e36d0 Use XTestFakeKeyEvent to send keys
Using XTestFakeKeyEvent rather than XSendEvent makes Shift, Control and
Alt working. The Escape key was working also with XSendEvent. Other keys
were not tested.
2021-06-24 21:11:54 +03:00
John Tsiombikas c408cbeaa9 Merge branch 'startup_led_off_fix' 2021-05-23 00:46:49 +03:00
John Tsiombikas 6bd07cf0f6 added comments to clarify the toggling of the LED state on startup issue 2021-05-23 00:44:41 +03:00
John Tsiombikas 6972066793 Merge branch 'led_off' of git://github.com/gebner/spacenavd into startup_led_off_fix 2021-05-23 00:37:58 +03:00
John Tsiombikas 4060531a2b cleaned up the log output when detecting USB devices 2021-05-23 00:36:26 +03:00
Gabriel Ebner fbf9019470 Turn off LED on startup if LED_OFF is set. 2021-05-22 13:45:20 +02:00
John Tsiombikas fc6ba4e7a5
Merge pull request #38 from stephank/patch-1
Fix Darwin build
2021-05-18 18:53:15 +03:00
Stéphan Kochen d6a25d5c3f
Fix Darwin build 2021-05-18 14:03:11 +02:00
John Tsiombikas 7e14d4892e serial magellan: using the specified 2 stop bits seem to cause PL2303
USB-serial UARTs to misbehave. 1 stop bit seems to work fine, so we'll
go with that. Fixes github bug #35.
2021-05-04 18:25:11 +03:00
John Tsiombikas d3a3aa017a Fixed bug in configure script which failed to detect and set the version
string, when executed outside of the git working directory. Closes
github issue #29
2020-12-01 22:23:17 +02:00
John Tsiombikas 3b2eeb011f print usage information when encountering invalid arguments, and also
accept -help and --help as aliases of -h.
2020-11-16 08:14:26 +02:00
John Tsiombikas e88d9dbe85 while enqueuing events, the new serial code failed to properly push
over the read end of the queue on overflow.
2020-11-16 08:09:38 +02:00
John Tsiombikas 18ae3074a4 Removed references to the log and logfile configuration options from the
example spnavrc file. These were changed into the `-l` commandline
argument some time ago. Closes issue #27.
2020-11-15 02:33:38 +02:00
John Tsiombikas d330773cae fixed missing zero terminator when trying to determine if the serial
device is a spaceball by looking for the "\r@1" substring.
2020-11-10 19:41:22 +02:00
John Tsiombikas 486645527f merged the new serial device code, and deleted libsball and the old
inactive magellan code.
2020-11-10 01:01:25 +02:00
John Tsiombikas 26c840da63 build: improved the dependency genration method 2020-11-07 07:08:40 +02:00
John Tsiombikas 11a43b8be5
Merge pull request #23 from counterfactual-regret-minimization/master
Point spacenavd.service at new binary location
2020-03-12 15:03:08 +02:00
datadog23 81a32c8752 Point spacenavd.service at new binary location 2020-03-12 10:36:29 +00:00
John Tsiombikas e3d941caac Add commandline option to print the version number. Closes sourceforge
feature request #5.
2020-02-16 20:13:50 +02:00
John Tsiombikas 7c271fa265 - evpool_size in sball.c and smag_event.c should be static.
- added malloc error checking in alloc_event in sball.c and smag_event.c
2020-02-08 20:18:18 +02:00
John Tsiombikas 0e7ee937e0 dev.c should conditionally include proto_x11.h, only if USE_X11 is
defined. Closes github bug #22.
2020-02-08 05:23:02 +02:00
John Tsiombikas a9221a74ed Slightly re-worded the README where it lists the X dependencies, as it
was inaccurate and confusing, speaking only of "headers", while the
dependency is on the whole library, not just the headers.
2020-02-01 22:24:00 +02:00
John Tsiombikas 407416d8f3 changed the environment variables used by configure to set add_cflags
and add_ldflags to the makefile, to CFLAGS and LDFLAGS, which is much
more conventional and predictable. This only affects the configure
script, the Makefile itself still expects user-specified CFLAGS and
LDFLAGS as add_cflags and add_ldflags, but it's preferable to set those
during configure so it doesn't matter.
2020-02-01 22:21:15 +02:00
John Tsiombikas ed40a14ad7
Merge pull request #21 from hartwork/document-libxi-dependency
readme: Mention optional dependency libXi
2020-02-01 22:13:22 +02:00
John Tsiombikas 3168b1dedf add a USE_X11 ifdef around the call to drop_xinput in src/dev.c to fix
non-X11 build. This should fix github issue #19.
2020-02-01 22:08:43 +02:00
Sebastian Pipping d84e336c25 readme: Mention optional dependency libXi 2020-02-01 19:51:17 +01:00
John Tsiombikas d16ef85b7e added helper script to prepare releases 2020-02-01 02:00:08 +02:00
John Tsiombikas 081182f167 GCC 10 changes the default from -fcommon to -fno-common, so we need to
set it explicitly now. Closes github bug #17.
2020-02-01 01:20:27 +02:00
John Tsiombikas 8acf883d97 formatting and whitespace fix for the recent pull request merge 2019-12-22 00:35:48 +02:00
John Tsiombikas e1de3389a6 Merge pull request #16 which fixes spacenavd trying to grab the cad
mouse, when connected over the universal receiver.
2019-12-22 00:30:56 +02:00
tpiekar 5b3a76b84d Removed unnecessary blacklisting of input devices 2019-12-21 15:02:27 +01:00
Thomas 4d2c0117e9 Merge remote-tracking branch 'upstream/master' 2019-12-21 08:58:33 +01:00
tpiekar 278e608d99 Added special handling for mice connected to the 3Dconnexion universal receiver 2019-08-24 20:08:00 +02:00
50 zmienionych plików z 3619 dodań i 2465 usunięć

Wyświetl plik

@ -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:

Wyświetl plik

@ -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:

Wyświetl plik

@ -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
Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

104
configure vendored
Wyświetl plik

@ -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.'

Wyświetl plik

@ -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]

Wyświetl plik

@ -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

Wyświetl plik

@ -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

15
doc/spnavrc_spilot 100644
Wyświetl plik

@ -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

40
mkrel 100755
Wyświetl plik

@ -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.'

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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)

Wyświetl plik

@ -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
Wyświetl plik

@ -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 */
}

Wyświetl plik

@ -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_ */

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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_ */

Wyświetl plik

@ -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: ");

Wyświetl plik

@ -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__ */

Wyświetl plik

@ -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__ */

Wyświetl plik

@ -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;

Wyświetl plik

@ -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)) {

Wyświetl plik

@ -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_ */

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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__ */

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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);

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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_ */

Wyświetl plik

@ -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_ */

75
src/proto.c 100644
Wyświetl plik

@ -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;
}

198
src/proto.h 100644
Wyświetl plik

@ -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_ */

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
}
}
}
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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);

Wyświetl plik

@ -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:

Wyświetl plik

@ -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_ */

Wyświetl plik

@ -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