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 >
C/C++ Source or Header  |  1991-04-04  |  10KB  |  447 lines

  1. /*
  2.  * OptMouse.c
  3.  *
  4.  * Serial Port Optical Mouse Driver
  5.  *
  6.  * (c) Copyright 1989,1991 J. Edward Hanway
  7.  * This code may be freely redistributed for non-commercial purposes. There
  8.  * is NO WARRANTY on this program. The author assumes no responsibility for
  9.  * any damages resulting from use of this program. You know the drill.
  10.  *
  11.  * Language: SAS/C 5.10a
  12.  *
  13.  * $Id: OptMouse.c,v 1.2 91/04/04 17:41:53 jeh Exp $
  14.  *
  15.  * $Log:    OptMouse.c,v $
  16.  * Revision 1.2  91/04/04  17:41:53  jeh
  17.  * Reworked to send two position update messages for each 5-byte packet
  18.  * received from the mouse.  Should now update the position about 48 times
  19.  * per second.
  20.  * 
  21.  * Added a simple accellerator function.
  22.  * 
  23.  * Note: I (jeh) haven't tested this version except to make sure that it
  24.  * doesn't crash the machine.  (Long ago I modified my mouse according
  25.  * to the instructions in Hardware.doc.)  Thanks to fellow BIXer rjenks
  26.  * for testing this version.
  27.  * 
  28.  * Revision 1.1  91/03/25  22:47:18  jeh
  29.  * Initial revision
  30.  * 
  31.  * pre-RCS Revision History:
  32.  *
  33.  * Version 1.0    27 May 1989
  34.  *    Supports Mouse Systems M2/M3 serial optical mouse
  35.  * Version 1.1  3 June 1989
  36.  *    Added support for other device/unit combinations besides serial.device
  37.  *    (e.g. siosbx.device) *** NOTE: This feature has not been tested. ***
  38.  */
  39.  
  40. #define VERSION "1.2"
  41.  
  42. #include <exec/types.h>
  43. #include <devices/serial.h>
  44. #include <devices/input.h>
  45. #include <devices/inputevent.h>
  46. #include <devices/keyboard.h>
  47. #include <libraries/dos.h>
  48. #include <proto/exec.h>
  49. #include <proto/dos.h>
  50. #include <string.h>
  51. #include <stdlib.h>
  52.  
  53. #define SAY(x) \
  54. {if(_Backstdout) {Write(_Backstdout, (x), strlen(x)); Close(_Backstdout);}}
  55.  
  56. void MemCleanup() {}
  57.  
  58. /* This stuff is for linking with cback.o (for "load and stay resident" code) */
  59.  
  60. LONG        _stack = 4000;        /* stack size */
  61. char        *_procname = "OptMouse";/* process name */
  62. LONG        _priority = 20;        /* priority */
  63. LONG        _BackGroundIO = 1;    /* requires I/O */
  64. extern    BPTR    _Backstdout;
  65.  
  66. static char    *ser_portname = "OptMouse";
  67.     
  68. /* default device/unit */
  69. static char    *device = SERIALNAME;
  70. static int    unit = 0;
  71.  
  72. /* default middle button behavior */
  73. static BOOL    mid_is_shift = 0;
  74.  
  75. /* default accelleration threshold/multiplier */
  76. static int    threshold = 32767;
  77. static int    multiplier = 2;
  78.  
  79. static struct dev {
  80.     struct MsgPort        *port;
  81.     struct IORequest    *iob;
  82.     BOOL            open;
  83. }    ser = {NULL, NULL, FALSE},
  84.     in = {NULL, NULL, FALSE},
  85.     time = {NULL, NULL, FALSE},
  86.     key = {NULL, NULL, FALSE};
  87.     
  88. /* shorthand */
  89. #define SER_IOB        ((struct IOExtSer *)(ser.iob))
  90. #define IN_IOB        ((struct IOStdReq *)(in.iob))
  91. #define TIME_IOB    ((struct timerequest *)(time.iob))
  92. #define KEY_IOB        ((struct IOStdReq *)(key.iob))
  93.  
  94. static signed char    b;        /* byte read from mouse */
  95.  
  96. static struct button {
  97.     BOOL left, middle, right;
  98. }    button, last_button = { FALSE, FALSE, FALSE };
  99.  
  100. static struct InputEvent event = {
  101.     NULL,                /* NextEvent */
  102.     IECLASS_RAWMOUSE,        /* Class */
  103.     NULL,                /* SubClass */
  104.     NULL,                /* Code, filled in later */
  105.     NULL,                /* Qualifier, filled in later */
  106.     { NULL, NULL },            /* Position, filled in later */
  107.     { 0L, 0L }            /* TimeStamp */    
  108. };
  109.  
  110. /* Matrix for reading key states. All we care about are the 
  111.  * shift/ctrl/alt/amiga keys, which are conveniently qrouped in
  112.  * keymatrix[12] in the same order as required for the qualifier
  113.  *
  114.  * The following number, which is the ONLY read length that works for
  115.  * KBD_READMATRIX, was documented NOWHERE! I had to find it by trial and error.
  116.  */
  117. #define KEY_MATRIX_READ_LENGTH    13
  118.  
  119. static UBYTE keymatrix[16];
  120.  
  121. void
  122. close_dev(struct dev *dev)
  123. {
  124.     if(dev->open) {
  125.         AbortIO(dev->iob);
  126.         CloseDevice(dev->iob);
  127.         dev->open = FALSE;
  128.     }
  129.     if(dev->iob) {
  130.         DeleteExtIO(dev->iob);
  131.         dev->iob = NULL;
  132.     }
  133.     if(dev->port) {
  134.         DeletePort(dev->port);
  135.         dev->port = NULL;
  136.     }
  137. }
  138.  
  139. void
  140. die(void)
  141. {
  142.     close_dev(&time);
  143.     close_dev(&in);
  144.     close_dev(&ser);
  145.     close_dev(&key);
  146.     _exit(0);
  147. }
  148.  
  149. void
  150. open_dev(struct dev *dev, char *portname, char *devname, int unit, int size)
  151.     if(!(dev->port = CreatePort(portname, 0)) ||
  152.        !(dev->iob = CreateExtIO(dev->port, size)) ||
  153.        !(dev->open = !OpenDevice(devname, unit, dev->iob, 0L))) {
  154.         SAY("Device error\n");
  155.         die();
  156.     }
  157. }
  158.  
  159. /* my_DoIO does everything that DoIO does (I hope) except it allows for a break
  160.  * signal.
  161.  */
  162. void
  163. my_DoIO(struct IORequest *iob)
  164. {
  165.     register LONGBITS signals, sigbit;
  166.  
  167. /* Lattice/SAS doesn't seem to provide a register-parameter entry to BeginIO,
  168.  * so here's a hack that avoids an annoying 3 line assembly language program.
  169.  * Just don't try to call BeginIO on anything but iob->io_Device.
  170.  */
  171. #pragma libcall iob->io_Device BeginIO 1e 901
  172.  
  173.     iob->io_Flags |= IOF_QUICK;
  174.     BeginIO(iob);
  175.     if(!(iob->io_Flags & IOF_QUICK)) {
  176.         sigbit = 1L << iob->io_Message.mn_ReplyPort->mp_SigBit;
  177.         signals = Wait(sigbit | SIGBREAKF_CTRL_C);
  178.         
  179.         if(signals & sigbit)
  180.             GetMsg(iob->io_Message.mn_ReplyPort);
  181.  
  182.         if(signals & SIGBREAKF_CTRL_C)
  183.             die();
  184.     }
  185. }
  186.  
  187. #undef isspace
  188.  
  189. int
  190. isspace(register char c)
  191. {
  192.     return ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'));
  193. }
  194.  
  195. char *
  196. skiparg(register char *s)
  197. {
  198.     while(*s && !isspace(*s))
  199.         s++;
  200.     return s;
  201. }
  202.  
  203. char *
  204. skipwhite(register char *s)
  205. {
  206.     while(*s && isspace(*s))
  207.         s++;
  208.     return s;
  209. }
  210.  
  211. void
  212. update_buttons(void)
  213. {
  214.     /* Send 0-3 events for button state changes as necessary */
  215.     
  216.     /* Valid time stamp is required for double-clicking
  217.      * to work properly.
  218.      */
  219.     my_DoIO(time.iob);
  220.     event.ie_TimeStamp = TIME_IOB->tr_time;
  221.  
  222.     /* Shift key status is needed for shift-click support */
  223.     my_DoIO(key.iob);
  224.  
  225.     event.ie_X = event.ie_Y = 0;
  226.  
  227.     /* straightforward parts of qualifier: left & right buttons +
  228.      * ctrl/alt/shift keys
  229.      */
  230.     event.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE |
  231.                  (button.right ? IEQUALIFIER_RBUTTON : 0) |
  232.                  (button.left ? IEQUALIFIER_LEFTBUTTON : 0) |
  233.                  keymatrix[12];
  234.  
  235.     /* middle mouse button support */
  236.     if(button.middle) {
  237.         if(mid_is_shift) {
  238.             /* make middle button look like shifted left button */
  239.             event.ie_Qualifier |= IEQUALIFIER_LEFTBUTTON | IEQUALIFIER_LSHIFT;
  240.         } else {
  241.             /* middle button event is ignored by Intuition prior to V36 */
  242.             event.ie_Qualifier |= IEQUALIFIER_MIDBUTTON;
  243.         }
  244.     }
  245.  
  246.     if(button.left ^ last_button.left) {
  247.         event.ie_Code = button.left ? IECODE_LBUTTON : IECODE_LBUTTON | IECODE_UP_PREFIX;
  248.         my_DoIO(in.iob);
  249.     }
  250.  
  251.     if(button.middle ^ last_button.middle) {
  252.         if(mid_is_shift) {
  253.             event.ie_Code = button.middle ? IECODE_LBUTTON : IECODE_LBUTTON | IECODE_UP_PREFIX;
  254.         } else {
  255.             event.ie_Code = button.middle ? IECODE_MBUTTON : IECODE_MBUTTON | IECODE_UP_PREFIX;
  256.         }
  257.         my_DoIO(in.iob);
  258.     }
  259.  
  260.     if(button.right ^ last_button.right) {
  261.         event.ie_Code = button.right ? IECODE_RBUTTON : IECODE_RBUTTON | IECODE_UP_PREFIX;
  262.         my_DoIO(in.iob);
  263.     }
  264.  
  265.     last_button = button;
  266. }
  267.  
  268. void
  269. update_position(WORD dx, WORD dy)
  270. {
  271.     /* Send one event if the mouse position has changed
  272.      *
  273.      * event.ie_Qualifier is initialized in update_buttons()
  274.      */
  275.  
  276.     if(dx || dy) {
  277.             
  278.         /* Valid time stamp is required for double-clicking
  279.          * to work properly.
  280.          */
  281.         my_DoIO(time.iob);
  282.         event.ie_TimeStamp = TIME_IOB->tr_time;
  283.         
  284.         /* apply accelleration for movements bigger than the threshold */
  285.         
  286.         if((dx > threshold) || (dx < -threshold)) {
  287.             dx *= multiplier;
  288.         } 
  289.         if((dy > threshold) || (dy < -threshold)) {
  290.             dy *= multiplier;
  291.         } 
  292.  
  293.         event.ie_X = dx;
  294.         event.ie_Y = -dy;
  295.         event.ie_Code = IECODE_NOBUTTON;
  296.  
  297.         my_DoIO(in.iob);
  298.     }
  299. }
  300.  
  301. void
  302. _tinymain(char *s)
  303. {
  304.     UWORD        state = 0;
  305.     WORD        dx, dy;
  306.     BOOL        kill = 0;
  307.  
  308.     /* I'm playing it fast and loose with parsing here */
  309.     s = skiparg(s);
  310.     s = skipwhite(s);
  311.     while(*s == '-') {
  312.         switch(*(s+1)) {
  313.         case 'a': case 'A':    /* accelleration */
  314.             s += 2;
  315.             s += stcd_i(s, &threshold);
  316.             if(*s) {
  317.                 s++; /* skip ',' */
  318.                 s += stcd_i(s, &multiplier);
  319.             }
  320.             break;
  321.             
  322.         case 'k': case 'K':    /* kill */
  323.             kill++;
  324.             break;
  325.         
  326.         case 'm': case 'M':    /* mid button is shifted left button */
  327.             mid_is_shift++;
  328.             break;
  329.         }
  330.         s = skiparg(s);
  331.         s = skipwhite(s);
  332.     }
  333.  
  334.     /* device name */
  335.     if(*s) {
  336.         device = s;
  337.         s = skiparg(s);
  338.         if(*s)
  339.             *s++ = '\0';
  340.         s = skipwhite(s);
  341.     }
  342.  
  343.     /* unit */
  344.     if(*s) {
  345.         (void) stcd_i(s, &unit);
  346.     }
  347.  
  348.     if(FindPort(ser_portname)) {
  349.         if(kill) {
  350.             Signal(FindTask(_procname),SIGBREAKF_CTRL_C);
  351.             SAY("Killed\n");
  352.         } else {
  353.             SAY("Already installed\n");
  354.         }
  355.         die();
  356.     }
  357.  
  358.     open_dev(&ser, ser_portname, device, unit, sizeof(struct IOExtSer));
  359.     open_dev(&in, NULL, "input.device", 0L, sizeof(struct IOStdReq));
  360.     open_dev(&time, NULL, TIMERNAME, UNIT_MICROHZ, sizeof(struct timerequest));
  361.     open_dev(&key, NULL, "keyboard.device", 0L, sizeof(struct IOStdReq));
  362.  
  363.     SAY("OptMouse " VERSION " \xA9 Copyright 1989,1991 J. Edward Hanway\n");
  364.  
  365.     /* Set serial port parameters:
  366.      * 1200 baud, 8 bits, no parity, 1 stop bit, no flow control
  367.      */
  368.     SER_IOB->IOSer.io_Command = SDCMD_SETPARAMS;
  369.     SER_IOB->io_Baud = 1200;
  370.     SER_IOB->io_ReadLen = 8;
  371.     SER_IOB->io_StopBits = 1;
  372.     SER_IOB->io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;
  373.     my_DoIO(ser.iob);
  374.  
  375.     /* Since each IORequest will be used for only one type of command,
  376.      * lots of invariant fields can be filled in here
  377.      */
  378.     KEY_IOB->io_Command = KBD_READMATRIX;
  379.     KEY_IOB->io_Data = (APTR) keymatrix;
  380.     KEY_IOB->io_Length = KEY_MATRIX_READ_LENGTH;
  381.  
  382.     TIME_IOB->tr_node.io_Command = TR_GETSYSTIME;
  383.  
  384.     IN_IOB->io_Command = IND_WRITEEVENT;
  385.     IN_IOB->io_Flags = 0;
  386.     IN_IOB->io_Length = sizeof(struct InputEvent);
  387.     IN_IOB->io_Data = (APTR) &event;
  388.  
  389.     SER_IOB->IOSer.io_Command = CMD_READ;
  390.     SER_IOB->IOSer.io_Length = 1;
  391.     SER_IOB->IOSer.io_Data = (APTR) &b;
  392.  
  393.     for(;;) {
  394.         my_DoIO(ser.iob);
  395.         
  396.         /* The Mouse Systems mouse sends back a five byte code
  397.          * as follows:
  398.          *
  399.          * byte 0: (sync byte) %10000XXX (XXX = button states)
  400.          * byte 1: X delta
  401.          * byte 2: Y delta
  402.          * byte 3: X delta
  403.          * byte 4: Y delta
  404.          *
  405.          * Note that bytes 3 & 4 specify the mouse movement since
  406.          * bytes 1 & 2 were sent.  Thus a single 5 byte message
  407.          * updates the mouse position twice, although it
  408.          * only updates switch states once.
  409.          */
  410.  
  411.         switch(state) {
  412.         case 0:
  413.             if((b & 0xf8) == 0x80) {
  414.                 button.right  = !(b & (1 << 0));
  415.                 button.middle = !(b & (1 << 1));
  416.                 button.left   = !(b & (1 << 2));
  417.                 update_buttons();
  418.                 state++;
  419.             }
  420.             break;
  421.         
  422.         case 1:
  423.             dx = b;
  424.             state++;
  425.             break;
  426.         
  427.         case 2:
  428.             dy = b;
  429.             update_position(dx, dy);
  430.             state++;
  431.             break;
  432.         
  433.         case 3:
  434.             dx = b;
  435.             state++;
  436.             break;
  437.         
  438.         case 4:
  439.             dy = b;
  440.             update_position(dx, dy);
  441.             state = 0;
  442.             break;
  443.         }
  444.     }
  445. }
  446.