[comp.sources.amiga] ClickToFront, release 2

afb@j.cc.purdue.edu (Matthew Bradburn) (12/07/87)

This is an update to the ClickToFront utility by Davide P. Cervone,
which brings a window or screen to the front or pushes it to the
back when the user double clicks in that window or screen.  Docs
in comp.binaries.amiga.

The author:

Davide P. Cervone
dpvc@tut.cc.rochester.edu
dpvc@ur-tut.UUCP
DPVC@UORDBV.BITNET

The poster:

Matthew Bradburn
ARPA:	afb@j.cc.purdue.edu
UUCP:	j.cc.purdue.edu!afb
BITNET: bradburn@purccvm.bitnet


#	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:
#	Click-Handler.c
#	ClickUpFront.c
#	Click-Handler.lnk
#	ClickUpFront.lnk
#	HandlerStub.a
# This archive created: Sun Dec  6 20:50:52 1987
# By:	Matthew Bradburn (Purdue University)
echo shar: extracting Click-Handler.c '(7244 characters)'
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 v2.1 (October 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 TOPLAYER(screen)    ((screen)->LayerInfo.top_layer)

#define ie_Secs         ie_TimeStamp.tv_secs
#define ie_Mics         ie_TimeStamp.tv_micro

static UBYTE LastCode = 0;     /* the last ie_Code for a button event */
static long LastSecs = 0;      /* seconds field from last mouse click */
static long LastMics = 0;      /* micros  field from last mouse click */
static long StayMask;          /* qualifier keys that allow you to 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.
 */

static 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:
 *    Ignore button-up events,
 *    For button down events, if it is the same as the last button, and
 *      the mouse has not moved, and the button was a double click, then
 *      Find the active window and screen (where the button press occured)
 *      for SELECTDOWN buttons:
 *        If the screen is not the first one, then bring it to the front. 
 *        If a window is selected, then find the top layer with a window.
 *        If the window where the click ocurred is not the top, and
 *          the stay-put qualifier key is not pressed, then 
 *            bring the window to the front.
 *      for MENUDOWN buttons:
 *        If a window is selected, and the window is not the bottom window
 *          already, and the window below it is not a backdrop window, then
 *          if the selected window has no double-menu requester and 
 *            the stay-put qualifier is not present (if there IS a 
 *            DM request and the stay-put qualifier IS present), then 
 *              send the window to the back.
 *              change the event into a mouse move with zero offsets
 *              (that is, remove one of the right button clicks).
 *         otherwise (there is no window, or it is already at the bottom,
 *          or the one below is a backdrop), then
 *          find the first window on the next screen and activate it.
 *          Send the active screen to the back.
 *     Otherwise (the mouse was moved, or it was not a double click, etc.)
 *       record this click and the time it ocurred.
 *    For mouse moves, clear the last code (ie, don't match a double click)
 *  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 Window *theWindow;
   register struct Screen *theScreen;
   register struct Layer *topLayer;

   Forbid();
   while(theEvent)
   {
      switch(theEvent->ie_Class)
      {
         case IECLASS_RAWMOUSE:
            if ((theEvent->ie_Code & IECODE_UP_PREFIX) == 0)
            {
               if (theEvent->ie_Code == LastCode &&
                   theEvent->ie_X == 0 && theEvent->ie_Y == 0 &&
                   DoubleClick(LastSecs,LastMics,
                              theEvent->ie_Secs,theEvent->ie_Mics))
               {
                  theWindow = IntuitionBase->ActiveWindow;
                  theScreen = IntuitionBase->ActiveScreen;
                  switch(LastCode)
                  {
                     case SELECTDOWN:
                        if (theScreen != IntuitionBase->FirstScreen)
                           ScreenToFront(theScreen);
                        if (theWindow)
                        {
                           topLayer = TOPLAYER(theScreen);
                           while (topLayer && WINDOW(topLayer) == NULL)
                              topLayer = topLayer->back;
                           if (theWindow != WINDOW(topLayer) &&
                              (theEvent->ie_Qualifier & StayMask) == 0)
                                 WindowToFront(theWindow);
                        }
                        break;

                     case MENUDOWN:
                        if (theWindow && theWindow->WLayer->back &&
                           (theWindow->Flags & BACKDROP) == 0 &&
                           (theWindow->WLayer->back->Flags &
                            LAYERBACKDROP) == 0)
                        {
                           if (theWindow && (theWindow->DMRequest == NULL) ==
                              ((theEvent->ie_Qualifier & StayMask) == 0))
                           {
                              WindowToBack(theWindow);
                              theEvent->ie_Code = IECODE_NOBUTTON;
                              theEvent->ie_X = theEvent->ie_Y = 0;
                           }
                        } else {
                           if (theScreen->NextScreen)
                           {
                              topLayer = TOPLAYER(theScreen->NextScreen);
                              while (topLayer && WINDOW(topLayer) == NULL)
                                 topLayer = topLayer->back;
                              if (topLayer && WINDOW(topLayer))
                                 ActivateWindow(WINDOW(topLayer));
                              ScreenToBack(theScreen);
                           }
                        }
                        break;
                  }
                  LastCode = 0;
               } else {
                  LastCode = theEvent->ie_Code;
                  LastSecs = theEvent->ie_Secs;
                  LastMics = theEvent->ie_Mics;
               }
            } else {
               if (theEvent->ie_Code == IECODE_NOBUTTON) LastCode = 0;
            }
            break;

         case IECLASS_RAWKEY:
            LastSecs = LastMics = 0;
            break;
      }
      theEvent = theEvent->ie_NextEvent;
   }
   Permit();
   return(EventList);
}
SHAR_EOF
if test 7244 -ne "`wc -c Click-Handler.c`"
then
echo shar: error transmitting Click-Handler.c '(should have been 7244 characters)'
fi
echo shar: extracting ClickUpFront.c '(9317 characters)'
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
if test 9317 -ne "`wc -c ClickUpFront.c`"
then
echo shar: error transmitting ClickUpFront.c '(should have been 9317 characters)'
fi
echo shar: extracting Click-Handler.lnk '(98 characters)'
cat << \SHAR_EOF > Click-Handler.lnk
FROM Click-Handler.o+handlerstub.o
TO Click-Handler
LIB LIB:amiga.lib
NODEBUG
SMALLDATA
SMALLCODE
SHAR_EOF
if test 98 -ne "`wc -c Click-Handler.lnk`"
then
echo shar: error transmitting Click-Handler.lnk '(should have been 98 characters)'
fi
echo shar: extracting ClickUpFront.lnk '(101 characters)'
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
if test 101 -ne "`wc -c ClickUpFront.lnk`"
then
echo shar: error transmitting ClickUpFront.lnk '(should have been 101 characters)'
fi
echo shar: extracting HandlerStub.a '(215 characters)'
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
if test 215 -ne "`wc -c HandlerStub.a`"
then
echo shar: error transmitting HandlerStub.a '(should have been 215 characters)'
fi
#	End of shell archive
exit 0