/* ShowConsole.c

      A program to help you gain experience with the console device.

   Manx Aztec C 5.2a source

    cc ShowConsole.c
    ln ShowConsole.o s.lib c.lib

*/

#include <stdio.h>
#include <string.h>
#include <exec/types.h>
#include <exec/io.h>
#include <clib/console_protos.h>
#include <intuition/intuition.h>
#include "ShowConsoleGadgets.c"

#define MEMF_CLEAR (1<<16)

UBYTE ERASEEOL[]    = { 0x9b, 'K' };
UBYTE ERASEEOD[]    = { 0x9b, 'J' };
UBYTE HOME[]        = { 0x9b, '1', ';', '1', 'H' };
UBYTE MOVE[]        = { 0x9b, '0', '0', ';', '0', '0', 'H' };
UBYTE MOVE2[]       = { 0x9b, '2', '1', ';', '1', '6', 'H' };
UBYTE ONEDOWN[]     = { 0x9b, '2', ';', '1', 'H' };
UBYTE RAWKEYINPUT[] = { 0x9b, '1', ';', '2', '{' };
UBYTE COOKEDKEYS[]  = { 0x9b, '1', ';', '2', '}' };
UBYTE CURSORON[]    = { 0x9b, 0x20, 0x70 };
UBYTE CURSOROFF[]   = { 0x9b, 0x30, 0x20, 0x70 };

char *mask[] = { "struct IOStdReq {\n",
                 "   struct Node *(........) (ln_Succ)\n",
                 "   struct Node *(........) (ln_Pred)\n",
                 "   UBYTE ...  (ln_Type)\n",
                 "   BYTE  ...  (ln_Pri)\n",
                 "   char *(........) (ln_Name)\n",
                 "   struct MsgPort *(........) (mn_ReplyPort)\n",
                 "   UWORD .....  (mn_Length)\n",
                 "   struct Device *(........) (io_Device)\n",
                 "   struct Unit *(........) (io_Unit)\n",
                 "   UWORD ..... (io_Command)\n",
                 "   UBYTE ...  (io_Flags)\n",
                 "   BYTE  ...  (io_Error)\n",
                 "   ULONG ........ (io_Actual)\n",
                 "   ULONG ........ (io_Length)\n",
                 "   APTR  ........ (io_Data)\n",
                 "   ULONG ........ (io_Offset)\n",
                 "};            ",
                 "<CSI>class;subclass;keycode;qualifiers;....|\n"
               };

char *quals[] = { "Left Shift",
                  "Right Shift",
                  "Ctrl",
                  "Left Alt",
                  "Right Alt",
                  "Left Amiga",
                  "Right Amiga"
                 };

    /* Defines */
#define IB                IntuitionBase
#define BufferLen         121
#define ERASE_EOL()       ConsoleWrite(ERASEEOL,2)
#define ERASE_EOD()       ConsoleWrite(ERASEEOD,2)
#define HOME_CURSOR()     ConsoleWrite(HOME,5)
#define MOVE_CURSOR()     ConsoleWrite(MOVE,7)
#define ONEDOWN_CURSOR()  ConsoleWrite(ONEDOWN,5)
#define DO_RETURNEDDATA() ConsoleWrite(MOVE2,7)
#define GET_RAWKEYINPUT() ConsoleWrite(RAWKEYINPUT,5)
#define GET_COOKEDKEYS()  ConsoleWrite(COOKEDKEYS,5)
#define SET_CURSORON()    ConsoleWrite(CURSORON,3)
#define SET_CURSOROFF()   ConsoleWrite(CURSOROFF,4)

    /* Prototypes */
extern struct MsgPort *CreatePort(char *, long);
extern struct IOStdReq *CreateStdIO(struct MsgPort *);
extern struct Window *OpenWindow(struct NewWindow *);
extern void *OpenLibrary(char *, long);
extern long OpenDevice(char *, long, struct IORequest *, long);
void shutdown(void);
void ConsoleWrite(UBYTE *, int);
void ConsoleRead(UBYTE *, int);
void PrintCharacter(UBYTE *);
void ClearCharacter(void);
void PrintQualifiers(UBYTE *);
void ClearQualifiers(void);
void PrintConsoleInfo(struct IOStdReq *);
void ClearScrn(void);
struct Window *DoWindow(void);
UWORD convert_code(UBYTE *string);
UWORD convert_qualifiers(UBYTE *string);

void ubtoa(UBYTE, char *);
void uwtoa(UWORD, char *);
void ultoh(ULONG, char *);

    /* Pragmas */
