home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Micro R&D 1
/
MicroRD-CD-ROM-Vol1-1994.iso
/
hardware
/
drivr
/
optmouse.lzh
/
OptMouse12
/
OptMouse.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-04-04
|
10KB
|
447 lines
/*
* 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;
}
}
}