[comp.sources.amiga] wKeys--attach window functions to function keys.

ahh@j.cc.purdue.edu (Brent L. Woods) (02/09/88)

Program Name:  wKeys
Submitted By:  Davide P. Cervone
               dpvc@tut.cc.rochester.edu
               dpvc@ur-tut.UUCP
               DPVC@UORDBV.BITNET
Summary:  A program to attach various window functions to the function keys.
Poster Boy:  Brent Woods  (ahh@j.cc.purdue.edu)
Tested.

NOTES:
     Very much like the previous submission of wKeys, but with some
functions added.  They are:

"The new functions added are:  ability to close any window with a close gadget;
iconify a window; uniconify a window; and select the next window icon on the 
workbench.  The last three are available only if wIconify is running.

See the documentation file for more information on wKeys.
[currently in the binaries posting--Moderator]

I have included .uu files for HandlerStub.o (in case anyone does not have
an assembler), and wIconCalls.o (since I have not distributed source to
this one yet).  The rest is pretty much self-explanatory (I hope)."
                                               -Author



Brent Woods, Co-Moderator, comp.{sources,binaries}.amiga

USENET:  ...!j.cc.purdue.edu!ahh     ARPANET:  ahh@j.cc.purdue.edu
BITNET:  PODUM@PURCCVM               PHONE:  +1 (317) 743-8421
USNAIL:  320 Brown St., #406  /  West Lafayette, IN  47906

================================================================

#	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:
#	wKeys.c
#	BindWKeys.c
#	mSort.c
#	wKeys-Handler.c
#	HandlerStub.a
#	wKeys.h
#	wKeys.lnk
#	wKeys-Handler.lnk
#	HandlerStub.o.uu
#	wIconCalls.o.uu
# This archive created: Sun Jan 10 21:47:10 1988
# By:	 (Davide P. Cervone)
cat << \SHAR_EOF > wKeys.c
/*
 *  wKeys.c    Moves and activates windows and screens via keystrokes.
 *
 *             Copyright (c) 1987,1988 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>

#include "wKeys.h"

static char *program  = "wKeys";
static int   version  = 2;
static char *date     = "January 1988";
static char *author   = "Copyright (c) 1987,1988 by Davide P. Cervone";
static int   hversion = 0;

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


/*
 *  This is the structure that holds the handler information between calls
 *  to wKeys.  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;     /* an error if the handler can't be found */
   struct LayersBase *Lbase;        /* library base used by the handler */
   long Segment;                    /* pointer from LoadSeg() */
   struct HotKey *Keys;             /* pointer to Key array */
   struct MsgPort *ReplyPort;       /* the reply port for CLOSE-WINDOW */
   int KeyCount;                    /* size of Key array */
};
#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 MsgPort *ReplyPort = NULL;     /* reply port for CLOSE-WINDOW */
struct HandlerBlock *HandlerInfo = NULL; /* holds info stored in NamedPort */

extern struct HotKeyItem *KeyList;
extern struct HotKey *KeyArray;
extern int KeyCount;
#define KEYARRAYSIZE    (KeyCount*sizeof(struct HotKey))
extern void GetKeyArray();


/*
 *  DeleteNonSigPort()
 *
 *  Deletes a message port with signal type PA_IGNORE.  Based on
 *  DeletePort() from the exec support functions documented in the RKM
 */

void DeleteNonSigPort(thePort)
struct MsgPort *thePort;
{
   if (thePort->mp_Node.ln_Name) RemPort(thePort);
   thePort->mp_Node.ln_Type = 0xFF;
   thePort->mp_MsgList.lh_Head = (struct Node *) -1;
   FreeMem(thePort,(ULONG)sizeof(struct MsgPort));
}


/*
 *  CreateNonSigPort()
 *
 *  Creates a message port with signal type PA_IGNORE.  Based on
 *  CreatePort() from the exec support functions documented in the RKM.
 */

struct MsgPort *CreateNonSigPort(name,pri)
char *name;
BYTE pri;
{
   struct MsgPort *thePort;
   
   thePort = (struct MsgPort *)AllocMem((ULONG)sizeof(struct MsgPort),
                                         MEMF_PUBLIC | MEMF_CLEAR);
   if (thePort)
   {
      thePort->mp_Node.ln_Name = name;
      thePort->mp_Node.ln_Pri  = pri;
      thePort->mp_Node.ln_Type = NT_MSGPORT;
      
      thePort->mp_Flags = PA_IGNORE;
      thePort->mp_SigBit = 0;
      thePort->mp_SigTask = NULL;
      
      if (name)
         AddPort(thePort);
        else
         NewList(&(thePort->mp_MsgList));
   }
   return(thePort);
}


/*
 *  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;
   struct HotKeyItem *TempKey;
   
   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)     DeleteNonSigPort(NamedPort);
   if (ReplyPort)     DeleteNonSigPort(ReplyPort);
   if (HandlerInfo)
   {
      if (HandlerInfo->PortName) FreeMem(HandlerInfo->PortName,NAMESIZE);
      FreeMem(HandlerInfo,HANDLERINFOSIZE);
   }
   if (KeyArray)      FreeMem(KeyArray,KEYARRAYSIZE);
   while (KeyList)
   {
      TempKey = KeyList;
      KeyList = KeyList->Next;
      FreeMem(TempKey,sizeof(*TempKey));
   }
   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.
 */

static 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'",name);
   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.
 */
 
static 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.
 */

static 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,
                             KeyArray,KeyCount,ReplyPort,&hversion));
   HandlerInfo->Handler.is_Node.ln_Pri = 51;
   HandlerInfo->Keys = KeyArray;
   HandlerInfo->KeyCount = KeyCount;
   HandlerInfo->ReplyPort = ReplyPort;

   TellInputDevice(IND_ADDHANDLER);
   printf("%s v%d.%d (%s) Installed\n",program,version,hversion,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.
 */

static void DeleteHandler()
{
   IntuitionBase = HandlerInfo->Ibase;
   LayersBase    = HandlerInfo->Lbase;
   KeyArray      = HandlerInfo->Keys;
   KeyCount      = HandlerInfo->KeyCount;
   ReplyPort     = HandlerInfo->ReplyPort;

   TellInputDevice(IND_REMHANDLER);
   UnLoadSeg(HandlerInfo->Segment);
   printf("%s Removed\n",program);
   
   FreeMem(KeyArray,KEYARRAYSIZE);
   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 wKeys 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.
 *    Get a port to use as a reply port for CLOSE-WINDOW intuimessages.
 *    Finally, add the input handler into the chain.
 *  Otherwise, the port exists, so wKeys 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 ports.
 */

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 = CreateNonSigPort(HandlerInfo->PortName,0)) == NULL)
         DoExit("Can't Create Message Port '%s'",PortName);
      if ((ReplyPort = CreateNonSigPort(NULL,0)) == NULL)
         DoExit("Can't Create Reply Port");
      NamedPort->mp_SigTask = (struct Task *)HandlerInfo;
      GetKeyArray(argc,argv);
      CreateHandler();
   } else {
      HandlerInfo = (struct HandlerBlock *)(NamedPort->mp_SigTask);
      DeleteHandler();
      FreeMem(HandlerInfo->PortName,NAMESIZE);
      FreeMem(HandlerInfo,HANDLERINFOSIZE);
      DeleteNonSigPort(ReplyPort);
      DeleteNonSigPort(NamedPort);
   }
}
SHAR_EOF
cat << \SHAR_EOF > BindWKeys.c
/*
 *  BindWKeys.c     Reads a file and creates an array of key bindings
 *                  to be used by a hot-key handler to tell which keys
 *                  do what.
 *
 *             Copyright (c) 1987,1988 by Davide P. Cervone
 *  You may use this code provided this copyright notice is left intact.
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/inputevent.h>
#ifndef NO_FILE
#include <stdio.h>
#endif

#include "wKeys.h"

#ifndef NO_FILE
#define KEYMASK         0x00FF      /* default qualifier key mask */
#define SHIFT           IECODE_UP_PREFIX

#define BADVALUE        -1          /* error while parsing input line */

/*
 *  Macros to tell whether a character is printable or not, and
 *  whether it is not alphanumeric
 */