#pragma regcall(ubtoa(d0,a0))
#pragma regcall(uwtoa(d0,a0))
#pragma regcall(ultoh(d0,a0))

    /* Globals */
struct Device *ConsoleDevice;
struct IntuitionBase *IntuitionBase;
struct MsgPort *myPort;
struct IOStdReq *myMessage;
struct Window *Window;
UBYTE buffer[BufferLen+1];
BOOL raw_keys = TRUE;

void main(void)
{
   UBYTE key;
   LONG gadget_class;
   int x;
   long error, windowsignal, consolesignal, returnedsignals;
   struct Gadget *gadget_selected;
   struct IntuiMessage *my_message;
   struct IOStdReq *my_ioreq;
   BOOL done = FALSE;

   IntuitionBase = (struct IB *)OpenLibrary("intuition.library", 0L);

   if ( !(Window = DoWindow()) )
   {
      puts("Not enough memory to open a window.");
      goto main_exit;
   }

   if ( !(myPort = CreatePort("myConPort", 0L)) )
   {
      puts("Not enough memory for a message port.");
      goto main_exit;
   }

   if ( !(myMessage = CreateStdIO(myPort)) )
   {
      puts("Not enough memory for a message structure.");
      goto main_exit;
   }

   myMessage->io_Data = (APTR) Window;
   myMessage->io_Length = sizeof(struct Window);
   error=OpenDevice("console.device", 0L, (struct IORequest *)myMessage, 0L);
   ConsoleDevice = myMessage->io_Device;

   if ( error )
   {
      puts("Problem encountered opening console device.");
      goto main_exit;
   }

   windowsignal = 1 << Window->UserPort->mp_SigBit;
   consolesignal = 1 << myPort->mp_SigBit;

   GET_RAWKEYINPUT(); /* The default is to request raw key events. */
   ClearScrn();
   RefreshGList(&GadgetList1, Window, 0L, -1);

   ConsoleWrite((UBYTE *)"ShowConsole by David A. Blackwell", -1);
   PrintConsoleInfo(myMessage);

   ConsoleRead(buffer, BufferLen-1);

   SET_CURSOROFF();

   while ( !done )
   {
      returnedsignals = Wait(windowsignal|consolesignal);
      if ( returnedsignals & windowsignal )
      {
         while (my_message = (struct IntuiMessage *)GetMsg(Window->UserPort))
         {
            gadget_class = my_message->Class;
            gadget_selected = (struct Gadget *)my_message->IAddress;
            ReplyMsg((struct Message *)my_message);
            switch(gadget_class)
            {
               case GADGETUP:
                  switch (gadget_selected->GadgetID)
                  {
                     case RAW_KEYS:
                        if ( !raw_keys )
                        {
                           GET_RAWKEYINPUT();
                           raw_keys = TRUE;
                        }
                        break;
                     case QUIT:
                        done = TRUE;
                        break;
                     case COOKED_KEYS:
                        if ( raw_keys )
                        {
                           GET_COOKEDKEYS();
                           raw_keys = FALSE;
                        }
                        break;
                     default:
                        puts("Unexpected gadget selection.");
                  }
                  break;
               default:
                  puts("Unexpected Intuition event.");
            }
         }
      }
      if ( returnedsignals & consolesignal )
      {
         while ( (my_ioreq = (struct IOStdReq *)GetMsg(myPort)) )
         {
            if ( my_ioreq->io_Command == CMD_READ )
            {
               PrintConsoleInfo(my_ioreq);
               ConsoleRead(buffer, BufferLen-1);
            }
            DeleteStdIO(my_ioreq);
         }
      }
   }

   SET_CURSORON();

   CloseDevice(myMessage);

main_exit:
   shutdown();
}

void shutdown(void)
{
   struct IOStdReq *my_ioreq;

   if ( myMessage )
      DeleteStdIO(myMessage);

   if ( myPort )
   {
      while ( (my_ioreq = (struct IOStdReq *)GetMsg(myPort)) )
      {
         DeleteStdIO(my_ioreq);
      }
      DeletePort(myPort);
   }

   if ( Window )
      CloseWindow(Window);

   if ( IntuitionBase )
      CloseLibrary(IntuitionBase);

}

/* DoWindow - open a window and return a pointer to the window structure. */

struct Window *DoWindow(void)
{
   struct NewWindow *nw;
   struct Window *window = (struct Window *) 0;

