/*******************************************************************
 *
 * Name: kbdtest.c
 *
 * Function:
 *     Implement prototype of keyboard event processing
 *
 * Author:
 *     Ken Borgendale           kwb@vnet.ibm.com
 *
 * Copyright:
 *     Copyright (C) IBM Corp. 1995
 *
 * Notes:
 *     This is a small testcase which uses the UniKeyboard functions.
 *     It is a simplified version of the code used in OS/2 for PowerPC
 *     Event Services.
 *
 * Syntax:
 *     kbdtest  testfile  kblfile
 *
 *     The testfile consists of a set of lines.  Each line can be a
 *     comment or a set of hex digits.  Comments start with a '*' or
 *     a '!'.  Lines starting with '!' are printed to stdout.
 *
 *     The hex scancodes can be in one of the following formats:
 *      xx - type 1 scancode
 *     1xx - make PM scancode
 *     2xx - break PM scancode
 *     3xx - repeat PM scancode
 *
 ********************************************************************/

/*
 * Common headers
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef  unsigned short int  uint16;
typedef  unsigned long  int  uint32;
#include <unikbd.h>

/*
 * These tables are within the prototype library.  They are not
 * exported in the real library but are used internal to the
 * Event Services.
 */
char       EventPMScan[128];
char       EventPMScanX[128];
uint16     PM2T1Scan[240];

/*
 * Global data.  In the real code, this is the session event state
 */
char       g_wase0;          /* Last scan was an e0     */
char       g_wasdead;        /* Last key was a deadkey  */
char       g_lastscan;       /* Previous PM scancode    */
SHIFTSTATE g_shift;          /* Current shift state     */
VDKEY      g_dkey;           /* Previous dead key       */
KHAND      g_keyboard;       /* Keyboard handle         */
char       g_line[1024];     /* Input line              */

/*
 * Keyboard event.  This is a partial structure of what is in
 * the OS/2 keyboard event packet.
 */
typedef struct {
    uint32  shift;           /* Current actual shift state       */
    UniChar unichar;         /* Unicode character                */
    VDKEY   vdkey;           /* Virtual or deadkey               */
    uint16  type1;           /* Type 1 scancode                  */
    char    biosscan;        /* BIOS (translated) scancode       */
    char    pmscan;          /* PM scancode                      */
    char    action;          /* Action  make=1, break=2, rept=3  */
    char    dead;            /* Dead key kind                    */
    char    valid;           /* Valid entry                      */
    char    resv;
} KEYB;

/*
 * Deadkey definitions
 */
enum DeadDef {
    DEAD_none   = 0,         /* Normal, non deadkey              */
    DEAD_initial,            /* Initial part of deadkey sequence */
    DEAD_invalid,            /* Invalid deadkey                  */
    DEAD_final,              /* End of deadkey sequence          */
    DEAD_chain               /* Chained deadkey sequence         */
};


/*
 * Internal Function Prototypes
 */
void  help     (void);
void  printkey (KEYB * key);
void  t1scan   (int inscan, KEYB * key);
void  scancode (int scan,   int makebreak, KEYB * key);


/*
 * main:  Main program
 */
main(int argc, char * * argv) {
    FILE *  in;
    int     x;
    int     rc;
    char  * name;
    KHAND   k;
    KEYB    key;
    int     scan;
    int     makebreak;
    char  * cp;

    if (argc<2 || *argv[1]=='?')
        help();
    printf("kbdtest - (C) Copyright IBM Corp. 1995\n");

    /*
     * Open keyboard file
     */
    if (argc<3)
        name = "us";
    else
        name = argv[2];
    if (*name == '*')
        name = "";
    rc = UniCreateKeyboard(&k, name, 0);
    if (!rc) {
        printf("Unable to open keyboard - %s\n", name);
        exit (2);
    }

    /*
     * Open the scancode file
     */
    in = fopen(argv[1], "r");
    if (!in) {
        printf("Unable to open scancode file - %s\n", argv[1]);
        UniDestroyKeyboard(k);
        exit (3);
    }

    /*
     * For the entire file
     */
    for(;;) {
        /*
         * Get an input line and strip trailing line ends
         */
        fgets(g_line, 1024, in);
        if (feof(in) || ferror(in))
            break;
        cp = g_line+strlen(g_line)-1;
        while (*cp=='\n' || *cp=='\r') {
            *cp = 0;
            cp--;
        }

        /*
         * Check for comment lines
         */
        if (*g_line == '*' || *g_line=='!') {
            if (*g_line=='!')
                printf("%s\n", g_line+1);
        }

        /*
         * Hex scancode lines
         */
        else {
            cp = g_line;
            while (*cp) {

                /*
                 * Parse the hex scancode
                 */
                x = strtoul(cp, &cp, 16);
                if (x==0) {
                    printf("Error in scanfile - %s\n", cp);
                    break;
                }
                scan = (BYTE) x;
                makebreak = x/256;
                if (makebreak>3) {
                    printf("Error in scanfile - %x\n", x);
                    break;
                }

                /*
                 * Convert the scancode
                 */
                if (makebreak == 0)
                    t1scan(scan, &key);
                else
                    scancode(scan, makebreak, &key);

                /*
                 * Print out the result
                 */
                if (key.valid) {
                    printkey(&key);
                }
            }
        }
    }

    /*
     * Cleanup and return
     */
    UniDestroyKeyboard(k);
    return 0;
}


