From dc8538e29a3debf94a66a34deb0aeb3ae8207c06 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Thu, 5 Nov 2020 21:08:14 +0200 Subject: [PATCH] spaceball button handling, and button number guessing --- NOTES | 49 ++++++++++++++++++++++++ src/sball.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 163 insertions(+), 9 deletions(-) create mode 100644 NOTES diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..3ce0f23 --- /dev/null +++ b/NOTES @@ -0,0 +1,49 @@ +id strings +========== + +Spaceballs +---------- +Spaceball 1003 (buttons: 8) + Firmware version 2.02 created on 11-Jun-1991. + +Spaceball 2003/2003C (buttons: 8) + Firmware version 2.02 created on 11-Jun-1991 (2003) + Firmware version 2.13 created on 07-Apr-1993 (2003) + Firmware version 2.15 created on 08-Jul-1993 (2003) + Firmware version 2.42 created on 24-Oct-1997 (2003C) + +Spaceball 3003/3003C (buttons: 2) + Firmware version 2.35 created on 23-Aug-1995 (Model 3003) + Firmware version 2.62 created on 24-Oct-1997 (Model 3003C) + Firmware version 2.63 created on 28-Sep-1998 (Model 3003C) + +Spaceball 4000 FLX/5000 FLX-A (buttons: 12) + Firmware version 2.42 created on 24-Oct-1997 + Firmware version 2.43 created on 13-Oct-2000 + Firmware version 2.45 created on 06-May-2003 (5000 FLX-A) + + +Magellan +-------- +SpaceMouse classic (buttons: 9) + MAGELLAN Version 5.49 by LOGITECH INC. 10/22/96 + MAGELLAN Version 5.79 by LOGITECH INC. 10/10/97 + +SpaceMouse plus (buttons: 9) + MAGELLAN Version 6.50 by LogiCad3D GmbH 05/11/00 + MAGELLAN Version 6.60 3Dconnexion GmbH 05/11/01 + MAGELLAN Version 6.70 3Dconnexion GmbH 05/11/02 + +Puckman (buttons: 0?) + MAGELLAN Version 5.79 by LOGITECH INC. 10/10/97 + +SpaceMouse plus XT (buttons: 9) + MAGELLAN Version 6.50 by LogiCad3D GmbH 05/11/00 + MAGELLAN Version 6.60 3Dconnexion GmbH 05/11/01 + MAGELLAN Version 6.70 3Dconnexion GmbH 05/11/02 + +Cadman (buttons: 2) + CadMan Version 7.10 3Dconnexion GmbH 21/03/02 + +Spaceball 5000 (buttons: 12) + SPACEBALL Version 8.20 3Dconnexion GmbH 05/11/02. (serial model only) diff --git a/src/sball.c b/src/sball.c index b52d896..627fc1b 100644 --- a/src/sball.c +++ b/src/sball.c @@ -13,13 +13,20 @@ #define INP_BUF_SZ 256 +enum { + SB4000 = 1, + FLIPXY = 2 +}; + struct sball { int fd; + unsigned int flags; + int nbuttons; char buf[INP_BUF_SZ]; int len; - int (*parse)(int, char*, int); + int (*parse)(struct sball*, int, char*, int); }; static int stty_sball(int fd); @@ -27,8 +34,10 @@ static int stty_mag(int fd); static int proc_input(struct sball *sb); -static int mag_parsepkt(int id, char *data, int len); -static int sball_parsepkt(int id, char *data, int len); +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(const char *verstr); static void make_printable(char *buf, int len); static int read_timeout(int fd, char *buf, int bufsz, long tm_usec); @@ -49,6 +58,8 @@ struct sball *sball_open(const char *dev) goto err; } sb->fd = fd; + sb->flags = 0; + sb->len = 0; if(stty_sball(fd) == -1) { goto err; @@ -59,8 +70,11 @@ struct sball *sball_open(const char *dev) make_printable(buf, sz); printf("Spaceball detected: %s\n", buf); - /* set SS mode */ - write(fd, "MSS\r", 4); + sb->nbuttons = guess_num_buttons(buf); + printf("%d buttons\n", sb->nbuttons); + + /* set binary mode and enable automatic data packet sending */ + write(fd, "\rCB\rMSS\r", 8); sb->parse = sball_parsepkt; return sb; @@ -76,6 +90,9 @@ struct sball *sball_open(const char *dev) make_printable(buf, sz); printf("Magellan SpaceMouse detected: %s\n", buf); + sb->nbuttons = guess_num_buttons(buf); + printf("%d buttons\n", sb->nbuttons); + /* set 3D mode, not-dominant-axis, pass through motion and button packets */ write(fd, "m3\r", 3); @@ -190,7 +207,7 @@ static int proc_input(struct sball *sb) while(bptr < end) { if(*bptr == '\r') { *bptr = 0; - sb->parse(*start, start + 1, bptr - start - 1); + sb->parse(sb, *start, start + 1, bptr - start - 1); start = ++bptr; } else { bptr++; @@ -218,7 +235,7 @@ static void print_keystate(unsigned int keystate) putchar('\n'); } -static int mag_parsepkt(int id, char *data, int len) +static int mag_parsepkt(struct sball *sb, int id, char *data, int len) { int i, mot[6]; unsigned int keystate; @@ -264,7 +281,7 @@ static int mag_parsepkt(int id, char *data, int len) return 0; } -static int sball_parsepkt(int id, char *data, int len) +static int sball_parsepkt(struct sball *sb, int id, char *data, int len) { int i; unsigned int keystate; @@ -312,7 +329,37 @@ static int sball_parsepkt(int id, char *data, int len) fprintf(stderr, "sball: invalid key packet, expected 2 bytes, got: %d\n", len); return -1; } - keystate = (data[1] & 0x30) >> 4; + if(sb->flags & SB4000) break; /* ignore K packets from spaceball 4000 devices */ + + /* 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 + */ + keystate = (data[1] & 0xf) | ((data[1] >> 4) & 3) | ((data[0] & 7) << 4) | + ((data[0] & 0x10) >> 1); + print_keystate(keystate); + break; + + case '.': + if(len != 2) { + fprintf(stderr, "sball: invalid sb4k key packet, expected 2 bytes, got: %d\n", len); + return -1; + } + /* spaceball 4000 key packet */ + sb->flags |= SB4000; + /* update orientation flag */ + if(data[0] & 0x20) { + sb->flags |= FLIPXY; + } else { + sb->flags &= ~FLIPXY; + } + + /* 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 + */ + keystate = (data[1] & 0x3f) | ((data[1] & 0x80) >> 1) | ((data[0] & 0x1f) << 7); print_keystate(keystate); break; @@ -338,6 +385,64 @@ static int sball_parsepkt(int id, char *data, int len) return 0; } +static int guess_num_buttons(const char *verstr) +{ + int major, minor; + const char *s, *model; + + if((s = strstr(verstr, "Firmware version"))) { /* spaceball */ + + /* if we got a model number, guess based on that */ + if((model = strchr(s, '('))) { + if(memcmp(model, "(Model ", 7) == 0) { + model += 7; + } else { + model++; + } + switch(atoi(model)) { + case 2003: + return 8; + case 3003: + return 2; + case 5000: + return 12; + default: + break; + } + } + /* 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) { + return 2; /* spaceball 3003/3003C */ + } + if(minor == 42 || minor == 43 || minor == 45) { + /* 2.42 is also used by spaceball 2003C, but this should be + * caught before we get here by the model number guess + */ + return 12; /* spaceball 4000flx/5000flx-a */ + } + if(minor == 2 || minor == 13 || minor == 15) { + return 8; /* spaceball 1003/2003/2003c */ + } + } + } + + if(strstr(verstr, "MAGELLAN Version")) { + return 9; /* magellan spacemouse */ + } + + if(strstr(verstr, "SPACEBALL Version")) { + return 12; /* spaceball 5000 */ + } + + if(strstr(verstr, "CadMan Version")) { + return 2; + } + + fprintf(stderr, "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; -- 1.7.10.4