Freeglut has partially working support for the Spacemouse. I would appreciate any assistance in updating the functionality to support the (most) current Spacemouse products. Currently, I am able to to get buttons and movement (on some SpaceMouse devices) with glutSpaceballMotionFunc(), etc. But only after using the proprietary 3Dconnexion SDK to initialize the device. Also, this only works for the (slightly older) SpaceMouse Compact and Pro, but not for the (newer) Enterprise and Wireless editions.
My best guess is that the HID schema has changed and needs to updated. I ran a HID utility and verified that the devices do show up. It seems that it might be necessary to set some params on the device via HID (to get rid of the 3dx SDK init) and likely update some other aspects (like product id's) within the freeglut spaceball methods.
I am happy to help fix/code this and can test on all 4 device variants forementioned. Perhaps someone can help point me in the right direction.
Which operating system?
Currently, our primary platform is Windows 10 (32-bit app). Though we
maintain support for Windows 7 as well. A copy of the app can be
downloaded here:
http://openantz.com/download/msw/proto/antz-xr_2020-09-29_app.zip
Note that these publicly posted files do not include the source code, as
they are using the proprietary 3DX SDK (just for the init function). If
you like, I am happy to send the source directly to you.
In the past (and planned future too) we have supported CentOS & Ubuntu
(not for a while) and OSX (up to 10.13, but now need to migrate to a
64bit app).
cheers,
Shane
Last edit: John Tsiombikas 2020-11-16
I can't help with the windows code, but if you come up with a patch to fix the issue, it's certainly welcome. If you do, please post it to the freeglut-developer mailing list, to maximize the number of people that might see and test it before it gets merged.
Last edit: John Tsiombikas 2020-11-16
John,
Ok, I will see what I can do and are more than happy to share my
results. Do let me know if you have any forum/docs/notes regarding the
(freeglut) glutSpaceBall... method updates (guessing 2012-15'ish), it
could help. Also you may find the 'Chronological Gallery of Spacemice'
of interest:
http://spacemice.org/index.php?title=Gallery
FYI: You may also recall having helped me with quad buffered stereo 3D
(in 2012) and I would like to say thanks once again... and let you know
that it is still working with the latest freeglut on both nVidia Quadro
and AMD Radeon (with or w/o zSpace HW).
cheers,
Shane
Last edit: John Tsiombikas 2020-11-18
I don't think there's any documentation around. But the first commit which added windows spaceball support seems to be rev.1759 and it came from this pull request in the github freeglut mirror: https://github.com/dcnieho/FreeGLUT/pull/26
Looking at the code now, it feels like it only ever was supposed to work with the space navigator. I don't see any VID/PID lists for different devices. Hopefully you can improve the device compatibility situation.
I don't remember your stereo question from 2012, but it does sound like something I'd be interested in. I haven't used quad-buffer stereo much since the advent of consumer VR HMDs, but I'm glad to hear it still works well.
Edit: oh yeah and I'm aware of the spacemice.org website. It's been a great resource, and if you look at the user-submitted pictures you'll find one of mine there from back in 2012 :)
Last edit: John Tsiombikas 2020-11-18
Thank you for pointing me to the commit, it helps to know exactly which
code was updated.
Below are the (older) 3DX models using the main Logitech VID=0x046D
which includes various spacemice and the CADman (2D mouse):
https://www.the-sz.com/products/usbid/index.php?v=0x046D&p=&n=3dconnexion
3DX was using the logitech VID=0x046D and is now using it's own
VID=0x256F with the full list of (newer) PID's here:
https://www.the-sz.com/products/usbid/index.php?v=0x256f&p=&n=
Some of the PID's are spacemice and some are variants of the regular
(2D) CadMouse, plus the 'Universal Receiver' (PID=0xC652).
I need to verify if the wireless spacemice shows up only with the unique
PID's listed, or if they also appear with the U-Receiver PID=0xC652.
Also, probably not important, but I wanted to mention the current 3DX
(proprietary) driver shows up as a Logitech driver to Windows.
Clearly, we can simply add all spacemice specific VID/PID pairs listed
above. However, future products will not work (without updating the
list). Perhaps we can find an elegant hack that auto-id's current/future
spacemice models (PID"s). For example, the new spacemice PID's are
nearly sequential from 0xC62E to 0xC635 then jump to 0xC651 for the
CadMouse (and U-Rx), so perhaps we could assume they reserverd 0xC62E -
0xC64F for spacemice. Or maybe just ask them.
Lastly, I have some interest in supporting multiple spacemice. The
driver supports this, and I do know a few folks who like to have one in
each hand. Though it occurs to me that this seems incompatible with the
existing fgSpaceball... API spec. Any thoughts on this front?
cheers,
Shane
Last edit: John Tsiombikas 2020-11-19
In my spacenavd project, I'm using a heurstic to detect 3Dconnexion devices. It basically matches any device with the new VID, any device with the logitech VID that also has "3Dconnexion" in the vendor name string, and also comes with a short blacklist of devices which would match but are not spacemice. The relevant code is here: https://github.com/FreeSpacenav/spacenavd/blob/master/src/dev.c#L244 A few years ago a "positive match" list of known spacemice ids was also added to make it certain we won't miss a known device, but this detection was working reliably for years without it, just based on the heuristic outlined above. At some earlier time I was also looking for the axis number being >= 6, but I must have removed it at some point.
As for multiple spacemice, you should bring it up to the freeglut-developer mailing list. Obviously the original spaceball callbacks can't be used for that purpose, since they don't carry any distinguishing information about the source of the event, but an extension could be created similar to the multitouch callbacks.
Personally I'm not convinced this is generally useful enough to warrant inclusion in freeglut by adding yet more API entry points, but I'm not opposed to it either if consensus is reached that it should be added.
Okay, I did a quick hack 'fg_spaceball_mswin.c' (attached) and got the
SpaceMouse Enterprise working. And (just now) I looked at the heuristic
method in your spacenavd project, and will start mulling this over.
Also glanced at the X11 'fg_spaceball_x11.c' and it seems that the
methods may work there as well (or does it work already?)
There are two things that need resolving at the moment:
1) Why it doesn't work without calling the 3DX SDK init function.
(Will report back on what I find).
2) How to properly handle the various (button) data packet sizes.
- https://forum.3dconnexion.com/viewtopic.php?t=3721
With Navigator & Enterprise, I've been getting erroneous button values
(large numbers which I temporarily ignored in the code if > a small #).
Likely related to the various packet sizes described in the link above.
Also, some other 3DX forum posts discussing 7 and 13 byte packets and
a reference that devices going forward from SpacePilot Pro would all
use the larger 'button' data packet size. But not sure what that size
is, (perhaps 4, 5 or 7 bytes).
In terms of supporting multiple Spacemice via an extension. It might be
worth bringing up in the context of general 6DOF support. Currently I
like using the SpaceMouse to navigate the cam and a 6DOF zSpace Stylus
to pick and edit objects. They and many others (Oculus, Hololenz, Vive)
plan to support OpenXR. I guess the question would be, should support
for multiple 6DOF hand controllers be part of FreeGLUT or handled
directly via OpenXR (which is not yet ready). Or perhaps some sort of
simplified freeglut API extension that (optionally?) hooks to OpenXR?
Far as I can think of, there are two types of 6DOF devices (of merit).
Spacemice that give force vectors and the Mixed Reality (AR/VR/MR)
hand controllers that provide real-world position (translate/rotate)
relative to the screen.
At the moment, it is unfortunate that the only way to use these is by
implementing (proprietary) SDK's for each manufacturer.
Philosophically, it seems inline with the original intent of GLUT to
support 6DOF devices in a way that is simple as a Spaceball.
Last edit: John Tsiombikas 2020-11-21
When you're done with your changes, post a patch in order for the changes to be reviewed properly and merged.
The X11 side of the spaceball support works with every device, because it relies on either the official driver or my own free software one, for 6dof events, instead of trying to talk to the devices directly.
Supporting input from VR trackers is in my opinion clearly outside of the scope of a GLUT implementation.
Definitely planning to post a patch as soon as the buttons and
init issue are resolved.
Nice that X11 is fully working, looking into spacenavd project to
try and tease out button data packet sizes (across models).
With both the Space Nav & Enterprise, I get erroneous button
values from glutSpaceballButtionFunc(), with the original code
and my new code, ie: 4096,... -2147483648). And yes they
are exactly n^2 + 1.
Turns out that the Nav device mixes (puck) force data into the
upper 16bits of the int. Lower 16 bits are good values, (0, 1, 2).
The SpaceMouse Explorer does not do this (puck force makes no
difference). But generates what appears to be a button bitfield,
where each button turns on/off a single bit, and combos are
possible, ie: 0x04000010.
What I found is that the packet size for the Nav is 7 bytes and
the Enterprise is 13. So, I think it will work to detect the packet
sizes and do a little bitmasking with conversion of the relevant
bits to button integers.
The problem I could foresee, is if there is another packet size
used by other relevant Spacemice and I would not know how to
guess which parts of the packet are the buttons vs motion.
Maybe you know the answer or a direction to go in?
cheers, Shane
Last edit: John Tsiombikas 2020-11-22
I have fixed all of the button bugs I could find, (dev testing with the Space Navigator and SpaceMouse Enterprise). Also, enhanced the code to potentially support other (legacy) 3Dconnexion devices using the Logitech VID = 0x046d with support for 3, 4 and 5 byte button data packets. The code has verbose comments documenting the VID, PID, button data packet bits, new (3Dconnexion VID= 0x256f) event types and numerous pecularities.
The code assumes that all (multi-axis) devices using VID=0x046d have a 3 byte button data packet (1 byte event + 16bits of bitwise buttons), except for:
Note that I have not tested the various packet sizes, (other than the Space Nagivator which does have a 3 byte packet). I read on a 3Dconnexion forum post that the above devices are the only exceptions, but can (and will) verify this for the Space Mouse Pro (and separately test the SpaceMouse Wireless with VID 0x256f to confirm it uses 32bits of button data). However, in theory the code should work on all other devices whether they are 16bit or more.
Still working on sorting out my (app) init issue, but this may not be freeglut, we'll see. Meanwhile, here is the 'fg_spaceball_mswin.c' code file with a patched freeglut.dll just in case someones wants to do a quick test:
http://openantz.com/code/freeglut/
Perhaps you would like to weigh in on the button id's. I created a system for auto-assigning the buttons a number based on the following rules:
1) BITWISE buttons = bit position + 1 (same as before for event id = 3). This is given precedence over over the 'X' button values below, ie: If a button has an associated bitwise bit, then the bit position determines it value, else see rule 2.
2) X buttons = integer value X (generated by the event id = 28). Applies ONLY to buttons that are not represented in the bitfield used with event id = 3 (rule 1).
3) HELD-buttons = integer value + 1000 (an arbitrary offset). These 'special' buttons generate event id = 29 when HELD down for 1 second and I give them an offset to make them unique.
The button numbers reported through
glutSpaceballButton
need to be contiguous from 1 toglutGet(GLUT_NUM_SPACEBALL_BUTTONS)
. You can't have arbitrary designation bits for special buttons I'm afraid.I can't test on windows myself. When you have a patch ready I'll do a visual code review, make sure it doesn't break the build on UNIX for some reason, and forward it to the list for windows users to test and see if it causes any issues on their side. I don't think there's anyone lurking in the mailing list who can test the actual spaceball functionality on windows other than you :) So when you say it's good to go, it's good to go. At the very least it seems that it'll be strictly an improvement from the current version, even if only the few devices you can test work correctly, since the current code only ever supported the space navigator.
Last edit: John Tsiombikas 2020-11-25
Well that sounds sensible. I can think of two possible approaches:
1) A set of lists, one for each PID with button count and all button integer values, (for reassigning them to a contigous set of button id's). For the multi-button devices we would need to create these lists for: Space Pilot, Space Explorer, Space Pilot Pro, Space Mouse Pro and SpaceMouse Enterprise. The rest we can just assume are 2 buttons.
2) Perhaps we can use the MSW (RID_DEVICE_INFO_HID) UsagePage(s), to programmatically determine the button count and event values. I am currrently looking into this, but have not found any 3DX related docs nor code, so it's a shot in the dark.
Regarding the UNIX compile: Someone needs to verify this for me, though I don't expect any major problems. All the changes are contained to
fg_spaceball_mswin.c
nd other than the obligatory MSW function calls and data types, I have written the code in a cross-platform style. On a bit of a sidenote, antz uses freeglut specifically for it's platform portability and a long long time ago was ported to linux (and I have some super-users asking for a CentOS version at the moment).Last edit: Shane Saxon 2020-11-25
I found that my init problem is a bug in
glutDeviceGet(GLUT_HAS_SPACEBALL)
, where it reports zero during startup, even afterglutShowWindow()
is called. I fixed the bug by modifyingfgPlatformHasSpaceball()
to include:if (!fg_sball_initialized) fgPlatformInitializeSpaceball();
A small victory that is super helpful, as I have completely unbolted the proprietary 3Dconnexion SDK.... Hurrah for open source!
The Good the Bad and the Ugly - special buttons edition:
I (just) noticed that the 3DX sdk has a file with a single enumiration of integer key constants for all keys (across all devices) and it directly matches the HID event values for all of the keys on the SpaceMouse Enterprise. Of course this is only true for the two primary event types: 3 (bitwise) and 28 (integer). It also defines unique key constants for the 'special' HELD event 29, but these (of course) do not directly correspond to the raw data, but are named in the enumerated list and could be mapped accordingly. The file is from 2013, which predates the 2016 release of the Enterprise. So, now I am thinking that (some or all) of the older devices may comply with this list and new ones likely will too, (definitely need real world testing!)
However this brings us back to the issue of compacting the gaps (contiguous 1 to # buttons). The problem I see here is that a button with the same name (eg. ESC, ALT, SHIFT, CTRL, ROTATE) will end up with different button values, depending on the device (eg. Space Pilot vs SpaceMouse Explorer, etc.) Also, some of the older devices have buttons that are not on the newer devices (eg. CONFIG, MINUS(-), PLUS(+), etc. ). This means that there is no solution for a unified compacted (contiguous) re-mapping schema without re-assigning (different) values to buttons with the same label (across models). This would make using
glutSpaceballButtonFunc()
very tricky. Especially since the existing API does not expose the model of the device.Simply put, there is no perfect solution, so the question becomes, what is a good compromise.
Currently thinking that
glutDeviceGet(GLUT_NUM_SPACEBALL_BUTTONS)
could either return the number of distinct button event types (2, 15, 21, 31....) OR it could return the maximum key (event) value generated by the device. This possible approach may require internally identifying the device (by PID) to properly remap event 29 key values to the (3DX) defined key list. A silver lining to this approach is that it provides developers with a seamless migration path from the SDK to open source, as key values would remain the same across both domains.Last edit: Shane Saxon 2020-11-26
I don't think there's a good way to avoid having different numbers for the same "labeled" buttons on different devices, so I wouldn't even try.
GLUT has always been about a simplified API, not necessarilly about providing all the available information. Spaceball button callbacks just provide a button number, with no implied relationship between that number and specific identifiable butotns on the device. It's a simple interface but it's sufficient for most use cases, and it just puts the onus of button identification, if necessary, to the application, by possibly providing some sort of button mapping UI to the user.
To maintain maximum compatibility with original GLUT, it's necessary that buttons are simply numbers from 1 to the number of available buttons. The user might rely on that by allocating arrays or bitmaps for state tracking, or showing a number of mappable buttons to the user by painting N squares on a UI screen. There can't be any gaps.
The contiguous button approach presents a few challenges, which could be considered as follows:
1) It is inconsistent with the (currently) defined FreeGLUT API, which has non-contiguous button defines:
2) It is problematic to the end-user, as different devices will behave differently with the same labelled keys.
3) There is not (currently) a method for the developer to detect the device model, and placing the onerous task of reverse-mapping buttons back to the actual label names (for every device) is simply not feasible without knowing the model.
4) It will require updating the key maps for each new model.
That said, I propose we stay consistent with the existing API and currently released code. Which (at least on Windows) directly provides the raw bitwise button bit position as the button number. I believe this is also the case for X11, but can you confirm this?
If X11 behaves differently, then we have a conundrum that deserves resolving, (as this means the current releases break cross-platform consistency).
We can add support to report the accurate number of buttons, (which currently is an empty function), by using an enumerated list based on VID and PID, hence properly supporting:
glutDeviceGet(GLUT_NUM_SPACEBALL_BUTTONS)
Further, it seems worthwhile to expose the device model by extending the API, perhaps with:
glutDeviceGet(GLUT_NUM_SPACEBALL_VID_PID)
This would allow the developer to provide the user with "N squares on a UI screen", without any any gaps.
Last edit: Shane Saxon 2020-11-27
Here's the GLUT documentation for the glutSpaceballButton callback: https://www.opengl.org/resources/libraries/glut/spec3/node57.html#SECTION000812000000000000000
It clearly states that buttons are numbers starting at 1. The X11 version of the code which I wrote works that way. If the win32 code which was added later provides bitmasks then it's a bug and it needs to be fixed. But since the win32 code only supported the space navigator which has only two buttons, bitmasks and 1-based button numbers are the same, which is probably why nobody noticed until now. (That, and also that very few people actually use spaceballs with freeglut).
Adding a
GLUT_NUM_SPACEBALL_VID_PID
query doesn't make much sense, because not all spaceballs are even USB devices. This is clearly one of the cases of being too much detail for GLUT. If an application needs to know which device is providing the input, they need to use platform-specific APIs to do that.In terms of original (forementioned) GLUT API docs, they do say:
It does say 'starting at one', but nothing about the buttons being contiguous. The FreeGLUT API (sec 12.17) added the non-contiguous button constants fore-mentioned, and referenced here:
http://freeglut.sourceforge.net/docs/api.php#WindowCallback
Now I see no reason to split hairs regarding the original GLUT docs, and the current FreeGLUT ones clearly have priority, after all that's why people use freeglut! Regardless, I do feel that what is important is that the current API be made consistent across platforms, (especially X11 and Windows). The question is, how to reconcile both the docs and the existing functionality to support current and legacy devices.
Note that the pre-existing Windows code does support ALL multi-axis (0x08) devices with VID=0x042d with up to 32 (raw) bitwise buttons. Of course, it does not support the new 3DX VID=0x256f, nor does it support the buttons that only show up with event types 28 and 29. These events may or may not exist on the legacy devices, perhaps someone has the HW and can help by testing this, (raw HW data packets are likely the same on X11)?
Since, the FreeGLUT (Windows) Spaceball code did not work at all until Dec, 2014. I think you are certainly right that very few (dev's) have used the (Win) API. Like myself (until recently), most probably don't know that the functionality was even restored. I originally tried it in 2011 and found that the functions were empty, and did not notice it was (partially) fixed until this year (and despite extensive research, have not found any docs anywhere online to this regard).
Hence, I have resorted to reverse-engineering the raw events, (which are convaluted to say the least). Not to mention downloading whatever product literature I could find to read about these 'dual-function' keys and other (mystery) programmable view keys, etc. So any info about the actual raw data of any/all devices would be greatly appreciated.
If (I understand you correctly and) there are significantly more developers using the X11 implementation of the Spaceball, (then those using the Windows API), then perhaps we just disregard the existing Win API and define a new methodology that is consistent with the X11 API and support for both legacy and new device functionality. Which brings me to the question, how/are the buttons currently being mapped in X11?
eg. Do you have a set of lists that map the (non-contiguous) button data packets, (raw HW keys) to the contiguous GLUT button numbers based on VID/PID and/or for non-USB devices?
The documentation is wrong and needs fixing. I didn't write that section. Just to dispel any doubt about the original GLUT behavior, here's the relevant code snippet from GLUT 3.7:
It just passes the
button
field from theXDeviceButtonEvent
structure (X input extension) directly to the callback. And that button field is a number from 1 to the number of available buttons.As for the button mapping question in the X11 code, I'm not doing anything to map specific "labeled" buttons to the same number across devices. I'm just passing the button number as reported by the device, through to the user. There's nothing else to do really, again this is not a button "name", it's a button number.
Now in terms of contiguous buttons, yes let's do this!
However, at this point we are scrambling the button id's across models. So regardless of the (below) logic, the X11 behavior should take precedence and be mirrored to maintain cross-platform consistency.
So the key question remains, what is the X11 key-map for the SM Pro and/or SM Enterprise?
Best I can determine, the SM Pro (original and Wireless) and SM Enterprise are the only 2 devices with gaps in the raw button values.
Both have a deviant event for the 'Dual Function' / long press keys. Note that the raw key value is the same key as when a short press occurs, but is distinguishable by the event 0x16 (SM Pro) and 0x1d (SM Enterprise).
Now it would make things relatively congruent if it follows the pattern: First mapping the Dual Function keys to fill the gaps, (based on the 'Space Pilot Pro' pattern), then compact all remaining gaps.
Space Pilot Pro defines the 'Dual Function 'keys with constants that fill up some of the SpaceMouse Pro gaps and all of the SM Enterprise gaps. Note that (for bitwise keys) it matches across all models from 2009 forward.
Lastly, the Enterprise has additional gaps in the non-bitwise keys (with value > 32), which could be compacted using the same pattern, (yes, 3 of them are also Dual Function keys).
For details on the HID schema's with event behavior, you can view my (in progress) docs here: 3Dconnexion HID Data Packets
I don't have a spacemouse pro or spacemouse enterprise to tell you for sure. But you could set up a GNU/Linux VM, and install spacenavd and freeglut to check.
I do have a spacepilot with 21 buttons, and the Linux kernel just sends me button numbers from 0 to 20 without any gaps, which is what I'm sending to applications through spacenavd, and which is what my freeglut code returns after adding 1 to make it start from 1. I have not examined the Linux HID driver to see how exactly it maps the HID packets to contiguous button numbers reported through the evdev interface, but I expect the HID report descriptors are self-describing enough for Linux to manage this without special drivers.
I finally got my hands on a SpaceMouse Enterprise, and indeed that's the first device I've seen with gaps in button numbers. I think this strange behaviour is due to there being two different report collections, one with 31 contiguous buttons, and one with 256 buttons with gaps, and apparently linux evdev does indeed report button numbers with gaps. I had to implement a hardcoded remapping in spacenavd to properly support these devices:
https://github.com/FreeSpacenav/spacenavd/commit/8a3c9617a507fc5d291f866ca1b66591e8a6b9f0#diff-12a9ed4a3cf62ac26e1aa260cafefc56833fdb435ad08271525dd6544db5b729R465
Of course freeglut for X11 takes its 6dof input from spacenavd, so it should now also have contiguous button mappings for these devices.
Excellent, very helpful details and will proccede accordingly!
I can't speak much to how Linux 'maps the HID packets... evdev interface'. Perhaps with the 'HID report descriptors', I started down a similar path with the msw code, but then came across people showing that (on Windows) the 3Dx HID descriptors are sometimes erroneous, (depending on device firmware etc.) Which I found to at least be true for Space Navigator button packet size, hid.dwSizeHid reports 7 bytes for all events (and same issue with SM Wireless reporting 13 bytes for all events as well). This is correct for motion events but not the correct size for the button event, which is 3 bytes (for all 2 button models) and the bytes past that contain garbage, (contains partial motion data, so pressing a button while pushing on the puck is needed to test the code).
Device testing:
Perhaps you can tell me which button the spacepilot sends for button bit 20, (freeglut button number = 21). There seems to be a bit of a mystery around this button, (as noticed in the blender file 'GHOST_NDOFManager.cpp').
I have some super-users that helped me verify the HID schema's for SpaceMouse Wireless and Space Mouse Pro (original). The S.M.P uses the original 1+6 byte dual motion events. However, the SM.W uses a 1+12 byte motion event and likely so does the SpaceMouse Pro Wireless and all multi-axis devices with VID = 0x256f, a theory that needs testing!
And yes, I will test the HW on hand (Space Nav and Enterprise) directly on linux with spacenavd.
The highest numbered button (button index 20 (0-based) in linux evdev) on the space pilot is the "config" button, which is the leftmost in a cluster of 2 buttons right in front of the puck.
I also captured the USB traffic from the device while pressing that button, and I got:
03 00 00 10
, which indeed looks like a button event with a bitmask with bit 20 set.