#define PRINTABLE(c)    ((c)>=' '&&(c)<='~')
#define NOTPRINTABLE(c) ((c)<' '||(c)>'~')
#define NOTALPHANUM(c)\
   ((c)<'0'||((c)>'9'&&(c)<'A')||((c)>'Z'&&(c)<'a')||(c)>'z')

/*
 *  Create a new instance of a given structure type
 */
#define NEW(s,var)      (var = (struct s *)New("var",sizeof(struct s)))


#define LINESIZE    132
static char InputLine[LINESIZE+1];    /* the line read from the file */
static char *CurPos;                  /* current character position in line */
static char *CurWord;                 /* pointer to begining of current word */
static char TerminationChar;          /* character that ended the word */
static int LineCount = 0;             /* number of lines read from the file */
#endif

struct HotKeyItem *KeyList = NULL;    /* the list of key definitions so far */
struct HotKey *KeyArray = NULL;       /* the sorted array of key definitions */
int KeyCount = 0;                     /* the number of key definitions */
#define KEYARRAYSIZE    (KeyCount*sizeof(struct HotKey))

#ifndef NO_FILE
static FILE *InFile = NULL;           /* the input file */
#define ERROR _OSERR                  /* the system's error variable */
extern int ERROR;


/*
 *  This structure maps a character string to a numeric value.  It is used
 *  for mapping key names to keyboard scan codes and key action names to 
 *  action numbers.
 */

struct Definition
{
   char *Name;
   UBYTE Code;
};


/*
 *  Qualifier[] maps the names of the qualifier keys to their corresponding 
 *  bit positions within the ie_Qualifier field of an InputEvent.  This List 
 *  is sorted by name.  Note that LCOMMAND is equivalent to LAMIGA, etc.  
 *  Note also that there are some compound qualifier names, such as SHIFT.  
 *  These are expanded into more than one key definition.
 *
 *  See devices/inputevent.h for more details on qualifier values.
 */
 
static struct Definition Qualifier[] =
{
   {"ALT",20},
   {"AMIGA",22},
   {"CAPSLOCK",2},
   {"CONTROL",3},
   {"INTERRUPT",10},
   {"KEYUP",31},
   {"LALT",4},
   {"LAMIGA",6},
   {"LBUTTON",14},
   {"LCOMMAND",6},
   {"LSHIFT",0},
   {"MBUTTON",12},
   {"MULTIBROADCAST",11},
   {"NUMERICPAD",8},
   {"RALT",5},
   {"RAMIGA",7},
   {"RBUTTON",13},
   {"RCOMMAND",7},
   {"RELATIVEMOUSE",15},
   {"REPEAT",9},
   {"RSHIFT",1},
   {"SHIFT",16},
};
#define MAXQUALIFIER    (sizeof(Qualifier)/sizeof(struct Definition))


/*
 *  AsciiToKeyCode[] maps ASCII characters to keyboard scan-codes.  This array 
 *  is in ASCII order.  SHIFT indicates that one of the shift keys must be 
 *  held down together with the correct key in order to produce that ASCII 
 *  character.  Note, however, that upper- and lower-case letters both are 
 *  mapped to un-shifted keys.
 */

static UBYTE AsciiToKeyCode[] =
{
   0x01 | SHIFT,    /* ! */
   0x2A | SHIFT,    /* " */
   0x03 | SHIFT,    /* # */
   0x04 | SHIFT,    /* $ */
   0x05 | SHIFT,    /* % */
   0x07 | SHIFT,    /* & */
   0x2A,            /* ' */
   0x09 | SHIFT,    /* ( */
   0x0A | SHIFT,    /* ) */
   0x08 | SHIFT,    /* * */
   0x0C | SHIFT,    /* + */
   0x38,            /* , */
   0x0B,            /* - */
   0x39,            /* . */
   0x3A,            /* / */
   0x0A,            /* 0 */
   0x01,            /* 1 */
   0x02,            /* 2 */
   0x03,            /* 3 */
   0x04,            /* 4 */
   0x05,            /* 5 */
   0x06,            /* 6 */
   0x07,            /* 7 */
   0x08,            /* 8 */
   0x09,            /* 9 */
   0x29 | SHIFT,    /* : */
   0x29,            /* ; */
   0x38 | SHIFT,    /* < */
   0x0C,            /* = */
   0x39 | SHIFT,    /* > */
   0x3A | SHIFT,    /* ? */
   0x02 | SHIFT,    /* @ */
   0x20,            /* A */
   0x35,            /* B */
   0x33,            /* C */
   0x22,            /* D */
   0x12,            /* E */
   0x23,            /* F */
   0x24,            /* G */
   0x25,            /* H */
   0x17,            /* I */
   0x26,            /* J */
   0x27,            /* K */
   0x28,            /* L */
   0x37,            /* M */
   0x36,            /* N */
   0x18,            /* O */
   0x19,            /* P */
   0x10,            /* Q */
   0x13,            /* R */
   0x21,            /* S */
   0x14,            /* T */
   0x16,            /* U */
   0x34,            /* V */
   0x11,            /* W */
   0x32,            /* X */
   0x15,            /* Y */
   0x31,            /* Z */
   0x1A,            /* [ */
   0x0D | SHIFT,    /* \ */
   0x1B,            /* ] */
   0x06 | SHIFT,    /* ^ */
   0x0B | SHIFT,    /* _ */
   0x00,            /* ` */
   0x20,            /* a */
   0x35,            /* b */
   0x33,            /* c */
   0x22,            /* d */
   0x12,            /* e */
   0x23,            /* f */
   0x24,            /* g */
   0x25,            /* h */
   0x17,            /* i */
   0x26,            /* j */
   0x27,            /* k */
   0x28,            /* l */
   0x37,            /* m */
   0x36,            /* n */
   0x18,            /* o */
   0x19,            /* p */
   0x10,            /* q */
   0x13,            /* r */
   0x21,            /* s */
   0x14,            /* t */
   0x16,            /* u */
   0x34,            /* v */
   0x11,            /* w */
   0x32,            /* x */
   0x15,            /* y */
   0x31,            /* z */
   0x1A | SHIFT,    /* { */
   0x0D | SHIFT,    /* | */
   0x1B | SHIFT,    /* } */
   0x00 | SHIFT,    /* ~ */
};
#define MAXASCII    sizeof(AsciiToKeyCode)


/*
 *  Key[] maps key names to their keyboard scan-codes.  This array is sorted
 *  by name.  SHIFT means that a SHIFT key must be held down in addition to
 *  the indicated key.  Some keys have more than one name (e.g., ESCAPE is
 *  equivalent to ESC).  Note that pressing a qualifier key also can be
 *  detected.
 */

static struct Definition Key[] =
{
   {"BACKSPACE",0x41},
   {"BS", 0x41},
   {"CAPSLOCKKEY",0x62},
   {"COLON",0x29 | SHIFT},
   {"COMMA",0x38},
   {"CONTROLKEY",0x63},
   {"DASH",0x0B},
   {"DEL",0x46},
   {"DELETE",0x46},
   {"DOT",0x3C},
   {"DOWNARROW",0x4D},
   {"ENTER",0x42},
   {"ESC",0x45},
   {"ESCAPE",0x45},
   {"F1",0x50},
   {"F10",0x59},
   {"F2",0x51},
   {"F3",0x52},
   {"F4",0x53},
   {"F5",0x54},
   {"F6",0x55},
   {"F7",0x56},
   {"F8",0x57},
   {"F9",0x58},
   {"HELP",0x5F},
   {"KP0",0x0F},
   {"KP1",0x1D},
   {"KP2",0x1E},
   {"KP3",0x1F},
   {"KP4",0x2D},
   {"KP5",0x2E},
   {"KP6",0x2F},
   {"KP7",0x3D},
   {"KP8",0x3E},
   {"KP9",0x3F},
   {"LALTKEY",0x64},
   {"LAMIGAKEY",0x66},
   {"LCOMMANDKEY",0x66},
   {"LEFTARROW",0x4F},
   {"LSHIFTKEY",0x60},
   {"MINUS",0x4A},
   {"RALTKEY",0x65},
   {"RAMIGAKEY",0x67},
   {"RCOMMANDKEY",0x67},
   {"RETURN",0x44},
   {"RIGHTARROW",0x4E},
   {"RSHIFTKEY",0x61},
   {"SPACE",0x40},
   {"TAB",0x42},
   {"UPARROW",0x4C},
};
#define MAXKEY      (sizeof(Key)/sizeof(struct Definition)) 


