;/* rawkey.c - Execute me to compile me with SAS C 5.10
LC -b1 -cfistq -v -y -j73 rawkey.c
Blink FROM LIB:c.o,rawkey.o TO rawkey LIBRARY LIB:LC.lib,LIB:Amiga.lib
quit
*/

/*
Copyright (c) 1992 Commodore-Amiga, Inc.

This example is provided in electronic form by Commodore-Amiga, Inc. for
use with the "Amiga ROM Kernel Reference Manual: Libraries", 3rd Edition,
published by Addison-Wesley (ISBN 0-201-56774-1).

The "Amiga ROM Kernel Reference Manual: Libraries" contains additional
information on the correct usage of the techniques and operating system
functions presented in these examples.  The source and executable code
of these examples may only be distributed in free electronic form, via
bulletin board or as part of a fully non-commercial and freely
redistributable diskette.  Both the source and executable code (including
comments) must be included, without modification, in any copy.  This
example may not be published in printed form or distributed with any
commercial product.  However, the programming techniques and support
routines set forth in these examples may be used in the development
of original executable software products for Commodore Amiga computers.

All other rights reserved.

This example is provided "as-is" and is subject to change; no
warranties are made.  All use is at your own risk. No liability or
responsibility is assumed.
*/

/*
** rawkey.c - How to correctly convert from RAWKEY to keymapped ASCII
*/
#define INTUI_V36_NAMES_ONLY

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <devices/inputevent.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/console_protos.h>
#include <stdio.h>

#ifdef LATTICE
int CXBRK(void)    { return(0); }  /* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); }  /* really */
#endif

/* our function prototypes */
LONG deadKeyConvert(struct IntuiMessage *msg, UBYTE *kbuffer,
                    LONG kbsize, struct KeyMap *kmap, struct InputEvent *ievent);
VOID print_qualifiers(ULONG qual);
BOOL doKeys(struct IntuiMessage *msg, struct InputEvent *ievent,
                    UBYTE **buffer, ULONG *bufsize);
VOID process_window(struct Window *win, struct InputEvent *ievent,
                    UBYTE **buffer, ULONG *bufsize);

/* A buffer is created for RawKeyConvert() to put its output. BUFSIZE is the size of
** the buffer in bytes.  NOTE that this program starts out with a buffer size of 2.
** This is only to show how the buffer is automatically increased in size  by this
** example!  In an application, start with a much larger buffer and you will probably
** never have to increase its size. 128 bytes or so should do the trick, but always
** be able to change the size if required.
*/
#define BUFSIZE (2)

struct Library *IntuitionBase, *ConsoleDevice;

/* main() - set-up everything used by program. */
VOID main(int argc, char **argv)
{
struct Window *win;
struct IOStdReq ioreq;
struct InputEvent *ievent;
UBYTE *buffer;
ULONG bufsize = BUFSIZE;

if(IntuitionBase = OpenLibrary("intuition.library",37)) {
    /* Open the console device just to do keymapping. (unit -1 means any unit) */
    if (0 == OpenDevice("console.device",-1,(struct IORequest *)&ioreq,0)) {
        ConsoleDevice = (struct Library *)ioreq.io_Device;

        /* Allocate the initial character buffer used by deadKeyConvert() and RawKeyConvert()
        ** for returning translated characters. If the characters generated by these routines
        ** cannot fit into the buffer, the application must pass a larger buffer.  This is
        ** done in this code by freeing the old buffer and allocating a new one.
        */
        if (buffer = AllocMem(bufsize,MEMF_CLEAR)) {
            if (ievent = AllocMem(sizeof(struct InputEvent),MEMF_CLEAR)) {
                if (win = OpenWindowTags(NULL,
                        WA_Width, 300,
                        WA_Height, 50,
                        WA_Flags, WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_ACTIVATE,
                        WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_RAWKEY,
                        WA_Title, "Raw Key Example",
                        TAG_END)) {
                    printf("Press keyboard keys to see ASCII conversion from rawkey\n");
                    printf("Unprintable characters will be shown as %c\n\n",0x7f);
                    process_window(win,ievent,&buffer,&bufsize);
                    CloseWindow(win);
                    }
                FreeMem(ievent,sizeof(struct InputEvent));
                }
            /* Buffer can be freed elsewhere in the program so test first. */
            if (buffer != NULL)
                FreeMem(buffer,bufsize);
            }
        CloseDevice((struct IORequest *)&ioreq);
        }
    CloseLibrary(IntuitionBase);
    }
}

/* Convert RAWKEYs into VANILLAKEYs, also shows special keys like HELP, Cursor Keys,
** FKeys, etc.  It returns:
**   -2 if not a RAWKEY event.
**   -1 if not enough room in the buffer, try again with a bigger buffer.
**   otherwise, returns the number of characters placed in the buffer.
*/
LONG deadKeyConvert(struct IntuiMessage *msg, UBYTE *kbuffer,
    LONG kbsize, struct KeyMap *kmap, struct InputEvent *ievent)
{
if (msg->Class != IDCMP_RAWKEY) return(-2);
ievent->ie_Class = IECLASS_RAWKEY;
ievent->ie_Code = msg->Code;
ievent->ie_Qualifier = msg->Qualifier;
ievent->ie_position.ie_addr = *((APTR*)msg->IAddress);

return(RawKeyConvert(ievent,kbuffer,kbsize,kmap));
}

