[comp.sources.amiga] v02i007: HeliosMouse

dpvc@ur-tut.UUCP (Davide P. Cervone) (07/26/87)

    Here is a SunMouse type tool that works as a handler.  Binaries available
in comp.binaries.amiga.
    -Doc

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	Helios-Handler.c
#	HeliosMouse.c
#	Helios-Handler.lnk
#	HeliosMouse.lnk
#	HandlerStub.a
#	README
# This archive created: Sat Jul 25 18:08:16 1987
# By:	Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > Helios-Handler.c
/*
 *  Helio-Handler.c     Input Handler for HeliosMouse.  Helios-handler
 *                      calls ActivateWindow() whenever the mouse moves
 *                      over a new window.
 *
 *              Copyright (c) 1987 by Davide P. Cervone
 *  You may use this code provided this copyright notice is left intact.
 */

#include <exec/types.h>
#include <devices/inputevent.h>
#include <intuition/intuitionbase.h>

static char *version = "Helios-Handler v1.0 (June 1987)";
static char *author  = "Copyright (c) 1987 by Davide P. Cervone";

extern struct Layer *WhichLayer();
extern void myHandlerStub();

/*
 *  The window associated with a given Layer structure.
 */
#define WINDOW(layer)   ((struct Window *)((layer)->Window))

/*
 *  The top line of a screen (in INTERLACED lines)
 */
#define SCREENTOP\
   (theScreen->TopEdge << ((theScreen->ViewPort.Modes & LACE)? 0: 1))


struct IntuitionBase *IntuitionBase = NULL;
struct LayersBase    *LayersBase    = NULL;
struct SysBase       *SysBase       = NULL;

/*
 *  Setup()
 *
 *  HeliosMouse calls LoadSeg() to get this handler into memory.  The segment
 *  that it gets points to this routine.  HeliosMouse calls Setup() and 
 *  passes the IntuitionBase, LayersBase and SysBase pointers that it
 *  has initialized (with OpenLibrary()).  Setup returns a pointer to
 *  the actual input handler, which HeliosMouse installs.
 */

long Setup(Ibase,Lbase,Sbase)
struct IntuitionBase *Ibase;
struct LayersBase *Lbase;
struct SysBase *Sbase;
{
   IntuitionBase = Ibase;
   LayersBase = Lbase;
   SysBase = Sbase;
   return((long) &myHandlerStub);
}


/*
 *  myHandler()
 *
 *  This is the input handler.  Whenever a mouse move or a keyboard event
 *  occurs, we check to see if the mouse is over an inactive window, and
 *  if so, we activate that window.
 *
 *  To do this, we get the current absolute Y position of the mouse and
 *  look through the Intuition screens for the first one that is closer
 *  to the top of the View than the mouse is (i.e., the first one that is
 *  high enough to be under the mouse).  Note that we must convert non-
 *  interlace screen coordinates to interlaces ones in order to be able
 *  to compare them to the mouse position (the SCREENTOP macro does this).
 *  If no screen is found (this should never happen, but just in case), then 
 *  just use the currently active one.
 *
 *  From the selected screen we get the mouse x and y position (if it's a 
 *  mouse move, we add the event offsets to find the new mouse position).  
 *  Using these, we call WhichLayer(), which tells us which Layer within 
 *  that screen the mouse was pointing at.  If that layer has a window and 
 *  that window is not currently the active window, we activate it.
 *
 *  When we're all through looking at events, we pass the list on, so that
 *  Intuition (and any other handlers) can do their things.
 *
 *  Compile this section with -dKEY_ONLY to have windows activate only when 
 *  a key is pressed (not when the mouse moves over an inactive one).
 */