/*
 *  Action[] maps key action names to their action numbers (used by the
 *  input handler to perform the action when the key is pressed).  This array
 *  is sorted by name.
 */

static struct Definition Action[] =
{
   {"BACK-WINDOW-TO-FRONT", BACKTOFRONT},
   {"CLOSE-WINDOW",         CLOSETHEWINDOW},
   {"FRONT-WINDOW-TO-BACK", FRONTTOBACK},
   {"ICON-TO-WINDOW",       ICONTOWINDOW},
   {"NEXT-WINDOW",          ACTIVATENEXT},
   {"PREVIOUS-WINDOW",      ACTIVATEPREVIOUS},
   {"SCREEN-TO-BACK",       SCREENTOBACK},
   {"SCREEN-TO-FRONT",      SCREENTOFRONT},
   {"SELECT-NEXT-ICON",     SELECTNEXTICON},
   {"WINDOW-TO-BACK",       WINDOWTOBACK},
   {"WINDOW-TO-FRONT",      WINDOWTOFRONT},
   {"WINDOW-TO-ICON",       WINDOWTOICON},
};
#define MAXACTION   (sizeof(Action)/sizeof(struct Definition))
#endif

/*
 *  Some shorthand macros used to define the default key layout
 */

#define RAMIGA      IEQUALIFIER_RCOMMAND
#define RSHIFT      IEQUALIFIER_RSHIFT

#define UPARROW     0x4C
#define DOWNARROW   0x4D
#define RIGHTARROW  0x4E
#define LEFTARROW   0x4F
#define BSKEY       0x41
#define TABKEY      0x42
#define RETURNKEY   0x44
#define DELETEKEY   0x46

#define HOTKEY(q,c,a)  {{c,0,q},{0xFF,a,RAMIGA|RSHIFT}}


/*
 *  DefaultKey[] maps the default key layout.  This array is sorted by
 *  KeyCode value.  Change this array to change the default key bindings.
 */

static struct HotKey DefaultKey[] =
{
   HOTKEY(          RAMIGA, BSKEY,      ICONTOWINDOW),
   HOTKEY(          RAMIGA, TABKEY,     SELECTNEXTICON),
   HOTKEY(          RAMIGA, RETURNKEY,  WINDOWTOICON),
   HOTKEY(          RAMIGA, DELETEKEY,  CLOSETHEWINDOW),
   HOTKEY(          RAMIGA, UPARROW,    WINDOWTOFRONT),
   HOTKEY( RSHIFT | RAMIGA, UPARROW,    SCREENTOFRONT),
   HOTKEY(          RAMIGA, DOWNARROW,  WINDOWTOBACK),
   HOTKEY( RSHIFT | RAMIGA, DOWNARROW,  SCREENTOBACK),
   HOTKEY(          RAMIGA, RIGHTARROW, ACTIVATENEXT),
   HOTKEY( RSHIFT | RAMIGA, RIGHTARROW, FRONTTOBACK),
   HOTKEY(          RAMIGA, LEFTARROW,  ACTIVATEPREVIOUS),
   HOTKEY( RSHIFT | RAMIGA, LEFTARROW,  BACKTOFRONT),
};
#define DEFAULTSIZE (sizeof(DefaultKey)/sizeof(struct HotKey))


#ifndef NO_FILE
/*
 *  Error()
 *
 *  Print an error message and the line number where the error occured.
 *  Return the error value.
 */

static int Error(s,x1,x2,x3)
char *s, *x1,*x2,*x3;
{
   printf("Line %2d:  ",LineCount);
   printf(s,x1,x2,x3);
   printf("\n");
   return(BADVALUE);
}


/*
 *  GetNextWord()
 *
 *  Isolate the next word in the line read from the file.
 *  If we are not at the end of the line, then
 *    skip over leading spaces,
 *    set CurWord to point to the beginning of the word,
 *    while we are not at the end of the word,
 *      check if the current character is a word delimiter:
 *      if it is a space or a tab,
 *        skip additional spaces or tabs,
 *        set the terminator character,
 *        and end the word.  
 *        (At this point, CurPos will be pointing to the "real" delimiter,
 *        or to the beginning of the next word, if the delimiter really was
 *        a space).
 *      if it was a NULL, convert it to a new-line.
 *      if it was a dash, comma, colon or new-line,
 *        save the termination character for later,
 *        replace the charactger with a NULL so that CurWord will end at
 *          the end of the word,
 *        and stop looking for more of the word.
 *      otherwise
 *       if we're still looking for the end of the word (i.e., we have not
 *         ended, it by hitting a space),
 *         if its unprintable or the current word is more than one character
 *           long and the current character is not alphanumeric,
 *           then record the bad character and end the word
 *         go on to the next character (i.e., add the current one to the word)
 */
 
static void GetNextWord()
{
   short NotDone = TRUE;
   char c;

   if (TerminationChar != '\n')
   {
      while (*CurPos == ' ' || *CurPos == '\t') CurPos++;
      CurWord = CurPos;
      while (NotDone)
      {
         if (*CurPos == ' ' || *CurPos == '\t')
         {
            *CurPos = '\0';
            while (*(++CurPos) == ' ' || *CurPos == '\t');
            TerminationChar = ' ';
            NotDone = FALSE;
         }
         switch(c = *CurPos)
         {
            case '\0':
               c = *CurPos = '\n';
            case '-':
            case ',':
            case ':':
            case '\n':
               TerminationChar = c;
               *CurPos++ = '\0';
               NotDone = FALSE;
               break;

            default:
               if (NotDone)
               {
                  if (NOTPRINTABLE(c) || (CurPos != CurWord && NOTALPHANUM(c)))
                  {
                     TerminationChar = c;
                     NotDone = FALSE;
                  }
                  CurPos++;
               }
               break;
         }
      }
   }
}


/*
 *  FindWord()
 *
 *  Search the KeyWord array (containing Count entries) for an entry with
 *  Name equal to theWord.  Return the corresponding Code value, or BADVALUE
 *  if theWord does not appear in the KeyWord array.
 *
 *  KeyWord[] must be sorted by name, since we use a binary search to find
 *  theWord witin it.
 */

static int FindWord(theWord,KeyWord,Count)
char *theWord;
struct Definition KeyWord[];
int Count;
{
   int value = BADVALUE;
   register short Min,Max, Num;
   register int comp;
   
   Max = Count; Min = -1;
   while ((Num = (Min + Max) >> 1) != Min && value == BADVALUE)
   {
      comp = stricmp(theWord,KeyWord[Num].Name);
      if (comp < 0) Max = Num; else if (comp > 0) Min = Num;
         else value = KeyWord[Num].Code;
   }
   return(value);
}


/*
 *  FindQualifier()
 *
 *  Find a qualifier name in the Qualifier[] array.
 */
#define FindQualifier(w)    FindWord(w,Qualifier,MAXQUALIFIER)


/*
 *  FindKeyCode()
 *
 *  Find the scan-code for the given key name.  If the key name is a single
 *  ASCII character, use the AsciiToKeyCode array to look up the scan-code
 *  directly, otherwise use FindWord() to search the Key[] array.
 */

static int FindKeyCode(theWord)
char *theWord;
{
   int value = BADVALUE;
   
   if (strlen(theWord) == 1)
   {
      if (PRINTABLE(*theWord)) value = AsciiToKeyCode[*theWord-'!'];
   } else {
      value = FindWord(theWord,Key,MAXKEY);
   }
   return(value);
}