   if ( !(nw = (struct NewWindow *)AllocMem(sizeof(struct NewWindow), MEMF_CLEAR)) )
      goto dw_exit;

   nw->LeftEdge = 0;
   nw->TopEdge = 0;
   nw->Width = 640;
   nw->Height = 200;
   nw->DetailPen = 0;
   nw->BlockPen = 1;
   nw->Title = (UBYTE *) "ShowConsole Window";
   nw->Flags = ACTIVATE|WINDOWDEPTH|SMART_REFRESH;
   nw->IDCMPFlags = GADGETUP;
   nw->Type = WBENCHSCREEN;
   nw->FirstGadget = &GadgetList1;
   nw->CheckMark = 0;
   nw->Screen = 0;
   nw->BitMap = 0;
   nw->MinWidth = 0;
   nw->MinHeight = 0;
   nw->MaxWidth = 640;
   nw->MaxHeight = 200;

   window = OpenWindow(nw);

dw_exit:

   return(window);
}

/* ConsoleWrite - Write a string to the console device. */

void ConsoleWrite(UBYTE *string, int length)
{
   struct IOStdReq *msg;

   if ( !(msg = CreateStdIO(myPort)) )
   {
      puts("Unable to write to the console.");
      goto cw_exit;
   }

   msg->io_Device = myMessage->io_Device;
   msg->io_Unit = myMessage->io_Unit;
   msg->io_Command = CMD_WRITE;
   msg->io_Data = (APTR)string;
   msg->io_Length = length;

   SendIO(msg);

cw_exit:
   return;
}

/* ConsoleRead - Read a given number of characters from the console device. */

void ConsoleRead(UBYTE *string, int length)
{
   struct IOStdReq *msg;

   if ( !(msg = CreateStdIO(myPort)) )
   {
      puts("Unable to read from the console.");
      goto cr_exit;
   }

   msg->io_Device = myMessage->io_Device;
   msg->io_Unit = myMessage->io_Unit;
   msg->io_Command = CMD_READ;
   msg->io_Data = (APTR)string;
   msg->io_Length = length;

   SendIO(msg);

cr_exit:
   return;
}

/* ClearScrn - Home the cursor and clear the screen. */

void ClearScrn(void)
{
   HOME_CURSOR();
   ERASE_EOD();
}

void PrintConsoleInfo(struct IOStdReq *msg)
{
   int x;
   static UBYTE tempb[4] = { "ESC " };

   ultoh((ULONG)msg->io_Message.mn_Node.ln_Succ, mask[1]+17);
   ultoh((ULONG)msg->io_Message.mn_Node.ln_Pred, mask[2]+17);
   ubtoa((UBYTE)msg->io_Message.mn_Node.ln_Type, mask[3]+9);
   ubtoa((UBYTE)msg->io_Message.mn_Node.ln_Pri, mask[4]+9);
   ultoh((ULONG)msg->io_Message.mn_Node.ln_Name, mask[5]+10);
   ultoh((ULONG)msg->io_Message.mn_ReplyPort, mask[6]+20);
   uwtoa((UWORD)msg->io_Message.mn_Length, mask[7]+9);
   ultoh((ULONG)msg->io_Device, mask[8]+19);
   ultoh((ULONG)msg->io_Unit, mask[9]+17);
   uwtoa((UWORD)msg->io_Command, mask[10]+9);
   ubtoa((UBYTE)msg->io_Flags, mask[11]+9);
   ubtoa((UBYTE)msg->io_Error, mask[12]+9);
   ultoh((ULONG)msg->io_Actual, mask[13]+9);
   ultoh((ULONG)msg->io_Length, mask[14]+9);
   ultoh((ULONG)msg->io_Data, mask[15]+9);
   ultoh((ULONG)msg->io_Offset, mask[16]+9);

   ONEDOWN_CURSOR();

   for ( x = 0 ; x < 19 ; x++ )
      ConsoleWrite((UBYTE *)mask[x], -1);

   ConsoleWrite((UBYTE *)"Returned Data: ", -1);
   MOVE[1] = '0';
   MOVE[2] = '2';
   MOVE[4] = '5';
   MOVE[5] = '5';
   MOVE_CURSOR();
   ConsoleWrite((UBYTE *)"Char: ", -1);
   MOVE[1] = '0';
   MOVE[2] = '4';
   MOVE[4] = '5';
   MOVE[5] = '5';
   MOVE_CURSOR();
   ConsoleWrite((UBYTE *)"Qualifiers: ", -1);
   DO_RETURNEDDATA();
   ERASE_EOL();
   if ( raw_keys )
   {
      if ( buffer[0] )
      {
         ConsoleWrite(tempb, 4);
         ConsoleWrite(&buffer[1], -1);
         PrintCharacter(buffer);
         PrintQualifiers(buffer);
      }
   }else{
      ConsoleWrite(&buffer[0], -1);
      ClearCharacter();
      ClearQualifiers();
   }

   for( x = 0 ; buffer[x] != 0 ; x++ )
      buffer[x] = 0;
   if ( MOVE2[2] == '0' )
   {
      MOVE2[2] = '1';
   }else{
      MOVE2[2] = '0';
   }

}