struct InputEvent *myHandler(EventList,data)
struct InputEvent *EventList;
APTR data;
{
   register struct InputEvent *theEvent = EventList;
   register struct Layer  *theLayer;
   register struct Window *theWindow;
   register struct Screen *theScreen;
   register long x,y;

   Forbid();
   while(theEvent)
   {
      if (
          #ifndef KEY_ONLY
            (theEvent->ie_Class == IECLASS_RAWMOUSE &&
            (theEvent->ie_X != 0 || theEvent->ie_Y != 0)) ||
          #endif
            (theEvent->ie_Class == IECLASS_RAWKEY)
         )
      {
         y = IntuitionBase->MouseY;
         theScreen = IntuitionBase->FirstScreen;
         while (theScreen && y < SCREENTOP) theScreen = theScreen->NextScreen;
         if (theScreen == NULL) theScreen = IntuitionBase->ActiveScreen;

         x = theScreen->MouseX;
         y = theScreen->MouseY;
         #ifndef KEY_ONLY
            if (theEvent->ie_Class == IECLASS_RAWMOUSE)
            {
               x += theEvent->ie_X;
               y += theEvent->ie_Y;
            }
         #endif

         theLayer = WhichLayer(&(theScreen->LayerInfo),x,y);
         if (theLayer && (theWindow = WINDOW(theLayer)))
            if (theWindow != IntuitionBase->ActiveWindow)
               ActivateWindow(theWindow);
      }
      theEvent = theEvent->ie_NextEvent;
   }
   Permit();
   return(EventList);
}
SHAR_EOF
cat << \SHAR_EOF > HeliosMouse.c
/*
 *  HeliosMouse.c   Installs an input handler that activates windows when
 *                  you move the mouse over inactive ones.
 *
 *             Copyright (c) 1987 by Davide P. Cervone
 *  You may use this code provided this copyright notice is left intact.
 */

#include <exec/types.h>
#include <libraries/dos.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <devices/input.h>
#include <devices/inputevent.h>

static char *program  = "HeliosMouse";
static char *version  = "v1.0";
static char *date     = "June 1987";
static char *author   = "Copyright (c) 1987 by Davide P. Cervone";

static char *PortName = "HeliosPort";
static char *handler  = "L:Helios-Handler";    /* the handler file */
#define HANDLER            &(handler[2])       /* without the "L:" */


/*
 *  This is the structure that holds the handler information between calls
 *  to HeliosMouse.  We create a named, public message-port that points to
 *  an instance of this structure so that we can find this information
 *  when we are asked to remove the handler.  The PortName is stored here 
 *  (NamedPort->mp_Node.ln_Name uses this area for its name).   The other 
 *  data are what we need in order to remove the handler and clean up properly.
 */

struct HandlerBlock
{
   char *PortName;                  /* name of the public, named message-port */
   struct Interrupt Handler;        /* the handler added to the handler chain */
   struct IntuitionBase *Ibase;     /* library base used by the handler */
   struct LayersBase *Lbase;        /* library base used by the handler */
   long Segment;                    /* pointer from LoadSeg() */
};
#define HANDLERINFOSIZE     sizeof(struct HandlerBlock)
#define NAMESIZE            (strlen(PortName)+1)

extern struct MsgPort *CreatePort();
extern struct IOStdReq *CreateStdIO();
extern struct MsgPort *FindPort(), *CreatePort();
extern APTR AllocMem();
extern long LoadSeg();

#define INTUITION_REV   0L
#define LAYERS_REV      0L

struct IntuitionBase  *IntuitionBase = NULL;
struct LayersBase     *LayersBase    = NULL;
extern struct SysBase *SysBase;

struct MsgPort *InputPort = NULL;     /* Port used to talk to Input.Device */
struct IOStdReq *InputBlock = NULL;   /* request block used with Input.Device */
LONG InputDevice = 0;                 /* flag whether Input.Device is open */
struct MsgPort *NamedPort = NULL;     /* holds info needed to remove handler */
struct HandlerBlock *HandlerInfo = NULL; /* holds info stored in NamedPort */


/*
 *  DoExit()
 *
 *  General purpose exit routine.  If 's' is not NULL, then print an
 *  error message with up to three parameters.  Free any memory, close
 *  any open device, delete any ports, close any libraries, etc.
 */

void DoExit(s,x1,x2,x3)
char *s, *x1, *x2, *x3;
{
   long status = RETURN_OK;
   
   if (s != NULL)
   {
      printf(s,x1,x2,x3);
      printf("\n");
      status = RETURN_ERROR;
   }
   if (InputDevice)   CloseDevice(InputBlock);
   if (InputBlock)    DeleteStdIO(InputBlock);
   if (InputPort)     DeletePort(InputPort);
   if (NamedPort)     DeletePort(NamedPort);
   if (HandlerInfo)
   {
      if (HandlerInfo->PortName) FreeMem(HandlerInfo->PortName,NAMESIZE);
      FreeMem(HandlerInfo,HANDLERINFOSIZE);
   }
   if (IntuitionBase) CloseLibrary(IntuitionBase);
   if (LayersBase)    CloseLibrary(LayersBase);
   exit(status);
}


/*
 *  CheckLibOpen()
 *
 *  General library open routine.  It opens a library and sets a pointer
 *  to it.  It checks that the library was openned successfully.
 */

void CheckLibOpen(lib,name,rev)
APTR *lib;
char *name;
int rev;
{
   extern APTR OpenLibrary();

   if ((*lib = OpenLibrary(name,(LONG)rev)) == NULL)
      DoExit("Can't open '%s'\n",name);
}


