spaceball button handling, and button number guessing
authorJohn Tsiombikas <nuclear@member.fsf.org>
Thu, 5 Nov 2020 19:08:14 +0000 (21:08 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Thu, 5 Nov 2020 19:08:14 +0000 (21:08 +0200)
NOTES [new file with mode: 0644]
src/sball.c

diff --git a/NOTES b/NOTES
new file mode 100644 (file)
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)
index b52d896..627fc1b 100644 (file)
 
 #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;