11 #include <sys/select.h>
14 #define INP_BUF_SZ 256
22 int (*parse)(int, char*, int);
25 static int stty_sball(int fd);
26 static int stty_mag(int fd);
28 static int proc_input(struct sball *sb);
30 static int mag_parsepkt(int id, char *data, int len);
31 static int sball_parsepkt(int id, char *data, int len);
33 static void make_printable(char *buf, int len);
34 static int read_timeout(int fd, char *buf, int bufsz, long tm_usec);
36 struct sball *sball_open(const char *dev)
42 if((fd = open(dev, O_RDWR | O_NOCTTY)) == -1) {
43 fprintf(stderr, "sball_open: failed to open device: %s: %s\n", dev, strerror(errno));
47 if(!(sb = malloc(sizeof *sb))) {
48 fprintf(stderr, "sball_open: failed to allocate sball object\n");
53 if(stty_sball(fd) == -1) {
57 if((sz = read_timeout(fd, buf, sizeof buf - 1, 2000000)) > 0 && memcmp(buf, "\r@1", 3) == 0) {
58 /* we got a response, so it's a spaceball */
59 make_printable(buf, sz);
60 printf("Spaceball detected: %s\n", buf);
63 write(fd, "MSS\r", 4);
65 sb->parse = sball_parsepkt;
69 /* try as a magellan spacemouse */
70 if(stty_mag(fd) == -1) {
75 if((sz = read_timeout(fd, buf, sizeof buf - 1, 250000)) > 0 && buf[0] == 'v') {
76 make_printable(buf, sz);
77 printf("Magellan SpaceMouse detected: %s\n", buf);
79 /* set 3D mode, not-dominant-axis, pass through motion and button packets */
82 sb->parse = mag_parsepkt;
92 void sball_close(struct sball *sb)
98 int sball_fd(struct sball *sb)
103 int sball_read(struct sball *sb)
107 while((sz = read(sb->fd, sb->buf + sb->len, INP_BUF_SZ - sb->len - 1)) > 0) {
112 /* if we fill the input buffer, make a last attempt to parse it, and discard
113 * it so we can receive more
115 if(sb->len >= INP_BUF_SZ) {
123 /* Labtec spaceball: 9600 8n1 XON/XOFF */
124 static int stty_sball(int fd)
128 if(tcgetattr(fd, &term) == -1) {
129 perror("sball_open: tcgetattr");
136 term.c_cc[VTIME] = 1;
138 term.c_cflag = CLOCAL | CREAD | CS8 | HUPCL;
139 term.c_iflag = IGNBRK | IGNPAR | IXON | IXOFF;
141 cfsetispeed(&term, B9600);
142 cfsetospeed(&term, B9600);
144 if(tcsetattr(fd, TCSANOW, &term) == -1) {
145 perror("sball_open: tcsetattr");
152 /* Logicad magellan spacemouse: 9600 8n2 CTS/RTS */
153 static int stty_mag(int fd)
157 if(tcgetattr(fd, &term) == -1) {
158 perror("sball_open: tcgetattr");
165 term.c_cc[VTIME] = 1;
167 term.c_cflag = CLOCAL | CREAD | CS8 | CSTOPB | HUPCL | CRTSCTS;
168 term.c_iflag = IGNBRK | IGNPAR;
170 cfsetispeed(&term, B9600);
171 cfsetospeed(&term, B9600);
173 if(tcsetattr(fd, TCSANOW, &term) == -1) {
174 perror("sball_open: tcsetattr");
182 static int proc_input(struct sball *sb)
185 char *bptr = sb->buf;
186 char *start = sb->buf;
187 char *end = sb->buf + sb->len;
189 /* see if we have a CR in the buffer */
193 sb->parse(*start, start + 1, bptr - start - 1);
200 sz = start - sb->buf;
202 memmove(sb->buf, start, sz);
208 static void print_keystate(unsigned int keystate)
212 printf("keystate: ");
213 for(i=0; i<12; i++) {
215 int hex = b < 10 ? b + '0' : b - 10 + 'a';
216 putchar(keystate & (1 << b) ? hex : '-');
221 static int mag_parsepkt(int id, char *data, int len)
224 unsigned int keystate;
226 /*printf("magellan packet: %c - %s (%d bytes)\n", (char)id, data, len);*/
231 fprintf(stderr, "magellan: invalid data packet, expected 24 bytes, got: %d\n", len);
235 mot[i] = ((((int)data[0] & 0xf) << 12) | (((int)data[1] & 0xf) << 8) |
236 (((int)data[2] & 0xf) << 4) | (data[3] & 0xf)) - 0x8000;
239 printf("motion: T %+4d %+4d %+4d R %+4d %+4d %+4d\n", mot[0], mot[1], mot[2], mot[3], mot[4], mot[5]);
244 fprintf(stderr, "magellan: invalid keyboard pakcet, expected 3 bytes, got: %d\n", len);
247 keystate = (data[0] & 0xf) | ((data[1] & 0xf) << 4) | (((unsigned int)data[2] & 0xf) << 8);
248 print_keystate(keystate);
253 fprintf(stderr, "magellan error: illegal command: %c%c\n", data[1], data[2]);
254 } else if(data[0] == 2) {
255 fprintf(stderr, "magellan error: framing error\n");
257 fprintf(stderr, "magellan error: unknown device error\n");
267 static int sball_parsepkt(int id, char *data, int len)
270 unsigned int keystate;
274 /* decode data packet, replacing escaped values with the correct ones */
276 while(rd < data + len) {
277 if((c = *rd++) == '^') {
280 *wr++ = 0x11; /* XON */
283 *wr++ = 0x13; /* XOFF */
291 fprintf(stderr, "sball decode: ignoring invalid escape code: %xh\n", (unsigned int)c);
297 len = wr - data; /* update the decoded length */
302 fprintf(stderr, "sball: invalid data packet, expected 14 bytes, got: %d\n", len);
306 mot = (short*)(data + 2); /* skip the period */
307 printf("motion: T %+6d %+6d %+6d R %+6d %+6d %+6d\n", mot[0], mot[1], mot[2], mot[3], mot[4], mot[5]);
312 fprintf(stderr, "sball: invalid key packet, expected 2 bytes, got: %d\n", len);
315 keystate = (data[1] & 0x30) >> 4;
316 print_keystate(keystate);
320 fprintf(stderr, "sball: error:");
321 for(i=0; i<len; i++) {
322 if(isprint(data[i])) {
323 fprintf(stderr, " %c", data[i]);
325 fprintf(stderr, " %02xh", (unsigned int)data[i]);
332 fprintf(stderr, "sball: got '%c' packet:", (char)id);
333 for(i=0; i<len; i++) {
334 fprintf(stderr, " %02x", (unsigned int)data[i]);
341 static void make_printable(char *buf, int len)
346 for(i=0; i<len; i++) {
350 while(*buf == '\n' || *buf == '\r') buf++;
358 static int read_timeout(int fd, char *buf, int bufsz, long tm_usec)
362 struct timeval tv0, tv;
365 if(!buf || bufsz <= 0) return -1;
368 gettimeofday(&tv0, 0);
370 while(sz < bufsz && usec > 0) {
371 tv.tv_sec = usec / 1000000;
372 tv.tv_usec = usec % 1000000;
376 if((res = select(fd + 1, &rdset, 0, 0, &tv)) > 0 && FD_ISSET(fd, &rdset)) {
377 sz += read(fd, buf + sz, bufsz - sz);
379 tm_usec = usec = 128000; /* wait 128ms for the rest of the message to appear */
380 gettimeofday(&tv0, 0);
383 if(res == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
386 gettimeofday(&tv, 0);
387 usec = tm_usec - ((tv.tv_sec - tv0.tv_sec) * 1000000 + (tv.tv_usec - tv0.tv_usec));
390 return sz > 0 ? sz : -1;