/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *      (C) Copyright 1991 by Peter Vorwerk
 *
 *      Name .....: PCKeyMap.c
 *      Created ..: Saturday 16-Mar-91 10:39
 *      Revision .: 0
 *
 *      Date            Author          Comment
 *      =========       ========        ====================
 *      16-Mar-91       Peter Vorwerk   Created this file!
 *
 * $Revision Header ********************************************************/
 #define REVISION 0

#define VERSION 1

#include <stdio.h>
#include <functions.h>
#include <exec/execbase.h>
#include <exec/tasks.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <exec/libraries.h>
#include <exec/interrupts.h>
#include <exec/nodes.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <libraries/arpbase.h>
#include <janus/janus.h>

void Input_Code(void); /* Predefine the ASM-Handler */

struct MsgPort    *IPort = NULL;
struct IOStdReq   *InputRequest = NULL;
struct Interrupt  Input_Handler;
struct Task       *MyTask;

ULONG User_Routine;
ULONG Qualifier , Code , MySignal, data , SigNum = 0L;

/* These codes are PC RawCodes.

   PC_ALT   means press the ALT key
   PC_nALT  means release the ALT key
   
   PC_x     means press the x key of the numeric block */

/* Remember, you MUST press and relase control keys as CTRL, ALT
   or SHIFT, while normal keys need only be pressed. */

#define PC_ALT    (UBYTE) '\070'
#define PC_nALT   (UBYTE) '\270'
#define PC_0      (UBYTE) '\122'
#define PC_1      (UBYTE) '\117'
#define PC_2      (UBYTE) '\120'
#define PC_3      (UBYTE) '\121'
#define PC_4      (UBYTE) '\113'
#define PC_5      (UBYTE) '\114'
#define PC_6      (UBYTE) '\115'
#define PC_7      (UBYTE) '\107'
#define PC_8      (UBYTE) '\110'
#define PC_9      (UBYTE) '\111'

struct IntuitionBase *IntuitionBase;
struct Library       *JanusBase   = NULL;
UBYTE                *KeyB        = NULL;
UBYTE                *IntR        = NULL;

/******************************
*                             *
*         PCKeyB()            *
*                             *
******************************/

/* Calculate the address of the keyboard-register (KeyB) and
   the keyboard-interrupt (IntR) of the XT/AT board. By writing
   a value in the KeyB and sending the interrupt, the XT/AT will
   assume we have pressed or released this key on the keyboard. */

void PCKeyB(void)
{
    UBYTE *ptr;
    UWORD *offset;

/* Die Keyboard Addresse ist als positiver Offset 0x72 in der Janus.library abgelegt. */
/* Achtung! Im 'Amiga SYSTEM-Handbuch wird die feste Addresse 0x7ffff als Keyboard Addresse
   genannt. Bei meinem Amiga 2000 mit AT Karte stimmte dies, jedoch nicht beim SideCar.

   Die Offset Struktur wurde experimentell ( teilweiser Speicherdump vom Programm
   PCWINDOW ) ermittelt. Zum Glück stimmte wenigstens die Adresse des Interrupts,
   so konnte ich im Speicher nach dem Auftreten dieser Kombination suchen und die
   nähere Umgebung disassemblieren. */

/* Keyboard address. Please note 0x7ffff is NOT the correct address in ALL Systems.
   You must calculate it by contents of (JanusBase + 0x72) + 0x7e000 !! */

/* I found this by disassembling the file PCWINDOW. */

    KeyB    = (UBYTE *) JanusBase;
    KeyB    += 0x72;
    offset  = (UWORD *) KeyB;
    ptr     = (UBYTE *) GetJanusStart();
    KeyB    = ptr + 0x7e000;
    KeyB    += *offset;
    IntR    = ptr + 0x7fffb; /* Addresse aus 'Amiga SYSTEM-Handbuch' */
}

/*******************************
*                              *
*        Handler()             *
*                              *
*******************************/

/* This is the C-code of my handler. This function will 
   test, if a key has been pressed or released. If so this
   function test, if the actual window title start with the
   sequence " PC ". If this is true also, send a signal to
   the main process. The main process will determine which
   action should be done. */

struct InputEvent *Handler(struct InputEvent *Input, ULONG *Data)
{
    char *s;
    struct InputEvent *Next;
    
