/* * OptMouse.c * * Serial Port Optical Mouse Driver * * (c) Copyright 1989 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: Lattice C 5.02 * * 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.1" #include #include #include #include #include #include #include #include #include #include #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; 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 GODDAMN_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 5.02 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)) { signals = Wait((sigbit = (1L << iob->io_Message.mn_ReplyPort->mp_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 _tinymain(char *s) { UWORD state = 0; WORD dx, dy; BOOL mid_is_shift = 0; 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 'k': case 'K': kill++; break; case 'm': case 'M': 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 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 = GODDAMN_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; while(TRUE) { my_DoIO(ser.iob); switch(state) { case 0: if((b & 0xf8) == 0x80) { button.right = !(b & (1 << 0)); button.middle = !(b & (1 << 1)); button.left = !(b & (1 << 2)); state++; } break; case 1: dx = b; state++; break; case 2: dy = b; state++; break; case 3: dx += b; state++; break; case 4: dy += b; /* Now compose the events to be sent to input.device. * Currently, 0-4 events (movement, up to 3 button * state changes) are sent. /* 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 = dx; event.ie_Y = -dy; event.ie_Code = IECODE_NOBUTTON; event.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE | (button.right ? IEQUALIFIER_RBUTTON : 0) | ((button.middle && !mid_is_shift) ? IEQUALIFIER_MIDBUTTON : 0) | (button.left ? IEQUALIFIER_LEFTBUTTON : 0) | keymatrix[12] | ((button.middle && mid_is_shift) ? (IEQUALIFIER_LEFTBUTTON | IEQUALIFIER_LSHIFT) : 0); if(dx || dy) my_DoIO(in.iob); event.ie_X = event.ie_Y = 0; 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) { event.ie_Code = mid_is_shift ? (button.middle ? IECODE_LBUTTON : IECODE_LBUTTON | IECODE_UP_PREFIX) : (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; state = 0; break; } } }