/*
 *  wKeys-Handler.c   Input Handler for wKeyw, which moves and activates 
 *                    windows and screensin response to keystrokes
 *
 *              Copyright (c) 1987 by Davide P. Cervone
 *  You may use this code provided this copyright notice is left intact.
 */

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

#include "wKeys.h"

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

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


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

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


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

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


/*
 *  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,VersionPtr)
struct IntuitionBase *Ibase;
struct LayersBase *Lbase;
struct SysBase *Sbase;
struct HotKey *theKeys;
int *VersionPtr;
int Count;
{
   IntuitionBase = Ibase;
   LayersBase = Lbase;
   SysBase = Sbase;
   Key = theKeys;
   KeyCount = Count;
   *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))
         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);
   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) 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)
   {
      WindowToBack(theWindow);
      if (nextWindow) ActivateWindow(nextWindow);
   }
}


/*
 *  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
};


/*
 *  myHandler()
 *
 *  This is the input handler.  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;

   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);
}
