From: ahh@j.cc.purdue.edu (Brent L. Woods)
Newsgroups: comp.sources.amiga
Subject: wKeys--attach window functions to function keys.
Message-ID: <6395@j.cc.purdue.edu>
Date: 9 Feb 88 04:52:12 GMT
Organization: Co-Moderators Unlimited.
Lines: 2242
Keywords: not compiled, not tested.
Approved: ahh@j.cc.purdue.edu  (Brent Woods)
 
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
# 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
#    End of shell archive
exit 0
