X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Fsball.c;h=e6ff48e2fef147609633cbab6a96370540ce18c0;hb=4ab56d777d7e9e2ae2e69278d18d31bb480d6151;hp=b52d896cef8d65fed9b5ebaff549e146bffc4910;hpb=d9869dd84b82cbf6cd786d857145b62e2bcf50de;p=sball diff --git a/src/sball.c b/src/sball.c index b52d896..e6ff48e 100644 --- a/src/sball.c +++ b/src/sball.c @@ -4,77 +4,118 @@ #include #include #include -#include #include #include #include #include #include +#include + +#if defined(__i386__) || defined(__ia64__) || defined(WIN32) || \ + (defined(__alpha__) || defined(__alpha)) || \ + defined(__arm__) || \ + (defined(__mips__) && defined(__MIPSEL__)) || \ + defined(__SYMBIAN32__) || \ + defined(__x86_64__) || \ + defined(__LITTLE_ENDIAN__) +#define SBALL_LITTLE_ENDIAN +#else +#define SBALL_BIG_ENDIAN +#endif #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); + short mot[6]; + unsigned int keystate; + + struct termios saved_term; + int saved_mstat; + + int (*parse)(struct sball*, int, char*, int); }; -static int stty_sball(int fd); -static int stty_mag(int fd); +static int stty_sball(struct sball *sb); +static int stty_mag(struct sball *sb); +static void stty_save(struct sball *sb); +static void stty_restore(struct sball *sb); 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); +static void print_state(struct sball *sb); + + struct sball *sball_open(const char *dev) { int fd, sz; char buf[128]; struct sball *sb = 0; - if((fd = open(dev, O_RDWR | O_NOCTTY)) == -1) { + if((fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) { fprintf(stderr, "sball_open: failed to open device: %s: %s\n", dev, strerror(errno)); return 0; } - if(!(sb = malloc(sizeof *sb))) { + if(!(sb = calloc(1, sizeof *sb))) { fprintf(stderr, "sball_open: failed to allocate sball object\n"); goto err; } sb->fd = fd; - if(stty_sball(fd) == -1) { + stty_save(sb); + + if(stty_sball(sb) == -1) { goto err; } + write(fd, "\r@RESET\r", 8); - if((sz = read_timeout(fd, buf, sizeof buf - 1, 2000000)) > 0 && memcmp(buf, "\r@1", 3) == 0) { + if((sz = read_timeout(fd, buf, sizeof buf - 1, 2000000)) > 0 && strstr(buf, "\r@1")) { /* we got a response, so it's a spaceball */ 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\rMSSV\r", 9); sb->parse = sball_parsepkt; return sb; } /* try as a magellan spacemouse */ - if(stty_mag(fd) == -1) { + if(stty_mag(sb) == -1) { goto err; } write(fd, "vQ\r", 3); if((sz = read_timeout(fd, buf, sizeof buf - 1, 250000)) > 0 && buf[0] == 'v') { make_printable(buf, sz); - printf("Magellan SpaceMouse detected: %s\n", buf); + printf("Magellan SpaceMouse detected:\n%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); @@ -84,6 +125,7 @@ struct sball *sball_open(const char *dev) } err: + stty_restore(sb); close(fd); free(sb); return 0; @@ -92,6 +134,8 @@ err: void sball_close(struct sball *sb) { if(!sb) return; + + stty_restore(sb); close(sb->fd); } @@ -120,16 +164,34 @@ int sball_read(struct sball *sb) return 0; } -/* Labtec spaceball: 9600 8n1 XON/XOFF */ -static int stty_sball(int fd) +int sball_axis(struct sball *sb, int axis) { - struct termios term; + return sb->mot[axis]; +} - if(tcgetattr(fd, &term) == -1) { - perror("sball_open: tcgetattr"); - return -1; - } +unsigned int sball_buttons(struct sball *sb) +{ + return sb->keystate; +} + +int sball_num_buttons(struct sball *sb) +{ + return sb->nbuttons; +} + +/* Labtec spaceball: 9600 8n1 XON/XOFF + * Can't use canonical mode to assemble input into lines for the spaceball, + * because binary data received for motion events can include newlines which + * would be eaten up by the line discipline. Therefore we'll rely on VTIME=1 to + * hopefully get more than 1 byte at a time. Alternatively we could request + * printable reports, but I don't feel like implementing that. + */ +static int stty_sball(struct sball *sb) +{ + int mstat; + struct termios term; + term = sb->saved_term; term.c_oflag = 0; term.c_lflag = 0; term.c_cc[VMIN] = 0; @@ -141,43 +203,72 @@ static int stty_sball(int fd) cfsetispeed(&term, B9600); cfsetospeed(&term, B9600); - if(tcsetattr(fd, TCSANOW, &term) == -1) { + if(tcsetattr(sb->fd, TCSAFLUSH, &term) == -1) { perror("sball_open: tcsetattr"); return -1; } + tcflush(sb->fd, TCIOFLUSH); + mstat = sb->saved_mstat | TIOCM_DTR | TIOCM_RTS; + ioctl(sb->fd, TIOCMGET, &mstat); return 0; } -/* Logicad magellan spacemouse: 9600 8n2 CTS/RTS */ -static int stty_mag(int fd) +/* Logicad magellan spacemouse: 9600 8n2 CTS/RTS + * Since the magellan devices don't seem to send any newlines, we can rely on + * canonical mode to feed us nice whole lines at a time. + */ +static int stty_mag(struct sball *sb) { + int mstat; struct termios term; - if(tcgetattr(fd, &term) == -1) { - perror("sball_open: tcgetattr"); - return -1; - } - + term = sb->saved_term; term.c_oflag = 0; - term.c_lflag = 0; + term.c_lflag = ICANON; term.c_cc[VMIN] = 0; - term.c_cc[VTIME] = 1; - - term.c_cflag = CLOCAL | CREAD | CS8 | CSTOPB | HUPCL | CRTSCTS; + term.c_cc[VTIME] = 0; + term.c_cc[VEOF] = 0; + term.c_cc[VEOL] = '\r'; + term.c_cc[VEOL2] = 0; + term.c_cc[VERASE] = 0; + term.c_cc[VKILL] = 0; + + term.c_cflag = CLOCAL | CREAD | CS8 | CSTOPB | HUPCL; +#ifdef CCTS_OFLOW + term.c_cflag |= CCTS_OFLOW; +#elif defined(CRTSCTS) + term.c_cflag |= CRTSCTS; +#endif term.c_iflag = IGNBRK | IGNPAR; cfsetispeed(&term, B9600); cfsetospeed(&term, B9600); - if(tcsetattr(fd, TCSANOW, &term) == -1) { + if(tcsetattr(sb->fd, TCSAFLUSH, &term) == -1) { perror("sball_open: tcsetattr"); return -1; } + tcflush(sb->fd, TCIOFLUSH); + mstat = sb->saved_mstat | TIOCM_DTR | TIOCM_RTS; + ioctl(sb->fd, TIOCMGET, &mstat); return 0; } +static void stty_save(struct sball *sb) +{ + tcgetattr(sb->fd, &sb->saved_term); + ioctl(sb->fd, TIOCMGET, &sb->saved_mstat); +} + +static void stty_restore(struct sball *sb) +{ + tcsetattr(sb->fd, TCSAFLUSH, &sb->saved_term); + tcflush(sb->fd, TCIOFLUSH); + ioctl(sb->fd, TIOCMSET, &sb->saved_mstat); +} + static int proc_input(struct sball *sb) { @@ -190,7 +281,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++; @@ -205,24 +296,10 @@ static int proc_input(struct sball *sb) return 0; } -static void print_keystate(unsigned int keystate) +static int mag_parsepkt(struct sball *sb, int id, char *data, int len) { int i; - printf("keystate: "); - for(i=0; i<12; i++) { - int b = 11 - i; - int hex = b < 10 ? b + '0' : b - 10 + 'a'; - putchar(keystate & (1 << b) ? hex : '-'); - } - putchar('\n'); -} - -static int mag_parsepkt(int id, char *data, int len) -{ - int i, mot[6]; - unsigned int keystate; - /*printf("magellan packet: %c - %s (%d bytes)\n", (char)id, data, len);*/ switch(id) { @@ -232,11 +309,11 @@ static int mag_parsepkt(int id, char *data, int len) return -1; } for(i=0; i<6; i++) { - mot[i] = ((((int)data[0] & 0xf) << 12) | (((int)data[1] & 0xf) << 8) | + sb->mot[i] = ((((int)data[0] & 0xf) << 12) | (((int)data[1] & 0xf) << 8) | (((int)data[2] & 0xf) << 4) | (data[3] & 0xf)) - 0x8000; data += 4; } - printf("motion: T %+4d %+4d %+4d R %+4d %+4d %+4d\n", mot[0], mot[1], mot[2], mot[3], mot[4], mot[5]); + print_state(sb); break; case 'k': @@ -244,8 +321,8 @@ static int mag_parsepkt(int id, char *data, int len) fprintf(stderr, "magellan: invalid keyboard pakcet, expected 3 bytes, got: %d\n", len); return -1; } - keystate = (data[0] & 0xf) | ((data[1] & 0xf) << 4) | (((unsigned int)data[2] & 0xf) << 8); - print_keystate(keystate); + sb->keystate = (data[0] & 0xf) | ((data[1] & 0xf) << 4) | (((unsigned int)data[2] & 0xf) << 8); + print_state(sb); break; case 'e': @@ -264,12 +341,10 @@ 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; char c, *rd, *wr; - short *mot; /* decode data packet, replacing escaped values with the correct ones */ rd = wr = data; @@ -287,6 +362,7 @@ static int sball_parsepkt(int id, char *data, int len) break; case '^': *wr++ = '^'; + break; default: fprintf(stderr, "sball decode: ignoring invalid escape code: %xh\n", (unsigned int)c); } @@ -303,8 +379,17 @@ static int sball_parsepkt(int id, char *data, int len) return -1; } - mot = (short*)(data + 2); /* skip the period */ - printf("motion: T %+6d %+6d %+6d R %+6d %+6d %+6d\n", mot[0], mot[1], mot[2], mot[3], mot[4], mot[5]); +#ifndef SBALL_BIG_ENDIAN + rd = data; + for(i=0; i<6; i++) { + rd += 2; + c = rd[0]; + rd[0] = rd[1]; + rd[1] = c; + } +#endif + memcpy(sb->mot, data + 2, 12); + print_state(sb); break; case 'K': @@ -312,14 +397,46 @@ 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; - print_keystate(keystate); + 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 + */ + sb->keystate = (data[1] & 0xf) | ((data[1] >> 4) & 3) | ((data[0] & 7) << 4) | + ((data[0] & 0x10) >> 1); + print_state(sb); + 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 (actually don't bother) */ + /* + 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 + */ + sb->keystate = (data[1] & 0x3f) | ((data[1] & 0x80) >> 1) | ((data[0] & 0x1f) << 7); + print_state(sb); break; case 'E': fprintf(stderr, "sball: error:"); for(i=0; i 0 ? sz : -1; } +static void print_motion(short *mot) +{ + printf(" T[%+6d %+6d %+6d] R[%+6d %+6d %+6d]", mot[0], mot[1], + mot[2], mot[3], mot[4], mot[5]); +} + +static void print_keystate(unsigned int keystate) +{ + int i; + + for(i=0; i<12; i++) { + int b = 11 - i; + int hex = b < 10 ? b + '0' : b - 10 + 'a'; + putchar(keystate & (1 << b) ? hex : '-'); + } +} + +static void print_state(struct sball *sb) +{ + print_motion(sb->mot); + printf(" B["); + print_keystate(sb->keystate); + printf("]\r"); + fflush(stdout); +}