[comp.sources.amiga] v02i004: ClickUpFront

dpvc@tut.cc.rochester.edu (Davide P. Cervone) (07/22/87)

    This is another "clicktofront" type utility.  See the README file for
more info.
    -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:
#	README
#	Click-Handler.c
#	ClickUpFront.c
#	Click-Handler.lnk
#	ClickUpFront.lnk
#	HandlerStub.a
# This archive created: Tue Jul 21 19:01:29 1987
# By:	Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > README
OK, here's a little program called ClickUpFront, which is my addition to the
ClickToFront-type utilities.  ClickUpFront installs an input handler that
brings windows (and screens) to the front when you double click in them.

To install the handler, just type:

    1> ClickUpFront

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

Once ClickUpFront is installed and you double click in a window, that window
will be brought to the front of all the other windows (unless it is a backdrop
window).  If the window is on a screen that is not the front-most screen,
then that screen is brought to the front of all the other screens as well.
You can set the double-click time with the Preferences tool.  Be careful that
you don't move the mouse between clicks as this will cause ClickUpFront to
ignore the double-click.

To remove the input handler, simply call ClickUpFront a second time. 
ClickUpFront 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.

Note that ClickUpFront does not remove mouse click events from the input
stream even when they form a double-click that moves a window to the front. 
If you want to be able to send a double-click to a window WITHOUT having it
come to the front, simply hold down one of the shift keys while you click
in the window.  ClickUpFront will ignore double-clicks when a shift key is
pressed.  If you want to use some other qualifier key instead of the shift
keys, you can specify a qualifier-key mask on the commaand line:

    1> ClickUpFront 0x0008

would specify the CTRL key instead of the shift keys.  See the include file
exec/inputevent.h for the hex values for the other qualifier keys.


To install ClickUpFront on your disk, simply unshar and uudecode the 
executables.  Put ClickUpFront in the C: directory and put Click-Handler in the
L: directory (or the current directory).

To compile and link ClickUpFront, type:

    1> LC -v ClickUpFront Click-Handler
    1> ASM HandlerStub
    1> BLINK WITH ClickUpFront.lnk
    1> BLINK WITH Click-Handler.lnk

You can use the -b and -r options with ClickUpFront, but don't use them for
Click-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
cat << \SHAR_EOF > Click-Handler.c
/*
 *  Click-Handler.c     Input Handler for ClickUpFront, which brings a
 *                      window to the front when you double-click in it.
 *
 *              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 = "Click-Handler v1.0 (July 1987)";
static char *author  = "Copyright (c) 1987 by Davide P. Cervone";

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

#define WINDOW(layer)   ((struct Window *)((layer)->Window))
#define SCREENTOP\
   (theScreen->TopEdge << ((theScreen->ViewPort.Modes & LACE)? 0: 1))
#define ie_Secs         ie_TimeStamp.tv_secs
#define ie_Mics         ie_TimeStamp.tv_micro

long LastSecs = 0;      /* seconds field from last mouse click */
long LastMics = 0;      /* micros  field from last mouse click */
long StayMask;          /* qualifier keys that allow you to double click */
                        /* a window without bringing it to the front */


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

/*
 *  Setup()
 *
 *  ClickUpFront calls LoadSeg() to get this handler into memory.  The segment
 *  that it gets points to this routine.  ClickUpFront 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 ClickUpFront installs.
 */

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


/*
 *  myHandler()
 *
 *  This is the input handler.  For each event in the event list:
 *  If it is a raw mouse event:
 *    if it is a left button click, then
 *      if the mouse has not moved since the last click and the user is
 *          not holding down the keys that allow windows to stay put, then
 *        if the time since the last click was less than the double click time,
 *          Find the screen where the mouse is pointing.
 *          Find the top window on the that screen.
 *          If there is one, then
 *            find the Layer in which the click occured.
 *            if that Layer's window is not already at the top,
 *              bring it to the top.
 *      Set the time since the last click to the current time.
 *    Otherwise, it was not a left button click, so
 *      ignore lift button up messages, but set the times to zero (a long time
 *         ago) if the mouse moved or the right button was clicked.
 *  If the event was a keyboard event, then set the times to zero.
 *  Ignore all other event types (timer, disk).
 *
 *  Finally, return the event list so that Intuition can do its thing.
 *
 */

