home *** CD-ROM | disk | FTP | other *** search
- /*
- * OptMouse.c
- *
- * Serial Port Optical Mouse Driver
- *
- * (c) Copyright 1989,1991 J. Edward Hanway
- * This code may be freely redistributed for non-commercial purposes. There
- * is NO WARRANTY on this program. The author assumes no responsibility for
- * any damages resulting from use of this program. You know the drill.
- *
- * Language: SAS/C 5.10a
- *
- * $Id: OptMouse.c,v 1.2 91/04/04 17:41:53 jeh Exp $
- *
- * $Log: OptMouse.c,v $
- * Revision 1.2 91/04/04 17:41:53 jeh
- * Reworked to send two position update messages for each 5-byte packet
- * received from the mouse. Should now update the position about 48 times
- * per second.
- *
- * Added a simple accellerator function.
- *
- * Note: I (jeh) haven't tested this version except to make sure that it
- * doesn't crash the machine. (Long ago I modified my mouse according
- * to the instructions in Hardware.doc.) Thanks to fellow BIXer rjenks
- * for testing this version.
- *
- * Revision 1.1 91/03/25 22:47:18 jeh
- * Initial revision
- *
- * pre-RCS Revision History:
- *
- * Version 1.0 27 May 1989
- * Supports Mouse Systems M2/M3 serial optical mouse
- * Version 1.1 3 June 1989
- * Added support for other device/unit combinations besides serial.device
- * (e.g. siosbx.device) *** NOTE: This feature has not been tested. ***
- */
-
- #define VERSION "1.2"
-
- #include <exec/types.h>
- #include <devices/serial.h>
- #include <devices/input.h>
- #include <devices/inputevent.h>
- #include <devices/keyboard.h>
- #include <libraries/dos.h>
- #include <proto/exec.h>
- #include <proto/dos.h>
- #include <string.h>
- #include <stdlib.h>
-
- #define SAY(x) \
- {if(_Backstdout) {Write(_Backstdout, (x), strlen(x)); Close(_Backstdout);}}
-
- void MemCleanup() {}
-
- /* This stuff is for linking with cback.o (for "load and stay resident" code) */
-
- LONG _stack = 4000; /* stack size */
- char *_procname = "OptMouse";/* process name */
- LONG _priority = 20; /* priority */
- LONG _BackGroundIO = 1; /* requires I/O */
- extern BPTR _Backstdout;
-
- static char *ser_portname = "OptMouse";
-
- /* default device/unit */
- static char *device = SERIALNAME;
- static int unit = 0;
-
- /* default middle button behavior */
- static BOOL mid_is_shift = 0;
-
- /* default accelleration threshold/multiplier */
- static int threshold = 32767;
- static int multiplier = 2;
-
- static struct dev {
- struct MsgPort *port;
- struct IORequest *iob;
- BOOL open;
- } ser = {NULL, NULL, FALSE},
- in = {NULL, NULL, FALSE},
- time = {NULL, NULL, FALSE},
- key = {NULL, NULL, FALSE};
-
- /* shorthand */
- #define SER_IOB ((struct IOExtSer *)(ser.iob))
- #define IN_IOB ((struct IOStdReq *)(in.iob))
- #define TIME_IOB ((struct timerequest *)(time.iob))
- #define KEY_IOB ((struct IOStdReq *)(key.iob))
-
- static signed char b; /* byte read from mouse */
-
- static struct button {
- BOOL left, middle, right;
- } button, last_button = { FALSE, FALSE, FALSE };
-
- static struct InputEvent event = {
- NULL, /* NextEvent */
- IECLASS_RAWMOUSE, /* Class */
- NULL, /* SubClass */
- NULL, /* Code, filled in later */
- NULL, /* Qualifier, filled in later */
- { NULL, NULL }, /* Position, filled in later */
- { 0L, 0L } /* TimeStamp */
- };
-
- /* Matrix for reading key states. All we care about are the
- * shift/ctrl/alt/amiga keys, which are conveniently qrouped in
- * keymatrix[12] in the same order as required for the qualifier
- *
- * The following number, which is the ONLY read length that works for
- * KBD_READMATRIX, was documented NOWHERE! I had to find it by trial and error.
- */
- #define KEY_MATRIX_READ_LENGTH 13
-
- static UBYTE keymatrix[16];
-
- void
- close_dev(struct dev *dev)
- {
- if(dev->open) {
- AbortIO(dev->iob);
- CloseDevice(dev->iob);
- dev->open = FALSE;
- }
- if(dev->iob) {
- DeleteExtIO(dev->iob);
- dev->iob = NULL;
- }
- if(dev->port) {
- DeletePort(dev->port);
- dev->port = NULL;
- }
- }
-
- void
- die(void)
- {
- close_dev(&time);
- close_dev(&in);
- close_dev(&ser);
- close_dev(&key);
- _exit(0);
- }
-
- void
- open_dev(struct dev *dev, char *portname, char *devname, int unit, int size)
- {
- if(!(dev->port = CreatePort(portname, 0)) ||
- !(dev->iob = CreateExtIO(dev->port, size)) ||
- !(dev->open = !OpenDevice(devname, unit, dev->iob, 0L))) {
- SAY("Device error\n");
- die();
- }
- }
-
- /* my_DoIO does everything that DoIO does (I hope) except it allows for a break
- * signal.
- */
- void
- my_DoIO(struct IORequest *iob)
- {
- register LONGBITS signals, sigbit;
-
- /* Lattice/SAS doesn't seem to provide a register-parameter entry to BeginIO,
- * so here's a hack that avoids an annoying 3 line assembly language program.
- * Just don't try to call BeginIO on anything but iob->io_Device.
- */
- #pragma libcall iob->io_Device BeginIO 1e 901
-
- iob->io_Flags |= IOF_QUICK;
- BeginIO(iob);
- if(!(iob->io_Flags & IOF_QUICK)) {
- sigbit = 1L << iob->io_Message.mn_ReplyPort->mp_SigBit;
- signals = Wait(sigbit | SIGBREAKF_CTRL_C);
-
- if(signals & sigbit)
- GetMsg(iob->io_Message.mn_ReplyPort);
-
- if(signals & SIGBREAKF_CTRL_C)
- die();
- }
- }
-
- #undef isspace
-
- int
- isspace(register char c)
- {
- return ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'));
- }
-
- char *
- skiparg(register char *s)
- {
- while(*s && !isspace(*s))
- s++;
- return s;
- }
-
- char *
- skipwhite(register char *s)
- {
- while(*s && isspace(*s))
- s++;
- return s;
- }
-
- void
- update_buttons(void)
- {
- /* Send 0-3 events for button state changes as necessary */
-
- /* Valid time stamp is required for double-clicking
- * to work properly.
- */
- my_DoIO(time.iob);
- event.ie_TimeStamp = TIME_IOB->tr_time;
-
- /* Shift key status is needed for shift-click support */
- my_DoIO(key.iob);
-
- event.ie_X = event.ie_Y = 0;
-
- /* straightforward parts of qualifier: left & right buttons +
- * ctrl/alt/shift keys
- */
- event.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE |
- (button.right ? IEQUALIFIER_RBUTTON : 0) |
- (button.left ? IEQUALIFIER_LEFTBUTTON : 0) |
- keymatrix[12];
-
- /* middle mouse button support */
- if(button.middle) {
- if(mid_is_shift) {
- /* make middle button look like shifted left button */
- event.ie_Qualifier |= IEQUALIFIER_LEFTBUTTON | IEQUALIFIER_LSHIFT;
- } else {
- /* middle button event is ignored by Intuition prior to V36 */
- event.ie_Qualifier |= IEQUALIFIER_MIDBUTTON;
- }
- }
-
- if(button.left ^ last_button.left) {
- event.ie_Code = button.left ? IECODE_LBUTTON : IECODE_LBUTTON | IECODE_UP_PREFIX;
- my_DoIO(in.iob);
- }
-
- if(button.middle ^ last_button.middle) {
- if(mid_is_shift) {
- event.ie_Code = button.middle ? IECODE_LBUTTON : IECODE_LBUTTON | IECODE_UP_PREFIX;
- } else {
- event.ie_Code = button.middle ? IECODE_MBUTTON : IECODE_MBUTTON | IECODE_UP_PREFIX;
- }
- my_DoIO(in.iob);
- }
-
- if(button.right ^ last_button.right) {
- event.ie_Code = button.right ? IECODE_RBUTTON : IECODE_RBUTTON | IECODE_UP_PREFIX;
- my_DoIO(in.iob);
- }
-
- last_button = button;
- }
-
- void
- update_position(WORD dx, WORD dy)
- {
- /* Send one event if the mouse position has changed
- *
- * event.ie_Qualifier is initialized in update_buttons()
- */
-
- if(dx || dy) {
-
- /* Valid time stamp is required for double-clicking
- * to work properly.
- */
- my_DoIO(time.iob);
- event.ie_TimeStamp = TIME_IOB->tr_time;
-
- /* apply accelleration for movements bigger than the threshold */
-
- if((dx > threshold) || (dx < -threshold)) {
- dx *= multiplier;
- }
- if((dy > threshold) || (dy < -threshold)) {
- dy *= multiplier;
- }
-
- event.ie_X = dx;
- event.ie_Y = -dy;
- event.ie_Code = IECODE_NOBUTTON;
-
- my_DoIO(in.iob);
- }
- }
-
- void
- _tinymain(char *s)
- {
- UWORD state = 0;
- WORD dx, dy;
- BOOL kill = 0;
-
- /* I'm playing it fast and loose with parsing here */
- s = skiparg(s);
- s = skipwhite(s);
- while(*s == '-') {
- switch(*(s+1)) {
- case 'a': case 'A': /* accelleration */
- s += 2;
- s += stcd_i(s, &threshold);
- if(*s) {
- s++; /* skip ',' */
- s += stcd_i(s, &multiplier);
- }
- break;
-
- case 'k': case 'K': /* kill */
- kill++;
- break;
-
- case 'm': case 'M': /* mid button is shifted left button */
- mid_is_shift++;
- break;
- }
- s = skiparg(s);
- s = skipwhite(s);
- }
-
- /* device name */
- if(*s) {
- device = s;
- s = skiparg(s);
- if(*s)
- *s++ = '\0';
- s = skipwhite(s);
- }
-
- /* unit */
- if(*s) {
- (void) stcd_i(s, &unit);
- }
-
- if(FindPort(ser_portname)) {
- if(kill) {
- Signal(FindTask(_procname),SIGBREAKF_CTRL_C);
- SAY("Killed\n");
- } else {
- SAY("Already installed\n");
- }
- die();
- }
-
- open_dev(&ser, ser_portname, device, unit, sizeof(struct IOExtSer));
- open_dev(&in, NULL, "input.device", 0L, sizeof(struct IOStdReq));
- open_dev(&time, NULL, TIMERNAME, UNIT_MICROHZ, sizeof(struct timerequest));
- open_dev(&key, NULL, "keyboard.device", 0L, sizeof(struct IOStdReq));
-
- SAY("OptMouse " VERSION " \xA9 Copyright 1989,1991 J. Edward Hanway\n");
-
- /* Set serial port parameters:
- * 1200 baud, 8 bits, no parity, 1 stop bit, no flow control
- */
- SER_IOB->IOSer.io_Command = SDCMD_SETPARAMS;
- SER_IOB->io_Baud = 1200;
- SER_IOB->io_ReadLen = 8;
- SER_IOB->io_StopBits = 1;
- SER_IOB->io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;
- my_DoIO(ser.iob);
-
- /* Since each IORequest will be used for only one type of command,
- * lots of invariant fields can be filled in here
- */
- KEY_IOB->io_Command = KBD_READMATRIX;
- KEY_IOB->io_Data = (APTR) keymatrix;
- KEY_IOB->io_Length = KEY_MATRIX_READ_LENGTH;
-
- TIME_IOB->tr_node.io_Command = TR_GETSYSTIME;
-
- IN_IOB->io_Command = IND_WRITEEVENT;
- IN_IOB->io_Flags = 0;
- IN_IOB->io_Length = sizeof(struct InputEvent);
- IN_IOB->io_Data = (APTR) &event;
-
- SER_IOB->IOSer.io_Command = CMD_READ;
- SER_IOB->IOSer.io_Length = 1;
- SER_IOB->IOSer.io_Data = (APTR) &b;
-
- for(;;) {
- my_DoIO(ser.iob);
-
- /* The Mouse Systems mouse sends back a five byte code
- * as follows:
- *
- * byte 0: (sync byte) %10000XXX (XXX = button states)
- * byte 1: X delta
- * byte 2: Y delta
- * byte 3: X delta
- * byte 4: Y delta
- *
- * Note that bytes 3 & 4 specify the mouse movement since
- * bytes 1 & 2 were sent. Thus a single 5 byte message
- * updates the mouse position twice, although it
- * only updates switch states once.
- */
-
- switch(state) {
- case 0:
- if((b & 0xf8) == 0x80) {
- button.right = !(b & (1 << 0));
- button.middle = !(b & (1 << 1));
- button.left = !(b & (1 << 2));
- update_buttons();
- state++;
- }
- break;
-
- case 1:
- dx = b;
- state++;
- break;
-
- case 2:
- dy = b;
- update_position(dx, dy);
- state++;
- break;
-
- case 3:
- dx = b;
- state++;
- break;
-
- case 4:
- dy = b;
- update_position(dx, dy);
- state = 0;
- break;
- }
- }
- }
-