void PrintCharacter(UBYTE *characterbuffer)
{
   char convertbuffer[4];
   long numberchars;
   static struct InputEvent ievent = {NULL, IECLASS_RAWKEY, 0, 0, 0};

   ievent.ie_Code = convert_code(characterbuffer);
   ievent.ie_Qualifier = convert_qualifiers(characterbuffer);
   ievent.ie_position.ie_addr = NULL;

   numberchars = RawKeyConvert(&ievent, (STRPTR) convertbuffer, 4L, 0L);

   if (!numberchars)
      goto pc_exit;

   MOVE[1] = '0';
   MOVE[2] = '2';
   MOVE[4] = '6';
   MOVE[5] = '1';
   MOVE_CURSOR();
   ConsoleWrite((UBYTE *)convertbuffer, 1);

pc_exit:
   return;
}

void ClearCharacter(void)
{
   MOVE[1] = '0';
   MOVE[2] = '2';
   MOVE[4] = '6';
   MOVE[5] = '1';
   MOVE_CURSOR();
   ConsoleWrite((UBYTE *)" ", 1);
}

void PrintQualifiers(UBYTE *characterbuffer)
{
   UWORD char_qualifiers;

   char_qualifiers = convert_qualifiers(characterbuffer);

   if (char_qualifiers & IEQUALIFIER_LSHIFT)
   {
      MOVE[1] = '0';
      MOVE[2] = '5';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ConsoleWrite((UBYTE *)quals[0], -1);
   }else{
      MOVE[1] = '0';
      MOVE[2] = '5';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ERASE_EOL();
   }

   if (char_qualifiers & IEQUALIFIER_RSHIFT)
   {
      MOVE[1] = '0';
      MOVE[2] = '6';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ConsoleWrite((UBYTE *)quals[1], -1);
   }else{
      MOVE[1] = '0';
      MOVE[2] = '6';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ERASE_EOL();
   }

   if (char_qualifiers & IEQUALIFIER_CONTROL)
   {
      MOVE[1] = '0';
      MOVE[2] = '7';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ConsoleWrite((UBYTE *)quals[2], -1);
   }else{
      MOVE[1] = '0';
      MOVE[2] = '7';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ERASE_EOL();
   }

   if (char_qualifiers & IEQUALIFIER_LALT)
   {
      MOVE[1] = '0';
      MOVE[2] = '8';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ConsoleWrite((UBYTE *)quals[3], -1);
   }else{
      MOVE[1] = '0';
      MOVE[2] = '8';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ERASE_EOL();
   }

   if (char_qualifiers & IEQUALIFIER_RALT)
   {
      MOVE[1] = '0';
      MOVE[2] = '9';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ConsoleWrite((UBYTE *)quals[4], -1);
   }else{
      MOVE[1] = '0';
      MOVE[2] = '9';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ERASE_EOL();
   }

   if (char_qualifiers & IEQUALIFIER_LCOMMAND)
   {
      MOVE[1] = '1';
      MOVE[2] = '0';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ConsoleWrite((UBYTE *)quals[5], -1);
   }else{
      MOVE[1] = '1';
      MOVE[2] = '0';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ERASE_EOL();
   }

   if (char_qualifiers & IEQUALIFIER_RCOMMAND)
   {
      MOVE[1] = '1';
      MOVE[2] = '1';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ConsoleWrite((UBYTE *)quals[6], -1);
   }else{
      MOVE[1] = '1';
      MOVE[2] = '1';
      MOVE[4] = '5';
      MOVE[5] = '5';
      MOVE_CURSOR();
      ERASE_EOL();
   }

}