/*
 *  GetKeyCode()
 *
 *  Parse a set of qualifiers and a key name and return the KeyCode longword
 *  that describes the designated key.  SHIFT, AMIGA, and ALT flags are 
 *  used to indicate when more than one key is designated.  Return BADVALUE
 *  if there is an error parsing the line.
 *
 *  Get the next word on the line, and check the termination character.  
 *  Qualifiers end with dashes, commas, or spaces; key-names end with a colon.
 *  Try to find the qualifier or key-name in the proper list, and give an error
 *  if it can not be found, or if the name is null.  For a qualifier, set its
 *  flag bit in the KeyCode longword.  For key-names, set the scan-code and
 *  the set the SHIFT bit in the qualifier flag bits if necessary.
 *  If the end of the line is reached, display an error.  If some other
 *  delimiter was found, then display an error (making unprintable characters
 *  printable).
 *
 *  Once a key-name is found, stop looking for more words.
 */

static void GetKeyCode(theKey)
long *theKey;
{
   short NotDone = TRUE;
   int value;

   *theKey = 0;
   while (NotDone)
   {
      GetNextWord();
      switch(TerminationChar)
      {
         case '-':
         case ',':
         case ' ':
            if (strlen(CurWord))
            {
               value = FindQualifier(CurWord);
               if (value > BADVALUE)
                  *theKey |= (1 << value);
                 else
                  *theKey = Error("Unrecognized key qualifier '%s'",CurWord);
            } else {
               *theKey = Error("Missing qualifier keyword");
            }
            break;

         case ':':
            if (strlen(CurWord))
            {
               value = FindKeyCode(CurWord);
               if (value > BADVALUE)
               {
                  *theKey |= ((value & (~SHIFT)) << 24) |
                             ((value & SHIFT) << 9);
               } else {
                  *theKey = Error("Unrecognized key name '%s'",CurWord);
               }
            } else {
               *theKey = Error("Key name not specified");
            }
            NotDone = FALSE;
            break;

         case '\n':
            *theKey = Error("Key name ends prematurely");
            NotDone = FALSE;
            break;

         default:
            if ((TerminationChar & 0x7F) >= ' ')
               *theKey = Error("Illegal delimiter character '%c'",
                  (char)TerminationChar);
              else
               *theKey = Error("Illegal delimiter character '^%c'",
                  (char)((TerminationChar & 0x7F) + '@'));
            break;
      }
   }
}


/*
 *  GetKeyAction()
 *
 *  Parse the rest of the input line for a key-action keyword.  First, remove
 *  leading and training blanks, and replace the final new-line with a NULL.
 *  Look up the remainder of the line (if any) in the Action[] array, and
 *  if it is not found, report the error.
 */

static void GetKeyAction(theAction)
short *theAction;
{
   *theAction = BADVALUE;

   while (*CurPos == ' ' || *CurPos == '\t') CurPos++;
   CurWord = CurPos;
   CurPos = CurPos + strlen(CurWord) - 1;
   if (*CurPos == '\n') *CurPos-- = '\0';
   while (*CurPos == ' ' || *CurPos == '\t') *CurPos-- = '\0';

   if (*CurWord)
   {
      *theAction = FindWord(CurWord,Action,MAXACTION);
      if (*theAction == BADVALUE) Error("Unrecognized action '%s'",CurWord);
   } else {
      Error("No action specified");
   }
}


/*
 *  KeyCompare()
 *
 *  Return a positive number if the first key is bigger than the second,
 *  zero if they are equal, and a negative number if the second key is 
 *  bigger.  This routine is used by mSort() to sort the keys.
 */

static int KeyCompare(key1,key2)
struct HotKeyItem *key1, *key2;
{
   return(key1->hki_KeyCode - key2->hki_KeyCode);
}


/*
 *
 *  KeyDispose()
 *
 *  Remove a duplicate key definition and report the fact that a key is
 *  multiply defined (it takes a little work to get the qualifier and key
 *  names back out of the arrays).  This routine is called by mSort() when
 *  it finds duplicate keys.
 */

static void KeyDispose(key)
struct HotKeyItem *key;
{
   short i;
   UBYTE code = key->hki_Code & 0x7F;
   int KeyNotFound = TRUE;
   long mask = 0xFFFFFFFF;

   printf("Key ");
   for (i=0; i<MAXQUALIFIER; i++)
      if (key->hki_KeyCode & (1 << Qualifier[i].Code) & mask)
      {
         printf("%s-",Qualifier[i].Name);
         mask &= ~(1 << Qualifier[i].Code);
      }
   for (i=0; i<MAXKEY && KeyNotFound; i++)
      if (code == Key[i].Code)
      {
         printf("%s",Key[i].Name);
         KeyNotFound = FALSE;
      }
   for (i=0; i<MAXASCII && KeyNotFound; i++)
   {
      if (code == AsciiToKeyCode[i])
      {
         printf("%c",i+'!');
         KeyNotFound = FALSE;
      }
   }
   printf(" multiply defined\n");
   KeyCount--;
   key->Next = key->Prev = NULL;
   FreeMem(key,sizeof(*key));
}


/*
 *  GetKeyList()
 *
 *  Read each line from the file (incrementing the line count as we go), 
 *  and skip leading spaces and blank lines.  Get the key code and key action
 *  specified on the line.  If no error was found, add the key definition 
 *  to the linked list of keys defined.  If SHIFT, AMIGA, or ALT were specified,
 *  then add one key each for the left and right version of that qualifier.
 */

static void GetKeyList()
{
   long theKey;
   short theAction;
   struct HotKeyItem *TempKey;
   UWORD mask,multikeys;
   extern char *fgets();

   while (feof(InFile) == FALSE)
   {
      CurPos = fgets(InputLine,LINESIZE,InFile);
      TerminationChar = '\0';
      LineCount++;
      while (*CurPos == ' ' || *CurPos == '\t') CurPos++;

      if (CurPos != NULL && *CurPos != '\0' && *CurPos != '\n')
      {
         GetKeyCode(&theKey);
         GetKeyAction(&theAction);
         if (theKey != BADVALUE && theAction != BADVALUE)
         {
            multikeys = (theKey >> 15) & 0xFE;
            multikeys |= multikeys << 1;
            if (multikeys == 0) multikeys = 1;
            for (mask=1; multikeys; mask<<=1,multikeys>>=1)
            {
               if (multikeys & 1)
               {
                  NEW(HotKeyItem,TempKey);
                  TempKey->hki_KeyCode = theKey;
                  TempKey->hki_Flags   = 0;
                  TempKey->hki_Qual   |= mask >> 1;
                  TempKey->hki_KeyMask = 0xFFFFFFFF;
                  TempKey->hki_Action  = theAction;
                  TempKey->Next = KeyList;
                  KeyList = TempKey;
                  KeyCount++;
               }
            }
         }
      }
   }
}


/*
 *  SetKeyMasks()
 *
 *  For each key scan-code, we OR together the qualifier masks for all the
 *  definitions for that scan-code and set the key Mask value for each
 *  key with that scan-code to the final ORed mask.  That is, the Mask value
 *  indicates what qualifiers are important for determining when a key has
 *  been pressed (and distinguishing it from other definitions using the
 *  same scan-code but different qualifiers).
 */

static void SetKeyMasks()
{
   struct HotKeyItem *CurKey = KeyList;
   struct HotKeyItem *LastKey = CurKey;
   UWORD Mask;

   while (LastKey)
   {
      Mask = 0;
      while (CurKey && CurKey->hki_Code == LastKey->hki_Code)
      {
         Mask |= CurKey->hki_Qual;
         CurKey = CurKey->Next;
      }
      if (Mask == 0) Mask = KEYMASK;
      do
      {
         LastKey->hki_Mask = Mask;
         LastKey = LastKey->Next;
      } while (LastKey != CurKey);
   }
}


/*
 *  MakeKeyArray()
 *
 *  If there are any keys defined, allocate enough space for the KeyArray
 *  that will contain the key definitions, then go through the list and
 *  copy the definitions into the array.  Free each item from the list once it
 *  is copied.  The array saves space (it does not need to contain pointers 
 *  to next and previous items), and allows for easy implementation of a 
 *  binary search on the array.
 */

static void MakeKeyArray()
{
   struct HotKeyItem *TempKey;
   short i;

   if (KeyCount)
   {
      KeyArray = (struct HotKey *)New("KeyArray",KEYARRAYSIZE);
      for (i=0; i<KeyCount; i++)
      {
         KeyArray[i].hk_KeyCode = KeyList->hki_KeyCode;
         KeyArray[i].hk_KeyMask = KeyList->hki_KeyMask;
         TempKey = KeyList; KeyList = KeyList->Next;
         FreeMem(TempKey,sizeof(*TempKey));
      }
   }
}
#endif