    Next = Input;
    while(Next)
    {
/* Is the Event produced of the keyboard ? */

        if (Next->ie_Class == IECLASS_RAWKEY)
        {
/* Starts the actual window with the sequence " PC " ? */

            s = (char *) IntuitionBase->ActiveWindow->Title;
            if (strncmp(" PC ",s,4) == 0)
            {
/* Save the Code and the Qualifier of the last Event ... */

                Qualifier = Next->ie_Qualifier;
                Code = Next->ie_Code;
/* ... and send a signal to the main process. */

                Signal(MyTask,MySignal);
            }
        }
/* Search for the next Event. */

        Next = Next->ie_NextEvent;
    }
/* Return the Event to the System, so that the other handlers
   can use it too. */

    return(Input);
}

/***********************
*                      *
*   InitHandler()      *
*                      *
***********************/

/* Start the Handler. */

void InitHandler(void)
{
    data = 0L;
    
    User_Routine                  = (ULONG) Handler;
    Input_Handler.is_Data         = (APTR) &data;
    Input_Handler.is_Code         = (void (*)())Input_Code;
    Input_Handler.is_Node.ln_Pri  = 51;
    Input_Handler.is_Node.ln_Name = "PCKeyMap-Handler";
    InputRequest->io_Data         = (APTR) &Input_Handler;
    InputRequest->io_Command      = IND_ADDHANDLER;
    
    DoIO((struct IORequest *) InputRequest);
}

/**********************
*                     *
*    Input_Code()     *
*                     *
**********************/

/* This is the assembler routine of the handler. We need it,
   because the system will send the parameter in a register.
   But a C-funtion search for the parameter on the stack. */

#asm
    public _geta4
    public _Input_Code
_Input_Code:
    move.l   a4,-(sp)
    jsr      _geta4
    movem.l  a0/a1,-(sp)
    move.l   _User_Routine,a0
    jsr      (a0)
    movem.l  (sp)+,a0/a1
    move.l   (sp)+,a4
    rts
#endasm

/********************
*                   *
*    CloseAll()     *
*                   *
********************/

/* Normaly this function is not called. Only if an error
   occurs during the setup this function will be called.
   
   Try to close all open stuff. */

void CloseAll(void)
{
    if (SigNum)
    {
        FreeSignal(SigNum);
    }
    if (InputRequest->io_Device)
    {
        CloseDevice((struct IORequest *) InputRequest);
    }
    if (InputRequest)
    {
        DeleteStdIO(InputRequest);
    }
    if (IPort)
    {
        DeletePort(IPort);
    }
    exit(10);
}

/************************
*                       *
*     OpenAll()         *
*                       *
************************/

/* Open the libraries and the ports. */

void OpenAll(void)
{
    if (!(JanusBase = ArpOpenLibrary("janus.library",0L)))
    {
        Printf("\nCan't find JANUS.library\nAborted...\n");
        exit(10);
    }
    if (!(IPort = CreatePort(NULL,0L)))
    {
        CloseAll();
    }
    if (!(InputRequest = CreateStdIO(IPort)))
    {
        CloseAll();
    }
    if (OpenDevice("input.device",0L,(struct IORequest *) InputRequest,0L))
    {
        CloseAll();
    }
/* Get a signal for our own Task */

    if ((SigNum = AllocSignal(-1L)) < 0)
    {
        CloseAll();
    }

    MySignal = 1 << SigNum;

/* Now start the Handler. */

    InitHandler();
}    

/********************
*                   *
*    SendKey()      *
*                   *
********************/

/* This function will send the values to the keyboard-register and
   send the interrupt. */

void SendKey(UBYTE k1, UBYTE k2, UBYTE k3)
{
    *KeyB   = PC_ALT;
    *IntR   = '\377';    /* 0xff */
    Delay(1);
    *KeyB   = k1;
    *IntR   = '\377';    /* 0xff */
    Delay(1);
    *KeyB   = k2;
    *IntR   = '\377';    /* 0xff */
    Delay(1);
    *KeyB   = k3;
    *IntR   = '\377';    /* 0xff */
    Delay(1);
    *KeyB   = PC_nALT;
    *IntR   = '\377';    /* 0xff */
    Delay(1);
}

/********************
*                   *
*    CheckKey()     *
*                   *
********************/

/* Check which key has been pressed. */

