From 60cb7f6f138ae291f431ec5cf269cfa6addccac6 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Fri, 25 Mar 2022 11:53:10 +0200 Subject: [PATCH] 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. --- src/cfgfile.c | 41 +++++-------- src/dev.c | 153 +++++++++++++++++++++++++++++++++-------------- src/dev.h | 1 + src/dev_serial.c | 5 ++ 4 files changed, 130 insertions(+), 70 deletions(-) diff --git a/src/cfgfile.c b/src/cfgfile.c index a80328f..72b27e5 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -57,9 +57,6 @@ static int add_cfgopt_devid(int vid, int pid); enum {TX, TY, TZ, RX, RY, RZ}; -static const int def_axmap[] = {0, 1, 2, 3, 4, 5}; -static const int def_axinv[] = {0, 1, 1, 0, 1, 1}; - struct cfgline { char *str; /* actual line text */ int opt; /* CFG_* item */ @@ -85,13 +82,11 @@ void default_cfg(struct cfg *cfg) cfg->dead_threshold[i] = 2; } - cfg->swapyz = 1; cfg->led = LED_ON; 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; iopt = 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) { @@ -528,23 +523,23 @@ int write_cfg(const char *fname, struct cfg *cfg) add_cfgopt(CFG_REPEAT, 0, "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]) { + if(cfg->invert[0] || cfg->invert[1] || cfg->invert[2]) { char flags[4] = {0}, *p = flags; - if(cfg->invert[0] != def_axinv[0]) *p++ = 'x'; - if(cfg->invert[1] != def_axinv[1]) *p++ = 'y'; - if(cfg->invert[2] != def_axinv[2]) *p = 'z'; + 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); } - if(cfg->invert[3] != def_axinv[3] || cfg->invert[4] != def_axinv[4] || cfg->invert[5] != def_axinv[5]) { + if(cfg->invert[3] || cfg->invert[4] || cfg->invert[5]) { char flags[4] = {0}, *p = flags; - if(cfg->invert[3] != def_axinv[3]) *p++ = 'x'; - if(cfg->invert[4] != def_axinv[4]) *p++ = 'y'; - if(cfg->invert[5] != def_axinv[5]) *p = 'z'; + 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); } - if(cfg->map_axis[1] != def_axmap[1]) { + if(cfg->swapyz) { add_cfgopt(CFG_SWAPYZ, 0, "swap-yz = true"); } @@ -560,10 +555,6 @@ int write_cfg(const char *fname, struct cfg *cfg) } } - if(cfg->swapyz != def.swapyz) { - add_cfgopt(CFG_SWAPYZ, 0, "swap-yz = %s", cfg->swapyz ? "true" : "false"); - } - if(cfg->led != def.led) { add_cfgopt(CFG_LED, 0, "led = %s", (cfg->led ? (cfg->led == LED_AUTO ? "auto" : "on") : "off")); } diff --git a/src/dev.c b/src/dev.c index 0698399..4186a10 100644 --- a/src/dev.c +++ b/src/dev.c @@ -32,10 +32,70 @@ along with this program. If not, see . #include "proto_x11.h" #endif + +#define VENDOR_3DCONNEXION 0x256f + +/* 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 +}; + +static struct { + int usbid[2]; + int type; + unsigned int flags; +} devid_list[] = { + {{0x046d, 0xc603}, DEV_PLUSXT, 0}, /* spacemouse plus XT */ + {{0x046d, 0xc605}, DEV_CADMAN, 0}, /* cadman */ + {{0x046d, 0xc606}, DEV_SMCLASSIC, 0}, /* spacemouse classic */ + {{0x046d, 0xc621}, DEV_SB5000, 0}, /* spaceball 5000 */ + {{0x046d, 0xc623}, DEV_STRAVEL, DF_SWAPYZ | DF_INVYZ}, /* space traveller */ + {{0x046d, 0xc625}, DEV_SPILOT, DF_SWAPYZ | DF_INVYZ}, /* space pilot */ + {{0x046d, 0xc626}, DEV_SNAV, DF_SWAPYZ | DF_INVYZ}, /* space navigator */ + {{0x046d, 0xc627}, DEV_SEXP, DF_SWAPYZ | DF_INVYZ}, /* space explorer */ + {{0x046d, 0xc628}, DEV_SNAVNB, DF_SWAPYZ | DF_INVYZ}, /* space navigator for notebooks*/ + {{0x046d, 0xc629}, DEV_SPILOTPRO, DF_SWAPYZ | DF_INVYZ}, /* space pilot pro*/ + {{0x046d, 0xc62b}, DEV_SMPRO, DF_SWAPYZ | DF_INVYZ}, /* space mouse pro*/ + {{0x046d, 0xc640}, DEV_NULOOQ, 0}, /* nulooq */ + {{0x256f, 0xc62e}, DEV_SMW, DF_SWAPYZ | DF_INVYZ}, /* spacemouse wireless (USB cable) */ + {{0x256f, 0xc62f}, DEV_SMW, DF_SWAPYZ | DF_INVYZ}, /* spacemouse wireless receiver */ + {{0x256f, 0xc631}, DEV_SMPROW, DF_SWAPYZ | DF_INVYZ}, /* spacemouse pro wireless */ + {{0x256f, 0xc632}, DEV_SMPROW, DF_SWAPYZ | DF_INVYZ}, /* spacemouse pro wireless receiver */ + {{0x256f, 0xc633}, DEV_SMENT, DF_SWAPYZ | DF_INVYZ}, /* spacemouse enterprise */ + {{0x256f, 0xc635}, DEV_SMCOMP, DF_SWAPYZ | DF_INVYZ}, /* spacemouse compact */ + {{0x256f, 0xc636}, DEV_SMMOD, DF_SWAPYZ | DF_INVYZ}, /* spacemouse module */ + + {{-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, 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_dev_info *devinfo); static int usbdevtype(unsigned int vid, unsigned int pid); +static unsigned int usbdevflags(unsigned int vid, unsigned int pid); static struct device *dev_list = NULL; static unsigned short last_id; @@ -79,6 +139,7 @@ int init_devices_usb(void) struct device *dev; struct usb_dev_info *usblist, *usbdev; spnav_event ev = {0}; + char buf[256]; /* detect any supported USB devices */ usblist = find_usb_devices(match_usbdev); @@ -96,13 +157,29 @@ int init_devices_usb(void) dev = add_device(); strcpy(dev->path, usbdev->devfiles[i]); dev->type = usbdevtype(usbdev->vendorid, usbdev->productid); + dev->flags = usbdevflags(usbdev->vendorid, usbdev->productid); 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] == VENDOR_3DCONNEXION) { + 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); + 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; @@ -224,7 +301,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) @@ -248,45 +339,6 @@ struct device *get_devices(void) return dev_list; } -#define VENDOR_3DCONNEXION 0x256f - -static int devid_list[][3] = { - {0x046d, 0xc603, DEV_PLUSXT}, /* spacemouse plus XT */ - {0x046d, 0xc605, DEV_CADMAN}, /* cadman */ - {0x046d, 0xc606, DEV_SMCLASSIC}, /* spacemouse classic */ - {0x046d, 0xc621, DEV_SB5000}, /* spaceball 5000 */ - {0x046d, 0xc623, DEV_STRAVEL}, /* space traveller */ - {0x046d, 0xc625, DEV_SPILOT}, /* space pilot */ - {0x046d, 0xc626, DEV_SNAV}, /* space navigator */ - {0x046d, 0xc627, DEV_SEXP}, /* space explorer */ - {0x046d, 0xc628, DEV_SNAVNB}, /* space navigator for notebooks*/ - {0x046d, 0xc629, DEV_SPILOTPRO}, /* space pilot pro*/ - {0x046d, 0xc62b, DEV_SMPRO}, /* space mouse pro*/ - {0x046d, 0xc640, DEV_NULOOQ}, /* nulooq */ - {0x256f, 0xc62e, DEV_SMW}, /* spacemouse wireless (USB cable) */ - {0x256f, 0xc62f, DEV_SMW}, /* spacemouse wireless receiver */ - {0x256f, 0xc631, DEV_SMPROW}, /* spacemouse pro wireless */ - {0x256f, 0xc632, DEV_SMPROW}, /* spacemouse pro wireless receiver */ - {0x256f, 0xc633, DEV_SMENT}, /* spacemouse enterprise */ - {0x256f, 0xc635, DEV_SMCOMP}, /* spacemouse compact */ - {0x256f, 0xc636, DEV_SMMOD}, /* spacemouse module */ - - {-1, -1, DEV_UNKNOWN} -}; - -/* 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, 0xc62c}, /* lipari(?) */ - {0x256f, 0xc641}, /* scout(?) */ - - {-1, -1} -}; - - static int match_usbdev(const struct usb_dev_info *devinfo) { int i; @@ -326,8 +378,8 @@ static int match_usbdev(const struct usb_dev_info *devinfo) } /* 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]) { + for(i=0; devid_list[i].usbid[0] > 0; i++) { + if(vid == devid_list[i].usbid[0] && pid == devid_list[i].usbid[1]) { return 1; } } @@ -344,10 +396,21 @@ static int match_usbdev(const struct usb_dev_info *devinfo) static int usbdevtype(unsigned int vid, unsigned int pid) { int i; - for(i=0; devid_list[i][0] != -1; i++) { - if(devid_list[i][0] == vid && devid_list[i][1] == pid) { - return devid_list[i][2]; + for(i=0; devid_list[i].usbid[0] != -1; i++) { + if(devid_list[i].usbid[0] == vid && devid_list[i].usbid[1] == pid) { + return devid_list[i].type; } } return DEV_UNKNOWN; } + +static unsigned int usbdevflags(unsigned int vid, unsigned int pid) +{ + int i; + for(i=0; devid_list[i].usbid[0] != -1; i++) { + if(devid_list[i].usbid[0] == vid && devid_list[i].usbid[1] == pid) { + return devid_list[i].flags; + } + } + return 0; +} diff --git a/src/dev.h b/src/dev.h index 94574ef..a0c7c3b 100644 --- a/src/dev.h +++ b/src/dev.h @@ -33,6 +33,7 @@ struct device { char path[PATH_MAX]; int type; unsigned int usbid[2]; /* vendor:product for USB devices */ + unsigned int flags; int num_axes, num_buttons; int *minval, *maxval; /* input value range (default: -500, 500) */ diff --git a/src/dev_serial.c b/src/dev_serial.c index 0cbb362..659da61 100644 --- a/src/dev_serial.c +++ b/src/dev_serial.c @@ -362,6 +362,11 @@ static int mag_parsepkt(struct sball *sb, int id, char *data, int len) (((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++;