/*
 *  MakeDefaultArray()
 *
 *  Copy the DefaultKey[] array into a dynamically allocated array that can
 *  be passed to the input handler and still remain in memory even when the
 *  original process is unloaded.
 */
 
static void MakeDefaultArray()
{
   short i;
   
   KeyCount = DEFAULTSIZE;
   KeyArray = (struct HotKey *)New("KeyArray",KEYARRAYSIZE);
   for (i=0; i<KeyCount; i++)
   {
      KeyArray[i].hk_KeyCode = DefaultKey[i].hk_KeyCode;
      KeyArray[i].hk_KeyMask = DefaultKey[i].hk_KeyMask;
   }
}


/*
 *  GetKeyArray()
 *
 *  If there is a command-line argument, then 
 *    it must be a file name, so try to open it (error if there is a problem).
 *    Create the key definition list from the lines in the file.
 *    If there were no valid key definitions, 
 *      say so,
 *     otherwise, 
 *      sort the key list,
 *      set the key masks for each scan-code,
 *      make the key array from the sorted list.
 *   otherwise (there was no file name given, so)
 *    make the key array from the default key list.
 */

void GetKeyArray(argc,argv)
int argc;
char *argv[];
{
#ifndef NO_FILE
   extern struct HotKeyItem *mSort(); 

   if (argc > 1)
   {
      InFile = fopen(argv[1],"r");
      if (InFile == NULL)
         DoExit("Can't Open File '%s':  Error %d",argv[1],ERROR);
      GetKeyList();
      if (KeyCount == 0)
      {
         DoExit("No valid key definitions found in file '%s'",argv[1]);
      } else {
         KeyList = mSort(KeyList,KeyCompare,KeyDispose);
         SetKeyMasks();
         MakeKeyArray();
      }
   } else
#endif
   {
      MakeDefaultArray();
   }
}
SHAR_EOF
cat << \SHAR_EOF > mSort.c
/*
 *  MSORT.C             version 1.0             18-Nov-1986
 *
 *  A general-purpose implementation of a merge-sort for linked lists.
 *
 *  Written for the Commodore Amiga 1000, version 1.1
 *  using Lattice C v3.03 by Davide P. Cervone
 *
 *  The merge-sort algorithm takes advantage of the fact that it is
 *  easy to combine two sorted lists into one sorted list:  just keep
 *  taking the smaller item off the top of the two lists and add it
 *  to the bottom of the new list until the original lists are empty.
 *
 *  The sort algorithm first places each item to be sorted in its
 *  own list (one item long).  Pairs of these singleton lists are merged
 *  in the manner described above, and we are left with half as many lists
 *  as before, each two items long.  Pairs of these lists are combined to
 *  form half as many lists again, each four items long.  This process is 
 *  repeated until only one list remains.  This is the final, sorted list.
 *
 *  The merge-sort is ideal for linked lists, since it does not require
 *  random access look-ups (which are difficult in linked-lists).  It does 
 *  not require extra "work-space", so no memory is wasted.  Finally, its 
 *  result is a simple, linked-list structure, ready to use (unlike some sort 
 *  methods that result in trees or other structures were it is dificult to 
 *  get from one item to the next).
 *
 *  The merge-sort is fairly efficient.  In it's worst case, it takes
 *  n * (log(n) - 1) + 1 comparisons to sort  n  items (where the log() 
 *  function is the log to base 2).  Thus, for 1024 items, the maximum 
 *  number of comparisons is 1024*(10-1)+1 = 9217, or approximately 9
 *  comparisons per item.  And that's its worst case!  Compare this to
 *  a worst case of n*(n-1)/2 for some popular sorts (e.g., the tree sort).  
 *  The worst case for the tree sort for 1024 items is 523776 compares!
 *
 *  In its best case, the merge-sort takes  n * log(n) / 2  comparisons.
 *  For our example of n=1024, that's 5120 comparisons.  For the tree sort,
 *  the minimum is approximately  n * (log(n) - 2)  comparisons, for a 
 *  minimum of 8192 comparisons for 1024 items. 
 *
 *  Note that the maximum comparisons that will ever occur with the merge-sort 
 *  is no more than  n  more than the minimum number possible with the tree 
 *  sort.  
 *
 *  While these numbers look impressive, there is one drawback:  the merge-
 *  sort is heavily weighted toward its worst case, since the merge sort skips 
 *  comparisons only when one list runs out of items more than one item before 
 *  the other.  In practice, the average number of comparisons made by the 
 *  merge-sort is a consistant 96% of the maximum number.  This gives you a very
 *  acurate estimate of the number of comparisons (hence the time it takes) to 
 *  sort a list of a particular size.  Since the maximum value is so close to
 *  the most probable value, you don't have to worry about pathelogic cases
 *  slowing down your sort routine (for a tree sort, the "pathelogic" case
 *  is a pre-sorted list, which may not be an uncommon case).
 *
 *  In a comparison against the tree sort using random data (the kind the 
 *  tree sort handles best), the merge-sort was found to take 96% of its 
 *  maximum number of sorts, while the tree sort averaged about 38% more than
 *  it's minimum (there was considerably more variation with the tree sort).
 *  Overall, the tree sort was found to take a consitant 25% more comparisons
 *  than the merge-sort on lists of length 300 items to 10000 items.  For
 *  100 items, the difference drops off to about 15%, and for 10 items, the two
 *  sorts are about the same, with the merge-sort comming out a little bit
 *  ahead (one or two comparisons out of an average of 23).
 *
 *  All in all, the merge-sort is an effective, fast, and very useful sort
 *  that compares favorably with other sort algorithms.
 */

#include <exec/types.h>

#define ADD_TO_NEWLIST(l)   (newlist = (newlist->Next = l))

/*
 *  The linked list is a double-linked list (there are pointers to both
 *  the previous and the following item).  The end of the list is marked
 *  by NULL pointers.
 *
 *  The pointers should appear at the top of the structure you intend to
 *  sort, so if you are sorting a linked list of people's names, you might
 *  use a structure like the following in your main program:
 *
 *      struct ListItem
 *      {
 *          struct ListItem *Next;
 *          struct ListItem *Prev;
 *          char FirstName[30];
 *          char MiddleInitial;
 *          char LastName[50]
 *      };
 *
 *  The pointers must be the first fields so that mSort() and Merge() can
 *  find the pointers without having to know anything about the structure
 *  of the data you are sorting.
 */

/*
 *  The #ifndef is so that this module can be #included into the program
 *  that calls mSort(), rather than requiring it to be linked in separately.
 *  To avoid a conflicting definition error, #define NextList to be the
 *  name of the pointer to the previous item as it is declared in your
 *  program (for the example given above, user #define NextList Prev).
 *  be sure that the #define and the struct ListItem declaration both
 *  appear before the #include for this module
 */

#ifndef NextList
struct ListItem
{
   struct ListItem *Next;
   struct ListItem *NextList;
};
#endif

/*
 *  These are the compare and dispose functions passed to mSort() (see
 *  mSort() for more details), and are assigned to global variables so that
 *  they don't have to be passed to mMerge() every time it is called.
 */

static int  (*mCompare)() = NULL;
static void (*mDispose)() = NULL;

/*
 * mMerge()
 *
 *  combines two sorted linked lists into one sorted linked list, using
 *  the mCompare() function to determine the sorting, and the dispose()
 *  function to remove duplicate values.
 *
 *      list1           a pointer to the first linked list
 *      list2           a pointer to the second linked list
 */

struct ListItem *
mMerge(list1,list2)
struct ListItem *list1, *list2;
{
   static struct ListItem top_item;
   static struct ListItem *newlist, *temp;
   static int result;