void CheckKey(void)
{
    switch(Code)
    {
        case 0x00: if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                   {
                       SendKey(PC_1,PC_2,PC_6);
                   }
                   else
                   {
                       SendKey(PC_0,PC_9,PC_6);
                   }
                   break;
                   
        case 0x01: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_1,PC_7,PC_3);
                       }
                       else
                       {
                           SendKey(PC_0,PC_4,PC_9);
                       }
                   }
                   break;
                   
        case 0x02: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_6,PC_4);
                       }
                       else
                       {
                           SendKey(PC_0,PC_5,PC_0);
                       }
                   }
                   break;
                   
        case 0x03: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_3,PC_5);
                       }
                       else
                       {
                           SendKey(PC_0,PC_5,PC_1);
                       }
                   }
                   break;
                   
        case 0x04: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_3,PC_6);
                       }
                       else
                       {
                           SendKey(PC_0,PC_5,PC_2);
                       }
                   }
                   break;
                   
        case 0x05: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_3,PC_7);
                       }
                       else
                       {
                           SendKey(PC_0,PC_5,PC_3);
                       }
                   }
                   break;
                   
        case 0x06: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_9,PC_4);
                       }
                       else
                       {
                           SendKey(PC_0,PC_5,PC_4);
                       }
                   }
                   break;
                   
        case 0x07: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_3,PC_8);
                       }
                       else
                       {
                           SendKey(PC_0,PC_5,PC_5);
                       }
                   }
                   break;
                   
        case 0x08: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_4,PC_2);
                       }
                       else
                       {
                           SendKey(PC_0,PC_5,PC_6);
                       }
                   }
                   break;
                   
        case 0x09: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_4,PC_0);
                       }
                       else
                       {
                           SendKey(PC_0,PC_5,PC_7);
                       }
                   }
                   break;
                   
        case 0x0a: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_4,PC_1);
                       }
                       else
                       {
                           SendKey(PC_0,PC_4,PC_8);
                       }
                   }
                   break;
                   
        case 0x0b: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_9,PC_5);
                       }
                       else
                       {
                           SendKey(PC_0,PC_4,PC_5);
                       }
                   }
                   break;
                   
        case 0x0c: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_4,PC_3);
                       }
                       else
                       {
                           SendKey(PC_0,PC_6,PC_1);
                       }
                   }
                   break;
                   
        case 0x0d: if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                   {
                       SendKey(PC_1,PC_2,PC_4);
                   }
                   else
                   {
                       SendKey(PC_0,PC_9,PC_2);
                   }
                   break;
                   
        case 0x1a: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_1,PC_2,PC_3);
                       }
                       else
                       {
                           SendKey(PC_0,PC_9,PC_1);
                       }
                   }
                   break;
                   
        case 0x1b: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_1,PC_2,PC_5);
                       }
                       else
                       {
                           SendKey(PC_0,PC_9,PC_3);
                       }
                   }
                   break;
                   
        case 0x29: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_5,PC_8);
                       }
                       else
                       {
                           SendKey(PC_0,PC_5,PC_9);
                       }
                   }
                   break;
                   
        case 0x2a: if (Qualifier & IEQUALIFIER_LALT || Qualifier & IEQUALIFIER_RALT )
                   {
                       if (Qualifier & IEQUALIFIER_LSHIFT || Qualifier & IEQUALIFIER_RSHIFT )
                       {
                           SendKey(PC_0,PC_3,PC_4);
                       }
                       else
                       {
                           SendKey(PC_0,PC_3,PC_9);
                       }
                   }
                   break;
    }
}


int main(int argc, char *argv[])
{
    if (MyTask = FindTask("PCKeyMap-Handler"))
    {
        Printf("[33mPCKeyMap[0m is already installed !\n");
        exit(5);
    }
    if (!(MyTask = FindTask(NULL)))
    {
        CloseAll();
    }
    MyTask->tc_Node.ln_Name = "PCKeyMap-Handler";
/* Set own process name, so we can determine if the program is already in memory. */

    OpenAll();
    Printf("Installing [33mPCKeyMap[0m V%ld.%ld by Peter Vorwerk\t[1mPUBLIC DOMAIN[0m\n",VERSION,REVISION);    

    PCKeyB(); /* Calculate the KeyBoardAddress. */

/* Do forever ... */
    for(;;)
    {
        Wait(MySignal);
        CheckKey();
    }
/* We will never reach this statement, but my Compiler needs it. */
    return(0);
}