/*
 * printkey:  Print out a key entry
 */
void printkey(KEYB * key) {
    printf("scan=%02x  t1=%04x  act=%d  uni=%04x  vdkey=%04x  shift=%08x  bios=%02x  dead=%d\n",
        key->pmscan, key->type1, key->action, key->unichar, key->vdkey,
        key->shift, key->biosscan, key->dead);
}


/*
 * help: Show minimal help
 */
void  help(void) {
    printf("kbdtest  scanfile  kblfile\n\n");
    printf("scanfile is a list of scancodes in hex:\n");
    printf(" xx - type 1 scancode\n");
    printf("1xx - make PM scancode\n");
    printf("2xx - break PM scancode\n");
    printf("3xx - repeat PM scancode\n");
    printf("*   - unprinted comments\n");
    printf("!   - printed comments\n");
    exit (1);
}


/*
 * t1scan:  Process type 1 scancode
 *          The type1 scancode is a single byte value
 */
void  t1scan(int  inscan, KEYB * key) {
    int  scan;
    int  makebreak;

    if (inscan==0xe0||inscan==0xe1) {  /* Next scancode is extended */
        memset(key, 0, sizeof(KEYB));  /* Set invalid               */
        g_wase0 = 1;
    } else {                           /* Normal type 1 scancode    */
        scan = inscan;
        if (scan&0x80) {               /* Break if high bit on      */
            makebreak = KEYEV_BREAK;
            scan &= 0x7f;
            g_lastscan = 0;            /* No previous key           */
        } else {
            makebreak = KEYEV_MAKE;
            if (scan==g_lastscan)      /* Same as last key          */
                makebreak = KEYEV_REPEAT;
            else
                g_lastscan = scan;     /* Remember this key         */
        }

        /*
         * Translate to PM scancode
         */
        if (g_wase0) {
            g_wase0 = 0;
            scan = EventPMScanX[scan]; /* Extended scancode map     */
        } else
            scan = EventPMScan[scan];  /* Normal scancode map       */
        scancode(scan, makebreak, key);
    }
}


/*
 * scancode:  Process a PM scancode
 *            A PM scancode is given as the scancode and makebreak
 */
void  scancode(int scan, int makebreak, KEYB * key) {
    UniChar  uni, composite, newdead;
    uint16   vdkey;

    /*
     * Make the event
     */
    memset(key, 0, sizeof(KEYB));
    key->pmscan = scan;                /* PM scancode                */
    key->type1  = PM2T1Scan[scan];
    if (makebreak==KEYEV_BREAK)        /* Set up the type 1 scancode */
        key->type1 |= 0x80;
    key->valid  = 1;                   /* This is a valid entry      */
    key->action = makebreak;           /* Record action              */

    /*
     * Update shift states
     */
    UniUpdateShiftState(g_keyboard, &g_shift, (VSCAN)scan, (BYTE)makebreak);
    key->shift  = g_shift.Shift;       /* Real shift state           */

    /*
     * Do keyboard and deadkey translation
     */
    UniTranslateKey(g_keyboard, g_shift.Effective, (VSCAN)scan,
                    &uni, &vdkey, &(key->biosscan));
    if (g_wasdead) {
        if (uni &&
            !UniTranslateDeadkey(g_keyboard, g_dkey, uni,
                                 &composite, &newdead)) {
            uni = composite;
            if (newdead) {
                g_dkey    = newdead;   /* Save deadkey */
                key->dead = DEAD_chain;/* Chained deadkey */
            } else {
                g_wasdead = 0;         /* Valid end of deadkey sequence */
                key->dead = DEAD_final;
            }
        } else {
            g_wasdead = 0;
            key->dead = DEAD_invalid;  /* Invalid deadkey sequence */
        }
    }
    key->unichar = uni;

    /*
     * Process deadkeys
     */
    if (vdkey) {                       /* Virtual or dead key */
        if (vdkey<DK_MIN) {            /* Valid virtual key   */
            key->vdkey = vdkey;
        } else {
            g_wasdead = 1;
            g_dkey    = vdkey;         /* Save deadkey        */
            key->dead = DEAD_initial;  /* Partial deadkey     */
        }
    }
}