   top_item.Next = top_item.NextList = NULL;
   newlist = &top_item;

/*
 *  While there are still items in each list, compare the top items in the 
 *  lists.  Link the smaller one to the end of the new, combined list, and
 *  pop the item off the top of the old list.  If the top items are equal, 
 *  then add one of them to the new list, and if there is a dispose function,
 *  get rid of the other one, otherwise add the second one into the list
 *  as well.
 *
 *  When one of the lists is exauseted, add any remaining items from 
 *  the other list onto the end of the result list, and return a pointer
 *  to the final, sorted list.
 */

   while (list1 != NULL && list2 != NULL)
   {
      result = (*mCompare)(list1,list2);
      if (result < 0)
      {
         ADD_TO_NEWLIST(list1);
         list1 = list1->Next;
      }
      else if (result > 0)
      {
         ADD_TO_NEWLIST(list2);
         list2 = list2->Next;
      }
      else
      {
         ADD_TO_NEWLIST(list1);
         list1 = list1->Next;
         if (mDispose == NULL)
         {
            ADD_TO_NEWLIST(list2);
            list2 = list2->Next;
         } else {
            temp = list2;
            list2 = list2->Next;
            temp->Next = temp->NextList = NULL;
            (*mDispose)(temp);
         }
      }
   }
   if (list1 != NULL) ADD_TO_NEWLIST(list1);
   else if (list2 != NULL) ADD_TO_NEWLIST(list2);
   return(top_item.Next);
}


/*
 *  mSort()
 *
 *  sorts a linked list of items of any sort, using a user-provided comparison
 *  routine.  Duplicate items will be removed if the dispose routine is
 *  provided.  The result list will be a doubly-linked list (pointers go both
 *  forward and backward) that is sorted.  mSort() returns a pointer to
 *  the top of the result list.
 *
 *      cur_item        a pointer to the begining of the original, unsorted
 *                      list.
 *      compare()       a pointer to a function that compares two
 *                      ListItems and returns a negative number if
 *                      the first item is smaller, zero if they are
 *                      equal, and a positive number if the first
 *                      item is larger than the second.  Compare() 
 *                      should take two parameters, the pointers
 *                      to the ListItems that are to be compared.
 *      dispose()       disposes of a duplicate ListItem.  Dispose()
 *                      should accept one parameter, the pointer to
 *                      the ListItem to be removed.  Dispose() should
 *                      free any memory allocated to the ListItem.
 *                      This list item will not appear in the final
 *                      linked list.  If dispose in NULL, then 
 *                      duplicate items are retained in the list.
 */

struct ListItem *
mSort(cur_item,compare,dispose)
struct ListItem *cur_item;
int (*compare)();
void (*dispose)();
{
   static struct ListItem *first_item, *next_item;
   
/*
 *  Put the compare and dispose routines where mMerge() can find them
 */
   mCompare = compare;
   mDispose = dispose;

   first_item = NULL;
   if (cur_item != NULL)
   {
/*
 *  Put each item in the original list into a separate list.  Use the
 *  NextList field to link the individual lists into a list (a linked list
 *  of linked lists).  Link the end of the list to the begining so we get
 *  a ring rather than a list.
 */
      first_item = cur_item;
      do
      {
         next_item = cur_item->Next;
         cur_item->Next = NULL;
         cur_item->NextList = (next_item != NULL)? next_item : first_item;
         cur_item = next_item;
      } while (cur_item != NULL);
/*
 *  While there's more than one list left in the ring (i.e., the list 
 *  following the current one is not itself), then merge the current list
 *  with the one following it (keep track of the first list after the ones
 *  we're combining so we can link the result of the merge back into
 *  the ring).  Finally, if there were more than two lists in the ring
 *  (i.e., if the current list is neither equal to the one preceding it nor
 *  equal to the one after the list with which it was combined), then 
 *  link the result list into the ring, otherwise link the result list
 *  to itself (since there were only two lists in the ring, their
 *  merger should leave us with only one).  Move down the ring, so we
 *  can merge the next two lists in the ring.
 */
      while ((cur_item = first_item->NextList) != first_item)
      {
         next_item = cur_item->NextList->NextList;
         cur_item = mMerge(cur_item,cur_item->NextList,compare,dispose);
         if (cur_item == first_item || cur_item == next_item)
         {
            cur_item->NextList = cur_item;
         } else {
            first_item->NextList = cur_item;
            cur_item->NextList = next_item;
         }
         first_item = cur_item;
      }
/*
 *  When there's only one list left, traverse the list, setting the
 *  reverse pointers so that the list is properly linked both directions.
 */
      first_item->NextList = NULL;
      while (cur_item->Next != NULL)
      {
         cur_item->Next->NextList = cur_item;
         cur_item = cur_item->Next;
      }
   }
   return(first_item);
}
SHAR_EOF
cat << \SHAR_EOF > wKeys-Handler.c
/*
 *  wKeys-Handler.c   Input Handler for wKeyw, which moves and activates 
 *                    windows and screensin response to keystrokes
 *
 *              Copyright (c) 1987,1988 by Davide P. Cervone
 *  You may use this code provided this copyright notice is left intact.
 */

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

#include "wKeys.h"

static char *program = "wKeys-Handler";
static int   version = 3;
static char *date    = "January 1988";
static char *author  = "Copyright (c) 1987,1988 by Davide P. Cervone";

extern struct Layer *WhichLayer();
extern struct IntuiMessage *GetMsg();
extern struct IntuiMessage *AllocMem();
extern void myHandlerStub();


#define WINDOW(layer)   ((struct Window *)((layer)->Window))

#define SCREENTOP\
   (theScreen->TopEdge << ((theScreen->ViewPort.Modes & LACE)? 0: 1))

#define IMSIZE          ((ULONG)sizeof(struct IntuiMessage))
#define NEWMESSAGE(m)   (m = AllocMem(IMSIZE,MEMF_PUBLIC | MEMF_CLEAR))
#define FREEMESSAGE(m)  FreeMem(m,IMSIZE);

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

static struct HotKey *Key = NULL;
static int KeyCount = 0;
static struct MsgPort *ReplyPort = NULL;


/*
 *  Setup()
 *
 *  wKeys calls LoadSeg() to get this handler into memory.  The segment
 *  that it gets points to this routine.  wKeys calls Setup() and 
 *  passes the IntuitionBase, LayersBase and SysBase pointers that it
 *  has initialized (with OpenLibrary()).  wKeys also passes the KeyArray
 *  and KeyCount which hold the key bindings.  Setup returns a pointer to
 *  the actual input handler, which wKeys installs, and sets the version
 *  number so that hotKey can report the handler's version.
 */

long Setup(Ibase,Lbase,Sbase,theKeys,Count,Port,VersionPtr)
struct IntuitionBase *Ibase;
struct LayersBase *Lbase;
struct SysBase *Sbase;
struct HotKey *theKeys;
struct MsgPort *Port;
int *VersionPtr;
int Count;
{
   IntuitionBase = Ibase;
   LayersBase = Lbase;
   SysBase = Sbase;
   Key = theKeys;
   KeyCount = Count;
   ReplyPort = Port;
   *VersionPtr = version;
   return((long) &myHandlerStub);
}


/*
 *  TopWindow()
 *
 *  Find the top window of the specified screen.  Start at the top layer of
 *  the screen and move backward as long as the layer exists and has no
 *  window connected to it.  Return the window associated with the final 
 *  layer, if any.
 */

static struct Window *TopWindow(theScreen)
struct Screen *theScreen;
{
   struct Window *theWindow = NULL;
   struct Layer *theLayer;
   
   theLayer = theScreen->LayerInfo.top_layer;
   while (theLayer && WINDOW(theLayer) == NULL) theLayer = theLayer->back;
   if (theLayer) theWindow = WINDOW(theLayer);
   return(theWindow);
}


/*
 *  BottomWindow()
 *
 *  Find the bottom window of the specified screen.  Start at the top layer
 *  and as long as the layer exists, go to the next layer back.  If the
 *  layer has a window attached, consider that to be the bottom window until
 *  a lower one is found.
 */

static struct Window *BottomWindow(theScreen)
struct Screen *theScreen;
{
   struct Window *theWindow = NULL;
   struct Layer *theLayer = theScreen->LayerInfo.top_layer;
   
   while (theLayer)
   {
      if (WINDOW(theLayer) && isIconified(WINDOW(theLayer)) == FALSE)
         theWindow = WINDOW(theLayer);
      theLayer = theLayer->back;
   }
   return(theWindow);
}