/* print_qualifiers() - print out the values found in the qualifier bits of
** the message. This will print out all of the qualifier bits set.
*/
VOID print_qualifiers(ULONG qual)
{
printf("Qual:");
if (qual & IEQUALIFIER_LSHIFT)         printf("LShft,");
if (qual & IEQUALIFIER_RSHIFT)         printf("RShft,");
if (qual & IEQUALIFIER_CAPSLOCK)       printf("CapLok,");
if (qual & IEQUALIFIER_CONTROL)        printf("Ctrl,");
if (qual & IEQUALIFIER_LALT)           printf("LAlt,");
if (qual & IEQUALIFIER_RALT)           printf("RAlt,");
if (qual & IEQUALIFIER_LCOMMAND)       printf("LCmd,");
if (qual & IEQUALIFIER_RCOMMAND)       printf("RCmd,");
if (qual & IEQUALIFIER_NUMERICPAD)     printf("NumPad,");
if (qual & IEQUALIFIER_REPEAT)         printf("Rpt,");
if (qual & IEQUALIFIER_INTERRUPT)      printf("Intrpt,");
if (qual & IEQUALIFIER_MULTIBROADCAST) printf("Multi Broadcast,");
if (qual & IEQUALIFIER_MIDBUTTON)      printf("MidBtn,");
if (qual & IEQUALIFIER_RBUTTON)        printf("RBtn,");
if (qual & IEQUALIFIER_LEFTBUTTON)     printf("LBtn,");
if (qual & IEQUALIFIER_RELATIVEMOUSE)  printf("RelMouse,");
}

/* doKeys() - Show what keys were pressed. */
BOOL doKeys(struct IntuiMessage *msg, struct InputEvent *ievent,
            UBYTE **buffer, ULONG *bufsize)
{
USHORT char_pos;
USHORT numchars;
BOOL   ret_code = TRUE;
UBYTE  realc, c;

/* deadKeyConvert() returns -1 if there was not enough space in the buffer to
** convert the string. Here, the routine increases the size of the buffer on the
** fly...Set the return code to FALSE on failure.
*/
numchars = deadKeyConvert(msg, *buffer, *bufsize - 1, NULL, ievent);
while ((numchars == -1) && (*buffer != NULL)) {
    /* conversion failed, buffer too small. try to double the size of the buffer. */
    FreeMem(*buffer, *bufsize);
    *bufsize = *bufsize << 1;
    printf("Increasing buffer size to %d\n", *bufsize);

    if (NULL == (*buffer = AllocMem(*bufsize, MEMF_CLEAR)))  ret_code = FALSE;
    else  numchars = deadKeyConvert(msg, *buffer, *bufsize - 1, NULL, ievent);
    }

/* numchars contains the number of characters placed within the buffer.  Key up events and   */
/* key sequences that do not generate any data for the program (like deadkeys) will return   */
/* zero.  Special keys (like HELP, the cursor keys, FKeys, etc.) return multiple characters  */
/* that have to then be parsed by the application.  Allocation failed above if buffer is NULL*/
if (*buffer != NULL) {
    /* if high bit set, then this is a key up otherwise this is a key down */
    if (msg->Code & 0x80)
          printf("Key Up:   ");
    else
          printf("Key Down: ");

    print_qualifiers(msg->Qualifier);
    printf(" rawkey #%d maps to %d ASCII character(s)\n", 0x7F & msg->Code, numchars);
    for (char_pos = 0; char_pos < numchars; char_pos++) {
        realc = c = (*buffer)[char_pos];
        if ((c <= 0x1F)||((c >= 0x80)&&(c < 0xa0)))
            c = 0x7f;
        printf("  %3d ($%02x) = %c\n", realc, realc, c);
        }
    }
return(ret_code);
}

/* process_window() - simple event loop.  Note that the message is not replied
** to until the end of the loop so that it may be used in the doKeys() call.
*/
VOID process_window(struct Window *win, struct InputEvent *ievent,
    UBYTE **buffer, ULONG *bufsize)
{
struct IntuiMessage *msg;
BOOL done;

done = FALSE;
while (done == FALSE) {
    Wait((1L<<win->UserPort->mp_SigBit));
    while ((done == FALSE) && (msg = (struct IntuiMessage *)GetMsg(win->UserPort))) {
        switch (msg->Class) {     /* handle our events */
            case IDCMP_CLOSEWINDOW:
                done = TRUE;
                break;
            case IDCMP_RAWKEY:
                if (FALSE == doKeys(msg,ievent,buffer,bufsize))
                    done = TRUE;
                break;
            }
        ReplyMsg((struct Message *)msg);
        }
    }
}