void ClearQualifiers(void)
{

   MOVE[1] = '0';
   MOVE[2] = '5';
   MOVE[4] = '5';
   MOVE[5] = '5';
   MOVE_CURSOR();
   ERASE_EOL();

   MOVE[1] = '0';
   MOVE[2] = '6';
   MOVE[4] = '5';
   MOVE[5] = '5';
   MOVE_CURSOR();
   ERASE_EOL();

   MOVE[1] = '0';
   MOVE[2] = '7';
   MOVE[4] = '5';
   MOVE[5] = '5';
   MOVE_CURSOR();
   ERASE_EOL();

   MOVE[1] = '0';
   MOVE[2] = '8';
   MOVE[4] = '5';
   MOVE[5] = '5';
   MOVE_CURSOR();
   ERASE_EOL();

   MOVE[1] = '0';
   MOVE[2] = '9';
   MOVE[4] = '5';
   MOVE[5] = '5';
   MOVE_CURSOR();
   ERASE_EOL();

   MOVE[1] = '1';
   MOVE[2] = '0';
   MOVE[4] = '5';
   MOVE[5] = '5';
   MOVE_CURSOR();
   ERASE_EOL();

   MOVE[1] = '1';
   MOVE[2] = '1';
   MOVE[4] = '5';
   MOVE[5] = '5';
   MOVE_CURSOR();
   ERASE_EOL();

}

UWORD convert_code(UBYTE *string)
{
   int x;
   char semicolon = ';';
   UBYTE temp;
   UBYTE *begining, *ending;
   UWORD rtnval = 0;

   for ( x = 0, begining = string ; x < 2 ; x++ )
   {
      begining++;
      begining = (UBYTE *)strchr((char *)begining, (int)semicolon);
      if ( !begining )
         goto cc_exit;
   }

   begining++;
   ending = (UBYTE *)strchr((char *)begining, (int)semicolon);

   if ( !ending )
      goto cc_exit;

   temp = *ending;
   *ending = (UBYTE) 0;
   rtnval = (UWORD) atol((char *)begining);
   *ending = temp;

cc_exit:
   return(rtnval);
}

UWORD convert_qualifiers(UBYTE *string)
{
   int x;
   char semicolon = ';';
   UBYTE *begining, *ending;
   UBYTE temp;
   UWORD rtnval = 0;

   for ( x = 0, begining = string ; x < 3 ; x++ )
   {
      begining++;
      begining = (UBYTE *)strchr((char *)begining, (int)semicolon);
      if ( !begining )
         goto cq_exit;
   }

   begining++;
   ending = (UBYTE *)strchr((char *)begining, (int)semicolon);

   if ( !ending )
      goto cq_exit;

   temp = *ending;
   *ending = (UBYTE) 0;
   rtnval = (UWORD) atol((char *)begining);
   *ending = temp;

cq_exit:
   return(rtnval);
}


/* ubtoa - Convert an unsigned byte to an ascii string (decimal). */

#asm

   public _ubtoa

_ubtoa:
   movem.l  d0-d1/a0,-(sp)
   and.l     #$ff,d0
   moveq    #2,d1
1$ move.b   #$20,(a0)+
   dbra     d1,1$
   moveq    #2,d1
2$ divu     #10,d0
   swap     d0
   or.b     #$30,d0
   move.b   d0,-(a0)
   swap     d0
   ext.l    d0
   dbra     d1,2$
   movem.l  (sp)+,d0-d1/a0
   rts

#endasm

/* uwtoa - Convert an unsigned word to an ascii string (decimal). */

#asm

   public _uwtoa

_uwtoa:
   movem.l  d0-d1/a0,-(sp)
   and.l    #$ffff,d0
   moveq    #4,d1
3$ move.b   #$20,(a0)+
   dbra     d1,3$
   moveq    #4,d1
4$ divu     #10,d0
   swap     d0
   or.b     #$30,d0
   move.b   d0,-(a0)
   swap     d0
   ext.l    d0
   dbra     d1,4$
   movem.l  (sp)+,d0-d1/a0
   rts

#endasm

/* ultoh - Convert an unsigned long to its ascii string (hex). */

#asm

   public _ultoh

_ultoh:
   movem.l  d0-d2/a0,-(sp)
   moveq    #7,d2
5$ move.b   #$20,(a0)+
   dbra     d2,5$
   moveq    #7,d2
   moveq    #0,d1
6$ move.b   d0,d1
   lsr.l    #4,d0
   and.b    #$f,d1
   or.b     #$30,d1
   cmp.b    #$39,d1
   ble.s    7$
   addq.b   #7,d1
7$ move.b   d1,-(a0)
   dbra     d2,6$
   movem.l  (sp)+,d0-d2/a0
   rts

#endasm