/*
 *  NextWindow()
 *
 *  Find the next window below the specified window (wrap arround to the top
 *  if the window is the bottom one).  Start with the window's layer and go
 *  back until a layer with a window is found, or no more layers exist.  If
 *  a window was found, return it, otherwise, use the top window.
 */

static struct Window *NextWindow(theWindow)
struct Window *theWindow;
{
   struct Layer  *theLayer  = theWindow->WLayer;
   
   do
      theLayer = theLayer->back;
   while (theLayer && (WINDOW(theLayer) == NULL ||
          isIconified(WINDOW(theLayer))));
   if (theLayer)
      theWindow = WINDOW(theLayer);
     else
      theWindow = TopWindow(theWindow->WScreen);
   return(theWindow);
}


/*
 *  PreviousWindow()
 *
 *  Find the window that is on top of the specified window (or NULL if there
 *  are no windows above it).  Start with the window's layer, and move to
 *  the layer in front until a layer with a (different) window is found, or
 *  until no more layers exist.  If a window was found, return it, otherwise
 *  return NULL.
 */
 
static struct Window *PreviousWindow(theWindow)
struct Window *theWindow;
{
   struct Layer  *theLayer  = theWindow->WLayer;
   
   do
      theLayer = theLayer->front;
   while (theLayer && (WINDOW(theLayer) == NULL ||
                       WINDOW(theLayer) == theWindow));
   if (theLayer)
      theWindow = WINDOW(theLayer);
     else
      theWindow = NULL;
   return(theWindow);
}


/*
 *  BackScreenToFront()
 *
 *  Bring the bottom-most screen to the top, and activate its top window.
 *  While there is a screen following the current one, move the the next screen.
 *  Bring that screen to the front and find its top window.  If one was found,
 *  activate the window.
 */

static void BackScreenToFront()
{
   struct Screen *theScreen = IntuitionBase->FirstScreen;
   struct Window *theWindow;
   
   if (theScreen)
   {
      while (theScreen->NextScreen) theScreen = theScreen->NextScreen;
      ScreenToFront(theScreen);
      theWindow = TopWindow(theScreen);
      if (theWindow) ActivateWindow(theWindow);
   }
}


/*
 *  FrontScreenToBack()
 *
 *  Move the top screen to the back and activate the top window on the new
 *  top screen.
 */

static void FrontScreenToBack()
{
   struct Screen *theScreen = IntuitionBase->FirstScreen;
   struct Window *theWindow;

   if (theScreen)
   {
      ScreenToBack(theScreen);
      theScreen = IntuitionBase->FirstScreen;
      if (theScreen)
      {
         theWindow = TopWindow(theScreen);
         if (theWindow) ActivateWindow(theWindow);
      }
   }
}


/*
 *  ActivatePreviousWindow()
 *
 *  Get the window previous to the current window (if none, then get the
 *  bottom window in the active screen), and activate that window.
 */

static void ActivatePreviousWindow()
{
   struct Window *theWindow = PreviousWindow(IntuitionBase->ActiveWindow);
   
   if (theWindow == NULL) theWindow = BottomWindow(IntuitionBase->ActiveScreen);
   if (theWindow) ActivateWindow(theWindow);
}


/*
 *  ActivateNextWindow()
 *
 *  Get the window below the current window and activate it.
 */

static void ActivateNextWindow()
{
   struct Window *theWindow = NextWindow(IntuitionBase->ActiveWindow);
   
   if (theWindow) ActivateWindow(theWindow);
}


/*
 *  CurrentWindowToBack()
 *
 *  Send the current window to the back of the list.
 */

static void CurrentWindowToBack()
{
   struct Window *theWindow = IntuitionBase->ActiveWindow;
   
   if (theWindow && (theWindow->Flags & BACKDROP) == 0)
      WindowToBack(theWindow);
}


/*
 *  CurrentWindowToFront()
 *
 *  Send the current window to the top of the list.
 */

static void CurrentWindowToFront()
{
   struct Window *theWindow = IntuitionBase->ActiveWindow;
   
   if (theWindow) WindowToFront(theWindow);
}


/*
 *  BackWindowToFront()
 *
 *  Move the bottom window to the top and activate it.  Get the bottom window,
 *  skipping over backdrop windows.  If one is found, bring it to the front,
 *  and activate it.
 */

static void BackWindowToFront()
{
   struct Window *theWindow = BottomWindow(IntuitionBase->ActiveScreen);
   
   if (theWindow)
   {
      while (theWindow && (theWindow->Flags & BACKDROP))
         theWindow = PreviousWindow(theWindow);
      if (theWindow)
      {
         WindowToFront(theWindow);
         ActivateWindow(theWindow);
      }
   }
}


/*
 *  FrontWindowToBack()
 *
 *  Move the top window to the back, and activate the new top window.
 *  Get the top window, and then the window following it.  Send the top window
 *  to the back, and activate the next window.
 */

static void FrontWindowToBack()
{
   struct Window *theWindow  = TopWindow(IntuitionBase->ActiveScreen);
   struct Window *nextWindow = NextWindow(theWindow);
   
   if (theWindow && (theWindow->Flags & BACKDROP) == 0)
   {
      WindowToBack(theWindow);
      if (nextWindow) ActivateWindow(nextWindow);
   }
}


/*
 *  CloseCurrentWindow()
 *
 *  If the current window has the CLOSEWINDOW IDCMP flag, then 
 *  send it a CLOSEWINDOW IDCMP message.  Unfortunately, the workbench
 *  has the CLOSWINDOW flag set, and it crashes if you send it a close
 *  message; therefore, CloseCurrentWindow will not sent a CLOSWINDOW
 *  event to a BACKDROP window.  This seemed the easiest way to rule out
 *  the workbench without too much overhead.
 *
 *  The message will be replied to the ReplyPort, which is checked in the 
 *  input handler.  Any messages that get returned are freed.
 */

static void CloseCurrentWindow()
{
   struct Window *theWindow = IntuitionBase->ActiveWindow;
   struct IntuiMessage *theMessage;
   
   if (theWindow && (theWindow->IDCMPFlags & CLOSEWINDOW) && 
      (theWindow->Flags & BACKDROP) == 0)
   {
      if (NEWMESSAGE(theMessage))
      {
         theMessage->Class = CLOSEWINDOW;
         theMessage->IDCMPWindow = theWindow;
         theMessage->ExecMessage.mn_ReplyPort = ReplyPort;
         PutMsg(theWindow->UserPort,theMessage);
      }
   }
}


/*
 *  WindowToIcon()
 *
 *  Iconify the current window if wIconify is running.
 */

static void WindowToIcon()
{
   struct Window *theWindow = IntuitionBase->ActiveWindow;
   
   if (theWindow) wIconifyWindow(theWindow);
}


/*
 *  IconToWindow()
 *
 *  Restore the selected wIconify icon to a window.  The workbench must
 *  be the active window, and wIconify must be running, and a window icon
 *  must be selected.
 */
 
static void IconToWindow()
{
   wRestoreWindow(NULL);
}


/*
 *  SelectNextIcon()
 *
 *  Tells wIconify to select the next window icon on the workbench.  If no
 *  icon is selected, then select the first icon.
 */

static void SelectNextIcon()
{
   wNextIcon();
}


/*
 *  Array of functions that perform the different actions associated with 
 *  the hot-keys.  These are called by the handler routine.
 */

typedef void (*FUNCTION)();

static FUNCTION Action[] =
{
   NULL,
   &BackScreenToFront,
   &FrontScreenToBack,
   &ActivatePreviousWindow,
   &ActivateNextWindow,
   &CurrentWindowToBack,
   &CurrentWindowToFront,
   &BackWindowToFront,
   &FrontWindowToBack,
   &CloseCurrentWindow,
   &WindowToIcon,
   &IconToWindow,
   &SelectNextIcon
};