struct InputEvent *myHandler(EventList,data)
struct InputEvent *EventList;
APTR data;
{
   register struct InputEvent *theEvent = EventList;
   register struct Layer  *theLayer, *topLayer;
   register struct Screen *theScreen;

   Forbid();
   while(theEvent)
   {
      switch(theEvent->ie_Class)
      {
         case IECLASS_RAWMOUSE:
            if (theEvent->ie_Code == SELECTDOWN)
            {
               if (theEvent->ie_X == 0 && theEvent->ie_Y == 0 &&
                  (theEvent->ie_Qualifier & StayMask) == 0)
               {
                  if (DoubleClick(LastSecs,LastMics,
                      theEvent->ie_Secs,theEvent->ie_Mics))
                  {
                     theScreen = IntuitionBase->FirstScreen;
                     while (theScreen && IntuitionBase->MouseY < SCREENTOP)
                        theScreen = theScreen->NextScreen;
                     if (theScreen == NULL)
                        theScreen = IntuitionBase->ActiveScreen;
                     if (theScreen != IntuitionBase->FirstScreen)
                        ScreenToFront(theScreen);

                     topLayer = theScreen->LayerInfo.top_layer;
                     while (topLayer && WINDOW(topLayer) == NULL)
                        topLayer = topLayer->back;
                     if (topLayer)
                     {
                        theLayer = WhichLayer(&(theScreen->LayerInfo),
                                   theScreen->MouseX,theScreen->MouseY);
                        if (theLayer && WINDOW(theLayer) != WINDOW(topLayer))
                           WindowToFront(WINDOW(theLayer));
                     }
                  }
               }
               LastSecs = theEvent->ie_Secs;
               LastMics = theEvent->ie_Mics;
            } else {
               if (theEvent->ie_Code != SELECTUP)
                  LastSecs = LastMics = 0;
            }
            break;

         case IECLASS_RAWKEY:
            LastSecs = LastMics = 0;
            break;
      }
      theEvent = theEvent->ie_NextEvent;
   }
   Permit();
   return(EventList);
}
SHAR_EOF
cat << \SHAR_EOF > ClickUpFront.c
/*
 *  ClickUpFront.c  Brings a window to the front when you double-click
 *                  in it (unless specified qualifier keys are also pressed).
 *
 *             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  = "ClickUpFront";
static char *version  = "v1.0";
static char *date     = "July 1987";
static char *author   = "Copyright (c) 1987 by Davide P. Cervone";

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

#define STAYMASK      (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)


/*
 *  This is the structure that holds the handler information between calls
 *  to ClickUpFront.  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 it's 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(argc,argv)
int argc;
char **argv;
{
   long (*Setup)();
   long flags = STAYMASK;

   CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV);
   CheckLibOpen(&LayersBase,"layers.library",LAYERS_REV);
   
   if (argc > 1) sscanf(argv[1],"%lx",&flags);

   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,flags));
   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 fro mthe 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, named message-port (we will look for this the next
 *      time ClickUpFront 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 ClickUpFront 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(argc,argv)
int argc;
char **argv;
{
   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(argc,argv);
   } else {
      HandlerInfo = (struct HandlerBlock *)(NamedPort->mp_SigTask);
      DeleteHandler();
      FreeMem(HandlerInfo->PortName,NAMESIZE);
      FreeMem(HandlerInfo,HANDLERINFOSIZE);
      DeletePort(NamedPort);
   }
}
SHAR_EOF
cat << \SHAR_EOF > Click-Handler.lnk
FROM Click-Handler.o+handlerstub.o
TO Click-Handler
LIB LIB:amiga.lib
NODEBUG
SMALLDATA
SMALLCODE
SHAR_EOF
cat << \SHAR_EOF > ClickUpFront.lnk
FROM LIB:c.o+ClickUpFront.o
TO ClickUpFront
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
#	End of shell archive
exit e