blob: e5e474ea71aa6ebbaf74cad47aef5b83a28fdc32 [file] [log] [blame]
/* simple driver for serial mouse */
/* Andrew Haylett, 17th June 1993 */
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include "mouse.h"
#define DEF_MDEV "/dev/mouse"
#define DEF_MTYPE P_MS
#define DEF_MBAUD 1200
#define DEF_MSAMPLE 100
#define DEF_MDELTA 25
#define DEF_MACCEL 2
/* thse settings may be altered by the user */
static char *mdev = DEF_MDEV; /* mouse device */
static mouse_type mtype = DEF_MTYPE; /* mouse type */
static int mbaud = DEF_MBAUD; /* mouse device baud rate */
static int msample = DEF_MSAMPLE; /* sample rate for Logitech mice */
static int mdelta = DEF_MDELTA; /* x+y movements more than mdelta pixels..*/
static int maccel = DEF_MACCEL; /* ..are multiplied by maccel. */
int ms_copy_button = MS_BUTLEFT,
ms_paste_button = MS_BUTRIGHT;
static char *progname;
static void
ms_usage()
{
printf(
"Selection version 1.5, 17th June 1993\n"
"Usage: %s [-a accel] [-b baud-rate] [-c l|m|r] [-d delta]\n"
" [-m mouse-device] [-p l|m|r] [-s sample-rate] [-t mouse-type]\n\n", progname);
printf(
" -a accel sets the acceleration (default %d)\n"
" -b baud-rate sets the baud rate (default %d)\n"
" -c l|m|r sets the copy button (default `l')\n"
" -d delta sets the delta value (default %d)\n"
" -m mouse-device sets mouse device (default `%s')\n"
" -p l|m|r sets the paste button (default `r')\n"
" -s sample-rate sets the sample rate (default %d)\n"
" -t mouse-type sets mouse type (default `ms')\n"
" Microsoft = `ms', Mouse Systems Corp = `msc',\n"
" MM Series = `mm', Logitech = `logi', BusMouse = `bm',\n"
" MSC 3-bytes = `sun', PS/2 = `ps2')\n",
DEF_MACCEL, DEF_MBAUD, DEF_MDELTA, DEF_MDEV, DEF_MSAMPLE);
exit(1);
}
extern int optind;
extern char *optarg;
void
ms_params(int argc, char *argv[])
{
int opt;
progname = (rindex(argv[0], '/')) ? rindex(argv[0], '/') + 1 : argv[0];
while ((opt = getopt(argc, argv, "a:b:c:d:m:p:s:t:")) != -1)
{
switch (opt)
{
case 'a':
maccel = atoi(optarg);
if (maccel < 2)
ms_usage();
break;
case 'b':
mbaud = atoi(optarg);
break;
case 'c':
switch (*optarg)
{
case 'l': ms_copy_button = MS_BUTLEFT; break;
case 'm': ms_copy_button = MS_BUTMIDDLE; break;
case 'r': ms_copy_button = MS_BUTRIGHT; break;
default: ms_usage(); break;
}
break;
case 'd':
mdelta = atoi(optarg);
if (mdelta < 2)
ms_usage();
break;
case 'm':
mdev = optarg;
break;
case 'p':
switch (*optarg)
{
case 'l': ms_paste_button = MS_BUTLEFT; break;
case 'm': ms_paste_button = MS_BUTMIDDLE; break;
case 'r': ms_paste_button = MS_BUTRIGHT; break;
default: ms_usage(); break;
}
break;
case 's':
msample = atoi(optarg);
break;
case 't':
if (!strcmp(optarg, "ms"))
mtype = P_MS;
else if (!strcmp(optarg, "sun"))
mtype = P_SUN;
else if (!strcmp(optarg, "msc"))
mtype = P_MSC;
else if (!strcmp(optarg, "mm"))
mtype = P_MM;
else if (!strcmp(optarg, "logi"))
mtype = P_LOGI;
else if (!strcmp(optarg, "bm"))
mtype = P_BM;
else if (!strcmp(optarg, "ps2"))
mtype = P_PS2;
else
ms_usage();
break;
default:
ms_usage();
break;
}
}
}
#define limit(n,l,u) n = ((n) < (l) ? (l) : ((n) > (u) ? (u) : (n)))
static int mx = 32767;
static int my = 32767;
static int x, y;
static int mfd = -1;
static const unsigned short cflag[NR_TYPES] =
{
(CS7 | CREAD | CLOCAL | HUPCL ), /* MicroSoft */
(CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* MouseSystems 3 */
(CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* MouseSystems 5 */
(CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ), /* MMSeries */
(CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* Logitech */
0, /* BusMouse */
0 /* PS/2 */
};
static const unsigned char proto[NR_TYPES][5] =
{
/* hd_mask hd_id dp_mask dp_id nobytes */
{ 0x40, 0x40, 0x40, 0x00, 3 }, /* MicroSoft */
{ 0xf8, 0x80, 0x00, 0x00, 3 }, /* MouseSystems 3 (Sun) */
{ 0xf8, 0x80, 0x00, 0x00, 5 }, /* MouseSystems 5 */
{ 0xe0, 0x80, 0x80, 0x00, 3 }, /* MMSeries */
{ 0xe0, 0x80, 0x80, 0x00, 3 }, /* Logitech */
{ 0xf8, 0x80, 0x00, 0x00, 5 }, /* BusMouse */
{ 0xcc, 0x00, 0x00, 0x00, 3 } /* PS/2 */
};
static void
ms_setspeed(const int old, const int new,
const unsigned short c_cflag)
{
struct termios tty;
char *c;
tcgetattr(mfd, &tty);
tty.c_iflag = IGNBRK | IGNPAR;
tty.c_oflag = 0;
tty.c_lflag = 0;
tty.c_line = 0;
tty.c_cc[VTIME] = 0;
tty.c_cc[VMIN] = 1;
switch (old)
{
case 9600: tty.c_cflag = c_cflag | B9600; break;
case 4800: tty.c_cflag = c_cflag | B4800; break;
case 2400: tty.c_cflag = c_cflag | B2400; break;
case 1200:
default: tty.c_cflag = c_cflag | B1200; break;
}
tcsetattr(mfd, TCSAFLUSH, &tty);
switch (new)
{
case 9600: c = "*q"; tty.c_cflag = c_cflag | B9600; break;
case 4800: c = "*p"; tty.c_cflag = c_cflag | B4800; break;
case 2400: c = "*o"; tty.c_cflag = c_cflag | B2400; break;
case 1200:
default: c = "*n"; tty.c_cflag = c_cflag | B1200; break;
}
write(mfd, c, 2);
usleep(100000);
tcsetattr(mfd, TCSAFLUSH, &tty);
}
int
ms_init(const int maxx, const int maxy)
{
if ((mfd = open(mdev, O_RDWR)) < 0)
{
char buf[32];
sprintf(buf, "ms_init: %s", mdev);
perror(buf);
return -1;
}
if (mtype != P_BM && mtype != P_PS2)
{
ms_setspeed(9600, mbaud, cflag[mtype]);
ms_setspeed(4800, mbaud, cflag[mtype]);
ms_setspeed(2400, mbaud, cflag[mtype]);
ms_setspeed(1200, mbaud, cflag[mtype]);
if (mtype == P_LOGI)
{
write(mfd, "S", 1);
ms_setspeed(mbaud, mbaud, cflag[P_MM]);
}
if (msample <= 0) write(mfd, "O", 1);
else if (msample <= 15) write(mfd, "J", 1);
else if (msample <= 27) write(mfd, "K", 1);
else if (msample <= 42) write(mfd, "L", 1);
else if (msample <= 60) write(mfd, "R", 1);
else if (msample <= 85) write(mfd, "M", 1);
else if (msample <= 125) write(mfd, "Q", 1);
else write(mfd, "N", 1);
}
mx = maxx;
my = maxy;
x = mx / 2;
y = my / 2;
return 0;
}
int
get_ms_event(struct ms_event *ev)
{
unsigned char buf[5];
char dx, dy;
int i, acc;
if (mfd == -1)
return -1;
if (mtype != P_BM)
{
if (read(mfd, &buf[0], 1) != 1)
return -1;
restart:
/* find a header packet */
while ((buf[0] & proto[mtype][0]) != proto[mtype][1])
{
if (read(mfd, &buf[0], 1) != 1)
{
perror("get_ms_event: read");
return -1;
}
}
/* read in the rest of the packet */
for (i = 1; i < proto[mtype][4]; ++i)
{
if (read(mfd, &buf[i], 1) != 1)
{
perror("get_ms_event: read");
return -1;
}
/* check whether it's a data packet */
if (mtype != P_PS2 && ((buf[i] & proto[mtype][2]) != proto[mtype][3]
|| buf[i] == 0x80))
goto restart;
}
}
else /* bus mouse */
{
while ((i = read(mfd, buf, 3)) != 3 && errno == EAGAIN)
usleep(40000);
if (i != 3)
{
perror("get_ms_event: read");
return -1;
}
}
/* construct the event */
switch (mtype)
{
case P_MS: /* Microsoft */
default:
ev->ev_butstate = ((buf[0] & 0x20) >> 3) | ((buf[0] & 0x10) >> 4);
dx = (char)(((buf[0] & 0x03) << 6) | (buf[1] & 0x3F));
dy = (char)(((buf[0] & 0x0C) << 4) | (buf[2] & 0x3F));
break;
case P_SUN: /* Mouse Systems 3 byte as used in Sun workstations */
ev->ev_butstate = (~buf[0]) & 0x07;
dx = (char)(buf[1]);
dy = -(char)(buf[2]);
break;
case P_MSC: /* Mouse Systems Corp (5 bytes, PC) */
ev->ev_butstate = (~buf[0]) & 0x07;
dx = (char)(buf[1]) + (char)(buf[3]);
dy = - ((char)(buf[2]) + (char)(buf[4]));
break;
case P_MM: /* MM Series */
case P_LOGI: /* Logitech */
ev->ev_butstate = buf[0] & 0x07;
dx = (buf[0] & 0x10) ? buf[1] : - buf[1];
dy = (buf[0] & 0x08) ? - buf[2] : buf[2];
break;
case P_BM: /* BusMouse */
ev->ev_butstate = (~buf[0]) & 0x07;
dx = (char)buf[1];
dy = - (char)buf[2];
break;
case P_PS2: /* PS/2 Mouse */
ev->ev_butstate = 0;
if (buf[0] & 0x01)
ev->ev_butstate |= MS_BUTLEFT;
if (buf[0] & 0x02)
ev->ev_butstate |= MS_BUTRIGHT;
dx = (buf[0] & 0x10) ? buf[1]-256 : buf[1];
dy = - ((buf[0] & 0x20) ? buf[2]-256 : buf[2]);
break;
}
acc = (abs(ev->ev_dx) + abs(ev->ev_dy) > mdelta) ? maccel : 1;
ev->ev_dx = dx * acc;
ev->ev_dy = dy * acc;
x += ev->ev_dx;
y += ev->ev_dy;
limit(x, 0, mx);
limit(y, 0, my);
ev->ev_x = x;
ev->ev_y = y;
if (dx || dy)
{
if (ev->ev_butstate)
ev->ev_code = MS_DRAG;
else
ev->ev_code = MS_MOVE;
}
else
{
if (ev->ev_butstate)
ev->ev_code = MS_BUTDOWN;
else
ev->ev_code = MS_BUTUP;
}
return 0;
}