/*
 *  myHandler()
 *
 *  This is the input handler.  
 *  First check if there are any returned CLOSEWINDOW events, then,
 *  For each event in the event list:
 *  If the event is a raw key event, then
 *    make the KeyCode longword for that event's code and qualifier,
 *    binary search the Key[] array for a matching entry (only consider
 *      the qualifiers specified by the KeyMask).  Since most keys pressed
 *      will NOT match a hot-key, we want the search to be as fast as 
 *      possible, so we use a binary search rather than a linear search.
 *    set NoHotKey if the key is not a hot key.
 *  if the key was not a hot key,
 *    go on to the next key
 *   otherwise,
 *    perform the function for the specified hot key,
 *    remove the hot key from the event list.
 *
 *  When all the events have been checked, return the event list so that
 *  Intuition can do its thing.
 */

struct InputEvent *myHandler(EventList,data)
struct InputEvent *EventList;
APTR data;
{
   register struct InputEvent **EventPtr = &EventList;
   register struct InputEvent *theEvent;
   register long   theKey;
   register short  Num,Min,Max;
   register long   NoHotKey;
   struct IntuiMessage *theMessage;

   while (theMessage = GetMsg(ReplyPort)) FREEMESSAGE(theMessage);

   Forbid();
   while((theEvent = *EventPtr) != NULL)
   {
      NoHotKey = TRUE;
      if (theEvent->ie_Class == IECLASS_RAWKEY)
      {
         theKey = KEY(theEvent);
         Max = KeyCount; Min = -1;
         while ((Num = (Min + Max) >> 1) != Min &&
                (NoHotKey = (theKey & Key[Num].hk_KeyMask) -
                             Key[Num].hk_KeyCode) != 0)
            if (NoHotKey > 0) Min = Num; else Max = Num;
      }
      if (NoHotKey)
      {
         EventPtr = &(theEvent->ie_NextEvent);
      } else {
         (*(Action[Key[Num].hk_Action]))();
         *EventPtr = theEvent->ie_NextEvent;
      }
   }
   Permit();
   return(EventList);
}
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 > wKeys.h
/*
 *  wKeys.h    Structure definitions for wKeys program, which moves
 *             and activates windows and screens via keystrokes.
 *
 *             Copyright (c) 1987,1988 by Davide P. Cervone
 *  You may use this code provided this copyright notice is left intact.
 */


/*
 *  HotKey is the structure that holds the information needed to bind a
 *  key to a function.  Each HotKey is a pair of longwords, the first
 *  represents a combination of the key-code and qualifier mask for the
 *  key to be bound to a function.  Using a single longword for this makes
 *  it easy to compare against the currently pressed key.
 *
 *  The second longword represents the qualifier mask that specifies the 
 *  qualifier flags that are important to distinguish this key binding from
 *  other bindings with the same scan-code but different qualifiers.  It is
 *  the logical OR of all the qualifiers used by any key definition with
 *  the same scan-code as this one.  This longword contains 0xFF in the 
 *  same position where the first one has the scan-code, so ANDing this mask
 *  against a KeyCode longword does not mask out the key code.
 *
 *  The second longword also contains a byte that represents the action that
 *  the key will perform when it is pressed.
 */

struct HotKey
{
   union
   {
      struct
      {
         UBYTE Code;                /* the InputEvent ie_Code field */
         UBYTE Flags;               /* SHIFT, AMIGA, and ALT (0 otherwise) */
         UWORD Qualifiers;          /* the InputEvent ie_Qualfiers field */
      } PartCode;
      long FullCode;                /* KeyCode longword */
   } Key;
   union
   {
      struct
      {
         UBYTE FF;                  /* always 0xFF */
         UBYTE Action;              /* the action to be performed by this key */
         UWORD Mask;                /* the qualifier mask */
      } PartMask;
      long FullMask;                /* the KeyMask longword */
   } Mask;
};

#define hk_Code     Key.PartCode.Code
#define hk_Flags    Key.PartCode.Flags
#define hk_Qual     Key.PartCode.Qualifiers
#define hk_KeyCode  Key.FullCode
#define hk_FF       Mask.PartMask.FF
#define hk_Action   Mask.PartMask.Action
#define hk_Mask     Mask.PartMask.Mask
#define hk_KeyMask  Mask.FullMask


/*
 *  This is the structure used in the linked list when building the key
 *  definition array.  It is the same as a HotKey except it contains pointers
 *  to the next and previous items so that it can be sorted by mSort().
 */

struct HotKeyItem
{
   struct HotKeyItem *Next;
   struct HotKeyItem *Prev;
   struct HotKey HotKey;
};

#define hki_Code     HotKey.hk_Code
#define hki_Flags    HotKey.hk_Flags
#define hki_Qual     HotKey.hk_Qual
#define hki_KeyCode  HotKey.hk_KeyCode
#define hki_FF       HotKey.hk_FF
#define hki_Action   HotKey.hk_Action
#define hki_Mask     HotKey.hk_Mask
#define hki_KeyMask  HotKey.hk_KeyMask


/*
 *  These are the actions that can be perfomed.  They must correspond to the
 *  Action[] array in the input handler.  You can add functions to the end
 *  of this list provided you add them into the Action[] array in 
 *  wKeys-Handler.c, and the Action[] array in BindWKeys.c.
 */

#define SCREENTOFRONT     1
#define SCREENTOBACK      2
#define ACTIVATEPREVIOUS  3
#define ACTIVATENEXT      4
#define WINDOWTOBACK      5
#define WINDOWTOFRONT     6
#define BACKTOFRONT       7
#define FRONTTOBACK       8
#define CLOSETHEWINDOW    9
#define WINDOWTOICON      10
#define ICONTOWINDOW      11
#define SELECTNEXTICON    12

#define KEYCODE(qual,key)       (((key)<<24)|(qual))
#define KEY(e)                  KEYCODE((e)->ie_Qualifier,(e)->ie_Code)
SHAR_EOF
cat << \SHAR_EOF > wKeys.lnk
FROM LIB:c.o+wKeys.o+BindWKeys.o+mSort.o
TO wKeys
LIB LIB:lc.lib+LIB:amiga.lib
NODEBUG
SMALLDATA
SMALLCODE
SHAR_EOF
cat << \SHAR_EOF > wKeys-Handler.lnk
FROM wKeys-Handler.o+HandlerStub.o+wIconCalls.o
TO wKeys-Handler
LIB LIB:amiga.lib+LIB:lc.lib
NODEBUG
SMALLDATA
SMALLCODE
SHAR_EOF
cat << \SHAR_EOF > HandlerStub.o.uu
begin 17 HandlerStub.o
M```#YP````1H86YD;&5R<W1U8BYO```````#Z`````%T97AT```#Z0````1(0
MYP#`3KD`````4(].=0`````#[X$```-?;7E(86YD;&5R```````!````!@$`1
>``1?;7E(86YD;&5R4W1U8@````````````````/R:
``
end
SHAR_EOF
cat << \SHAR_EOF > wIconCalls.o.uu
begin 17 wIconCalls.o
M```#YP````-W:6-O;F-A;&QS+F\```/I````,DY5__1(>0````!.N0````!8-
MCT*M__0K0/_X2H!G0"\\``$``7`:+P!.N0````!0CRM`__Q*@&<F($`Q;0`*5
M`!@A;0`,`!1"J``.+P`O+?_X3KD`````4(]P`2M`__0@+?_T3EU.=4Y5```OU
M+0`(<`$O`&&,4(].74YU3E4``"\M``AP`B\`80#_>%"/3EU.=4*G<`4O`&$`*
M_VA0CTYU3E7__$*M__Q*K0`(9Q(@;0`(""@`!P`99P9P`2M`__P@+?_\3EU.S
M=0`````#[`````$````!````!@````````/O@0```U]&:6YD4&]R=```````G
M``$````,@0```U]!;&QO8TUE;0````````$````J@0```E]0=71-<V<`````G
M`0```%(!```$7W=)8V]N:69Y5VEN9&]W`````&8!```$7W=297-T;W)E5VEN*
M9&]W`````'H!```#7W=.97AT26-O;@``````D`$```-?:7-)8V]N:69I960`?
M``">`````````_(```/J````!'=)8V]N:69Y4&]R=`````````/R```#ZP``4
&``````/RU
``
end
SHAR_EOF
#	End of shell archive
exit 0


------- End of Forwarded Message