From 70fa99bf4e211d2ff99de790f525977b94c33be7 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sun, 8 Nov 2020 03:16:30 +0200 Subject: [PATCH] - fixed button detection for certain sb2003 - fixed button mapping for older spaceballs - added support for the space explorer - added button test with synthesized packets --- src/main.c | 14 +++- src/sball.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++---------- src/sball.h | 2 + 3 files changed, 198 insertions(+), 37 deletions(-) diff --git a/src/main.c b/src/main.c index ec3bcf8..c5e8c65 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -21,11 +22,20 @@ int main(int argc, char **argv) { int fd; fd_set rdset; + const char *dev = DEFDEV; signal(SIGINT, sighandler); - if(!(sb = sball_open(argv[1] ? argv[1] : DEFDEV))) { - fprintf(stderr, "Failed to open spaceball at %s\n", argv[1] ? argv[1] : DEFDEV); + if(argv[1]) { + if(strcmp(argv[1], "btest") == 0) { + sball_button_test(); + return 0; + } + dev = argv[1]; + } + + if(!(sb = sball_open(dev))) { + fprintf(stderr, "Failed to open spaceball at %s\n", dev); return 1; } fd = sball_fd(sb); diff --git a/src/sball.c b/src/sball.c index c8fed5b..a92e56e 100644 --- a/src/sball.c +++ b/src/sball.c @@ -46,7 +46,7 @@ struct sball { int len; short mot[6]; - unsigned int keystate; + unsigned int keystate, keymask; struct termios saved_term; int saved_mstat; @@ -102,10 +102,14 @@ struct sball *sball_open(const char *dev) printf("Spaceball detected: %s\n", buf); sb->nbuttons = guess_num_buttons(buf); + sb->keymask = 0xffff >> (16 - sb->nbuttons); printf("%d buttons\n", sb->nbuttons); - /* set binary mode and enable automatic data packet sending */ - write(fd, "\rCB\rMSSV\r", 9); + /* 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 sb; @@ -122,6 +126,7 @@ struct sball *sball_open(const char *dev) printf("Magellan SpaceMouse detected:\n%s\n", buf); sb->nbuttons = guess_num_buttons(buf); + sb->keymask = 0xffff >> (16 - sb->nbuttons); printf("%d buttons\n", sb->nbuttons); /* set 3D mode, not-dominant-axis, pass through motion and button packets */ @@ -324,11 +329,14 @@ static int mag_parsepkt(struct sball *sb, int id, char *data, int len) break; case 'k': - if(len != 3) { + if(len < 3) { fprintf(stderr, "magellan: invalid keyboard pakcet, expected 3 bytes, got: %d\n", len); return -1; } 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; + } print_state(sb); break; @@ -411,8 +419,8 @@ static int sball_parsepkt(struct sball *sb, int id, char *data, int len) * 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) >> 1); + sb->keystate = ((data[1] & 0xf) | ((data[1] >> 4) & 3) | ((data[0] & 7) << 4) | + ((data[0] & 0x10) << 3)) & sb->keymask; print_state(sb); break; @@ -422,7 +430,12 @@ static int sball_parsepkt(struct sball *sb, int id, char *data, int len) return -1; } /* spaceball 4000 key packet */ - sb->flags |= SB4000; + if(!(sb->flags & SB4000)) { + printf("Switching to spaceball 4000flx/5000flx-a mode (12 buttons) \n"); + sb->flags |= SB4000; + sb->nbuttons = 12; /* might have guessed 8 before */ + sb->keymask = 0xfff; + } /* update orientation flag (actually don't bother) */ /* if(data[0] & 0x20) { @@ -468,40 +481,24 @@ static int sball_parsepkt(struct sball *sb, int id, char *data, int len) static int guess_num_buttons(const char *verstr) { int major, minor; - const char *s, *model; + const char *s; 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 - */ + if(minor == 43 || minor == 45) { return 12; /* spaceball 4000flx/5000flx-a */ } - if(minor == 2 || minor == 13 || minor == 15) { + 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. + */ return 8; /* spaceball 1003/2003/2003c */ } } @@ -516,7 +513,7 @@ static int guess_num_buttons(const char *verstr) } if(strstr(verstr, "CadMan")) { - return 2; + return 4; } fprintf(stderr, "Can't guess number of buttons, default to 8, report this as a bug!\n"); @@ -585,8 +582,8 @@ static void print_keystate(unsigned int keystate) { int i; - for(i=0; i<12; i++) { - int b = 11 - i; + for(i=0; i<16; i++) { + int b = 15 - i; int hex = b < 10 ? b + '0' : b - 10 + 'a'; putchar(keystate & (1 << b) ? hex : '-'); } @@ -600,3 +597,155 @@ static void print_state(struct sball *sb) printf("]\r"); fflush(stdout); } + + +/* test all known button packets for all models */ +enum { + BTEST_SB2003, + BTEST_SB2003C, + BTEST_SB3003C, + BTEST_SB4000FLX, + BTEST_SMCLASSIC, + BTEST_SMPLUS, + BTEST_SB5000FLX, + BTEST_SPEXP, + + NUM_BTEST_MODELS +}; + +static const char *btest_models[NUM_BTEST_MODELS] = { + "Spaceball 2003", + "Spaceball 2003C", + "Spaceball 3003C", + "Spaceball 4000FLX", + "Spacemouse classic", + "Spacemouse plus/xt", + "Spaceball 5000FLX", + "Space Explorer" +}; + +struct btest_data { + int device; + const char *label; + const char *pkt; +}; + +struct btest_data btest_data[] = { + {BTEST_SB2003, "bn 1", "K@A\r"}, + {BTEST_SB2003, "bn 2", "K@B\r"}, + {BTEST_SB2003, "bn 3", "K@D\r"}, + {BTEST_SB2003, "bn 4", "K@H\r"}, + {BTEST_SB2003, "bn 5", "KA@\r"}, + {BTEST_SB2003, "bn 6", "KB@\r"}, + {BTEST_SB2003, "bn 7", "KD@\r"}, + {BTEST_SB2003, "bn 8", "KH@\r"}, + {BTEST_SB2003, "pick", "KP@\r"}, + + {BTEST_SB2003C, "bn 1", "K@A\r"}, + {BTEST_SB2003C, "bn 2", "K@B\r"}, + {BTEST_SB2003C, "bn 3", "K@D\r"}, + {BTEST_SB2003C, "bn 4", "K@H\r"}, + {BTEST_SB2003C, "bn 5", "KA@\r"}, + {BTEST_SB2003C, "bn 6", "KB@\r"}, + {BTEST_SB2003C, "bn 7", "KD@\r"}, + {BTEST_SB2003C, "bn 8", "KP@\r"}, + {BTEST_SB2003C, "zero", "K`@\r"}, + + {BTEST_SB3003C, "bn R", "KPP\r"}, + {BTEST_SB3003C, "bn L", "K@`\r"}, + {BTEST_SB3003C, "zero", "K`@\r"}, + + {BTEST_SB4000FLX, "bn 1", ".@A\r"}, + {BTEST_SB4000FLX, "bn 2", ".@B\r"}, + {BTEST_SB4000FLX, "bn 3", ".@D\r"}, + {BTEST_SB4000FLX, "bn 4", ".@H\r"}, + {BTEST_SB4000FLX, "bn 5", ".@P\r"}, + {BTEST_SB4000FLX, "bn 6", ".@`\r"}, + {BTEST_SB4000FLX, "bn 7", ".@\xc0\r"}, + {BTEST_SB4000FLX, "bn 8", ".A@\r"}, + {BTEST_SB4000FLX, "bn 9", ".B@\r"}, + {BTEST_SB4000FLX, "bn A", ".D@\r"}, + {BTEST_SB4000FLX, "bn B", ".H@\r"}, + {BTEST_SB4000FLX, "bn C", ".P@\r"}, + {BTEST_SB4000FLX, "r.h.", ".`@\r"}, + + {BTEST_SMCLASSIC, "bn 1", "kA00\r"}, + {BTEST_SMCLASSIC, "bn 2", "kB00\r"}, + {BTEST_SMCLASSIC, "bn 3", "kD00\r"}, + {BTEST_SMCLASSIC, "bn 4", "kH00\r"}, + {BTEST_SMCLASSIC, "bn 5", "k0A0\r"}, + {BTEST_SMCLASSIC, "bn 6", "k0B0\r"}, + {BTEST_SMCLASSIC, "bn 7", "k0D0\r"}, + {BTEST_SMCLASSIC, "bn 8", "k0H0\r"}, + {BTEST_SMCLASSIC, "bn *", "k00A\r"}, + + {BTEST_SMPLUS, "bn 1", "kA00\r"}, + {BTEST_SMPLUS, "bn 2", "kB00\r"}, + {BTEST_SMPLUS, "bn 3", "kD00\r"}, + {BTEST_SMPLUS, "bn 4", "kH00\r"}, + {BTEST_SMPLUS, "bn 5", "k0A0\r"}, + {BTEST_SMPLUS, "bn 6", "k0B0\r"}, + {BTEST_SMPLUS, "bn 7", "k0D0\r"}, + {BTEST_SMPLUS, "bn 8", "k0H0\r"}, + {BTEST_SMPLUS, "bn *", "k00A\r"}, + {BTEST_SMPLUS, "bn A", "k00B\r"}, + {BTEST_SMPLUS, "bn B", "k00D\r"}, + + {BTEST_SB5000FLX, "bn 1", "kA00\r"}, + {BTEST_SB5000FLX, "bn 2", "kB00\r"}, + {BTEST_SB5000FLX, "bn 3", "kD00\r"}, + {BTEST_SB5000FLX, "bn 4", "kH00\r"}, + {BTEST_SB5000FLX, "bn 5", "k0A0\r"}, + {BTEST_SB5000FLX, "bn 6", "k0B0\r"}, + {BTEST_SB5000FLX, "bn 7", "k0D0\r"}, + {BTEST_SB5000FLX, "bn 8", "k0H0\r"}, + {BTEST_SB5000FLX, "bn 9", "k00A\r"}, + {BTEST_SB5000FLX, "bn A", "k00B\r"}, + {BTEST_SB5000FLX, "bn B", "k00D\r"}, + {BTEST_SB5000FLX, "bn C", "k00H\r"}, + + {BTEST_SPEXP, "bn 1", "kA000\r"}, + {BTEST_SPEXP, "bn 2", "kB000\r"}, + {BTEST_SPEXP, "bn T", "kD000\r"}, + {BTEST_SPEXP, "bn L", "kH000\r"}, + {BTEST_SPEXP, "bn R", "k0A00\r"}, + {BTEST_SPEXP, "bn F", "k0B00\r"}, + {BTEST_SPEXP, " ALT", "k0D00\r"}, + {BTEST_SPEXP, " ESC", "k0H00\r"}, + {BTEST_SPEXP, "SHFT", "k00A0\r"}, + {BTEST_SPEXP, "CTRL", "k00B0\r"}, + {BTEST_SPEXP, " fit", "k00D0\r"}, + {BTEST_SPEXP, "panl", "k00H0\r"}, + {BTEST_SPEXP, "bn +", "k000A\r"}, + {BTEST_SPEXP, "bn -", "k000B\r"}, + {BTEST_SPEXP, " 2D", "k000D\r"}, + + {-1, 0, 0} +}; + +void sball_button_test(void) +{ + char pkt[16]; + int dev = -1; + int (*parse)(struct sball*, int, char*, int); + struct btest_data *td = btest_data; + struct sball sb = {0}; + + + while(td->device >= 0) { + if(td->device != dev) { + dev = td->device; + sb.keymask = dev == BTEST_SB3003C ? 3 : 0xffff; + parse = (dev < BTEST_SMCLASSIC) ? sball_parsepkt : mag_parsepkt; + printf("\n\nTesting button packets for: %s", btest_models[dev]); + } + + strcpy(pkt, td->pkt + 1); + sb.flags = dev == BTEST_SB4000FLX ? SB4000 : 0; + + printf("\n %s: ", td->label); + parse(&sb, td->pkt[0], pkt, strlen(pkt) - 1); + td++; + } + putchar('\n'); +} diff --git a/src/sball.h b/src/sball.h index 507a056..4ef64e8 100644 --- a/src/sball.h +++ b/src/sball.h @@ -12,4 +12,6 @@ int sball_axis(struct sball *sb, int axis); unsigned int sball_buttons(struct sball *sb); int sball_num_buttons(struct sball *sb); +void sball_button_test(void); + #endif /* SBALL_H_ */ -- 1.7.10.4