/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Linux console internal mouse driver for Microsoft mouse. * * By George Foot. * * See readme.txt for copyright information. */ #include #include #include #include #include #include #include "allegro.h" #include "allegro/internal/aintern.h" #include "allegro/platform/aintunix.h" #include "linalleg.h" static int intellimouse; static int packet_size; /* processor: * Processes the first packet in the buffer, if any, returning the number * of bytes eaten. * * Microsoft protocol has only seven data bits. The top bit is set in * the first byte of the packet, clear in the other two. The next two * bits in the header are the button state. The next two are the top * bits of the Y offset (second data byte), and the last two are the * top bits of the X offset (first data byte). */ static int processor (unsigned char *buf, int buf_size) { int r, l, m, x, y, z; if (buf_size < packet_size) return 0; /* not enough data, spit it out for now */ /* if packet is invalid, just eat it */ if (!(buf[0] & 0x40)) return 1; /* invalid byte */ if (buf[1] & 0x40) return 1; /* first data byte is actually a header */ if (buf[2] & 0x40) return 2; /* second data byte is actually a header */ /* packet is valid, decode the data */ l = !!(buf[0] & 0x20); r = !!(buf[0] & 0x10); if (intellimouse) { m = !!(buf[3] & 0x10); z = (buf[3] & 0x0f); if (z) z = (z-7) >> 3; } else { m = 0; z = 0; } x = (signed char) (((buf[0] & 0x03) << 6) | (buf[1] & 0x3F)); y = -(signed char) (((buf[0] & 0x0C) << 4) | (buf[2] & 0x3F)); __al_linux_mouse_handler(x, y, z, l+(r<<1)+(m<<2)); return packet_size; /* yum */ } /* analyse_data: * Analyses the given data, returning 0 if it is unparsable, nonzero * if there's a reasonable chance that this driver can work with that * data. */ static int analyse_data (AL_CONST char *buffer, int size) { int pos = 0; int packets = 0, errors = 0; int step = 0; for (pos = 0; pos < size; pos++) switch (step) { case 3: packets++; step = 0; case 0: if (!(buffer[pos] & 0x40)) { errors++; } else { step++; } break; case 1: case 2: if (buffer[pos] & 0x40) { errors++; step = 0; pos--; } else { step++; } break; } return (errors <= 5) || (errors < size / 20); /* 5% error allowance */ } static INTERNAL_MOUSE_DRIVER intdrv = { -1, processor, 2 }; /* sync_mouse: * To find the start of a packet, we just read all the data that's * waiting. This isn't a particularly good way, obviously. :) */ static void sync_mouse (int fd) { fd_set set; int result; struct timeval tv; char bitbucket; do { FD_ZERO (&set); FD_SET (fd, &set); tv.tv_sec = tv.tv_usec = 0; result = select (FD_SETSIZE, &set, NULL, NULL, &tv); if (result > 0) read (fd, &bitbucket, 1); } while (result > 0); } /* mouse_init: * Here we open the mouse device, initialise anything that needs it, * and chain to the framework init routine. */ static int mouse_init (void) { char tmp1[128], tmp2[128], tmp3[128]; AL_CONST char *udevice; struct termios t; /* Find the device filename */ udevice = get_config_string (uconvert_ascii ("mouse", tmp1), uconvert_ascii ("mouse_device", tmp2), uconvert_ascii ("/dev/mouse", tmp3)); /* Open mouse device. Devices are cool. */ intdrv.device = open (uconvert_toascii (udevice, tmp1), O_RDONLY | O_NONBLOCK); if (intdrv.device < 0) { uszprintf (allegro_error, ALLEGRO_ERROR_SIZE, get_config_text ("Unable to open %s: %s"), udevice, ustrerror (errno)); return -1; } /* Set parameters */ tcgetattr (intdrv.device, &t); t.c_iflag = IGNBRK | IGNPAR; t.c_oflag = t.c_lflag = t.c_line = 0; t.c_cflag = CS7 | CREAD | CLOCAL | HUPCL | B1200; tcsetattr (intdrv.device, TCSAFLUSH, &t); /* Discard any garbage, so the next thing we read is a packet header */ sync_mouse (intdrv.device); return __al_linux_mouse_init (&intdrv); } /* ms_mouse_init: * Initialisation for vanilla MS mouse. */ static int ms_mouse_init (void) { intellimouse = FALSE; packet_size = 3; intdrv.num_buttons = 2; return mouse_init (); } /* msi_mouse_init: * Initialisation for MS mouse with Intellimouse extension. */ static int ims_mouse_init (void) { intellimouse = TRUE; packet_size = 4; intdrv.num_buttons = 3; return mouse_init (); } /* mouse_exit: * Chain to the framework, then uninitialise things. */ static void mouse_exit (void) { __al_linux_mouse_exit(); close (intdrv.device); } MOUSE_DRIVER mousedrv_linux_ms = { MOUSEDRV_LINUX_MS, empty_string, empty_string, "Linux MS mouse", ms_mouse_init, mouse_exit, NULL, /* poll() */ NULL, /* timer_poll() */ __al_linux_mouse_position, __al_linux_mouse_set_range, __al_linux_mouse_set_speed, __al_linux_mouse_get_mickeys, analyse_data, NULL, /* enable_hardware_cursor */ NULL }; MOUSE_DRIVER mousedrv_linux_ims = { MOUSEDRV_LINUX_IMS, empty_string, empty_string, "Linux MS Intellimouse", ims_mouse_init, mouse_exit, NULL, /* poll() */ NULL, /* timer_poll() */ __al_linux_mouse_position, __al_linux_mouse_set_range, __al_linux_mouse_set_speed, __al_linux_mouse_get_mickeys, analyse_data, NULL, /* enable_hardware_cursor */ NULL };