/*
 *  Macros that make memory allocation easier.
 */
#define NEW(s,var)      (var = (struct s *)New("var",sizeof(struct s)))
#define NEWCHAR(var,s)  (var = (char *)New("var",s))


/*
 *  New()
 *
 *  Allocate public memory of a given size and set it to all zeros.  If there
 *  is not enough memory, then exit with an error, otherwise return the
 *  pointer to the newly allocated memory.
 */
 
APTR New(name,size)
char *name;
int size;
{
   APTR ptr;
   
   if ((ptr = AllocMem(size,MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
      DoExit("Can't Get Memory for '%s'");
   return(ptr);
}


/*
 *  TellInputDevice()
 *
 *  Create a port and I/O block, and open the input device.  Set up the
 *  I/O block to add or remove the input handler, and send the request
 *  to the input device.  Finally, close the device and delete the
 *  I/O block and port.
 */
 
void TellInputDevice(function)
int function;
{
   long status;

   if ((InputPort = CreatePort(0,0)) == NULL) DoExit("Can't Create Port");
   if ((InputBlock = CreateStdIO(InputPort)) == NULL)
      DoExit("Can't Create Standard IO Block");
   InputDevice = (OpenDevice("input.device",0,InputBlock,0) == 0);
   if (InputDevice == FALSE) DoExit("Can't Open 'input.device'");
   
   InputBlock->io_Command = (long) function;
   InputBlock->io_Data    = (APTR) &(HandlerInfo->Handler);
   if (status = DoIO(InputBlock)) DoExit("Error from DoIO:  %ld",status);

   CloseDevice(InputBlock);
   DeleteStdIO(InputBlock);
   DeletePort(InputPort);
}


/*
 *  CreateHandler()
 *
 *  Open the libraries needed by the input handler and store their locations
 *  in the HandlerInfo structure (so we can close them later).  Try to 
 *  LoadSeg() the handler.  If it is not in the current directory, try the
 *  L: directory.  Exit with an error if the handler can't be found.
 *  Convert the segment pointer into a pointer to the Setup routine (the
 *  first routine in the handler executable).  Call Setup() and pass it
 *  the pointers to the libraries that it will need to use.  Setup() returns
 *  a pointer to the actual handler routine that should be added into the
 *  input handler chain.  Store this in the HandlerInfo structure so we
 *  can use it to remove the handler later.  Set the handler priority to
 *  51 so that it is ahead of Intuition.
 *
 *  Finally, add the handler in the chain and tell the user that the
 *  handler has been installed.
 */

void CreateHandler()
{
   long (*Setup)();

   CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV);
   CheckLibOpen(&LayersBase,"layers.library",LAYERS_REV);

   HandlerInfo->Ibase = IntuitionBase;
   HandlerInfo->Lbase = LayersBase;
   if ((HandlerInfo->Segment = LoadSeg(HANDLER)) == NULL)
      if ((HandlerInfo->Segment = LoadSeg(handler)) == NULL)
         DoExit("Can't Load '%s'",handler);
   Setup = (long (*)()) ((HandlerInfo->Segment << 2) + 4);
   HandlerInfo->Handler.is_Code = 
      (void (*)()) ((*Setup)(IntuitionBase,LayersBase,SysBase));
   HandlerInfo->Handler.is_Node.ln_Pri = 51;

   TellInputDevice(IND_ADDHANDLER);
   printf("%s %s (%s) Installed\n",program,version,date);
}


/*
 *  Delete Handler()
 *
 *  Retreive the library pointers from the HandlerInfo structure where
 *  we stored them when we originally installed the handler, then remove
 *  the handler from the input handler chain.  Tell the user that the
 *  handler is gone and then close the libraries that are no longer needed.
 */

void DeleteHandler()
{
   IntuitionBase = HandlerInfo->Ibase;
   LayersBase    = HandlerInfo->Lbase;

   TellInputDevice(IND_REMHANDLER);
   UnLoadSeg(HandlerInfo->Segment);
   printf("%s Removed\n",program);
   
   CloseLibrary(IntuitionBase);
   CloseLibrary(LayersBase);
}


/*
 *  main()
 *
 *  Check if a message port with our name already exists.
 *  If not, then the handler is not already installed, so:
 *    Allocate a new HandlerInfo structure and initialize the port name.
 *    Create a public, names message-port (we will look for this the next
 *      time HeliosMouse is called).
 *    Make the message port point to the HandlerInfo structure so we
 *      can use it later when the user asks us to remove the handler.
 *      Note that the port is not actually used for putting and getting
 *      messages, so the Task field is never used, so we can take it for
 *      our own uses.
 *    Finally, add the input handler into the chain.
 *  Otherwise, the port exists, so HeliosMouse already is installed:
 *    Retreive the HandlerInfo poiner from the port, and
 *    remove the handler from the input handler chain.
 *    Free the memory used by the HandlerInfo, and delete the message port.
 */

void main()
{
   NamedPort = FindPort(PortName);
   if (NamedPort == NULL)
   {
      NEW(HandlerBlock,HandlerInfo);
      NEWCHAR(HandlerInfo->PortName,NAMESIZE);
      strcpy(HandlerInfo->PortName,PortName);
      if ((NamedPort = CreatePort(HandlerInfo->PortName,0)) == NULL)
         DoExit("Can't Create Message Port '%s'",PortName);
      NamedPort->mp_SigTask = (struct Task *)HandlerInfo;
      CreateHandler();
   } else {
      HandlerInfo = (struct HandlerBlock *)(NamedPort->mp_SigTask);
      DeleteHandler();
      FreeMem(HandlerInfo->PortName,NAMESIZE);
      FreeMem(HandlerInfo,HANDLERINFOSIZE);
      DeletePort(NamedPort);
   }
}
SHAR_EOF
cat << \SHAR_EOF > Helios-Handler.lnk
FROM Helios-Handler.o+handlerstub.o
TO Helios-Handler
LIB LIB:amiga.lib
NODEBUG
SMALLDATA
SMALLCODE
SHAR_EOF
cat << \SHAR_EOF > HeliosMouse.lnk
FROM LIB:c.o+HeliosMouse.o
TO HeliosMouse
LIB LIB:lc.lib+LIB:amiga.lib
NODEBUG
SMALLDATA
SMALLCODE
SHAR_EOF
cat << \SHAR_EOF > HandlerStub.a
        CSECT   text

        XREF    _myHandler
        XDEF    _myHandlerStub
        
_myHandlerStub:
        MOVEM.L A0/A1,-(A7)
        JSR     _myHandler
        ADDQ.L  #8,A7
        RTS
        
        END
SHAR_EOF
cat << \SHAR_EOF > README
OK, here's a little program called HeliosMouse, which is my addition to the
SunMouse-type utilities.  HeliosMouse installs an input handler that activates
windows whenever the mouse is moved over inactive ones (i.e., when you move
the mouse accross window boundries).

To install the handler, just type:

    1> HeliosMouse

It should tell you that it has installed the handler.  You don't have to
RUN HeliosMouse, because it sets up the handler and then exits, leaving only 
the input handler in memory.  The handler itself only takes up about 600 bytes 
when it is installed, so you shouldn't have to worry about using lots of memory.

Once HeliosMouse is installed and you move the mouse over an inactive window,
that window will be activated automatically.  Note, however, that if you open
a new, active window (with the NewCLI command, for instance) the window under
the mouse will not be activated until you move the mouse or press a key.  (You
can change this by having HeliosMouse check the active window every 
IECLASS_TIMER event rather than IECLASS_RAWMOUSE and IECLASS_RAWKEY events.  
Note, however, that timer events are posted approximately 10 times per second,
so this could degrade system performance).

To remove the input handler, simply call HeliosMouse a second time. 
HeliosMouse creates a public, named message-port that it uses to hold the
information it needs in order to remove the handler that it installed.  See
the code for details of how this works.

To install HeliosMouse, simply unshar and uudecode the executables.  Put
HeliosMouse in the C: directory and put Helios-Handler in the L: directory 
(or the current directory).

To compile and link HeliosMouse, type:

    1> LC -v HeliosMouse Helios-Handler
    1> ASM HandlerStub
    1> BLINK WITH HeliosMouse.lnk
    1> BLINK WITH Helios-Handler.lnk

If you compile Helios-Handler with -dKEY_ONLY, then the windows will be 
activated only when a key is pressed (not when the mouse moves).  This will
act much like SunMouse, except that no fake input events are sent, so gadgets
won't be pressed when you don't expect them to.

You can use the -b and -r options with HeliosMouse, but don't use them for
Helios-Handler.

Hope you enjoy this little program!

Davide P. Cervone
University of Rochester Computing Center            dpvc@tut.cc.rochester.EDU
Taylor Hall                                         dpvc@ur-tut.UUCP
Rochester, New York  14627                          DPVC@UORDBV.BITNET
(716) 275-2811
SHAR_EOF
#	End of shell archive
exit 0