/* file : console.c
 *
 *	(C) Copyright 1986 Daryl Hegyi.
 *	This program may be copied and used for non-commercial applications.	
 *
 *	This module handles input and output via the console device.
 *	It responds to keyboard input and window (mouse) events.
 *	Window events are returned as keycodes (defined in console.h)
 *	
 *	functions defined:
 *		Init_Console()	  - open console window and attach console dev.
 *		getchar()	  - get character from keyboard
 *		constat()	  - check to see if char is available
 *		putchar()	  - write character to console
 *		putstr()	  - write (null terminated) string
 *		putnstr		  - write (n) chars from string
 *		Close_Console()	  - remove everything.
 *		
 */
 
#define	 CONSOLE_VERSION "1.0E 4-Nov-86"

/* do we really need all this??? */

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/exec.h>
#include <exec/execbase.h>
#include <exec/ports.h>
#include <exec/devices.h>
#include <exec/memory.h>
#include <devices/timer.h>
#include <hardware/blit.h>
#include <graphics/copper.h>
#include <graphics/regions.h>
#include <graphics/rastport.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/gels.h>
#include <intuition/intuition.h>

#include "console.h"			/* some configuration defines */

    #define	ERROR (-1)
    #define	NIL	0L


/*
 *   Some variables that you may want to inquire about after getchar()
 *   returns a window event.
 */
    ULONG cmClass;
    USHORT  cmCode, cmQualifier;
    long cmMouseX, cmMouseY;

    char *Console_Err_Msg;	/* set if Init_Console() returns NIL */
				/* can be interrogated */

/* you may want to extract these library definitions into your main program */

    extern struct ExecBase *SysBase;
    struct IntuitionBase *IntuitionBase;
    struct GfxBase *GfxBase;

/* here are the global data structures needed */

    static struct Window *ConsoleWindow;	/* what we look out of */

/* these connect to the console device */

    static struct MsgPort *WritePort; 
    static struct MsgPort *ReadPort;

/* these are the messages we use for communication */

    static struct IOStdReq *ConsoleWrite;
    static struct IOStdReq *ConsoleRead;

    static unsigned char ConsoleBuffer[10];	/* incoming keyboard input */
    static long ConsoleWaitMask;		/* events to wait upon */

/* change this to configure your window */

    static struct NewWindow NewWindow = 
    {
	0, 0, 640, 120,           /* sizes */
 	-1, -1,                    /* pens */

   	CLOSEWINDOW 		/* IDCMP flags */
	| REFRESHWINDOW 
	| MOUSEBUTTONS
	| MENUPICK,

   	WINDOWDRAG 		/* flags */
	| WINDOWDEPTH 
	| WINDOWCLOSE 
	| WINDOWSIZING
	| SIMPLE_REFRESH 
	| REPORTMOUSE 
	| ACTIVATE,

   	NULL, NULL,                /* gadget, checkmark */
   	(UBYTE *) "Console",       /* title 		*/
   	NULL, NULL,                /* screen, bitmap 	 */
   	200, 120, 640, 200,         /* min and max sizing   */
   	WBENCHSCREEN               /* on the workbench screen */
    };

struct Window *Init_Console(header)
	UBYTE *header;
/*
 *  Call intution to create a new window for us on the
 *  screen and open the console device to that window
 */
{
	register struct IOStdReq *cw;
	register struct IOStdReq *cr;

	struct IntuiMessage *GetMsg();
	void		*OpenLibrary();
	struct Window	*OpenWindow();
 	struct MsgPort	*CreatePort();
	struct IOStdReq	*CreateStdIO();

	if (  ( IntuitionBase = (struct IntuitionBase *)
                   OpenLibrary("intuition.library", 0L) )
   	   == NULL  )
	{
	    conerror("Can't open intution library");
	    return NULL; 
	}

	if ( (GfxBase = (struct GfxBase *)
		   OpenLibrary("graphics.library", 0L))
	      == NULL) 
	{
	    conerror("Can't open GfxBase");
  	    return NULL; 
	}
 
	NewWindow.Title  = header;
	if ( (ConsoleWindow = OpenWindow(&NewWindow))  == NULL )
	{
	    conerror("Can't open Console window");
            return NULL;
	}
 
    /* create and open console read and write port  */

	if ( (WritePort = CreatePort("con.write", 0L)) ==  0 )
	{
	    conerror("Can't Open Console write port");
	    return NULL;
	}
	if ( (ReadPort = CreatePort("con.read", 0L)) ==  0 )
	{
	    conerror("Can't Open Console read port");
	    return NULL;
	}

    /* create console write message */

	if ( (cw = ConsoleWrite = CreateStdIO(WritePort)) == 0 )
	{
	    conerror("Can't create Console write Message");
	    return NULL;
	}
    /* open console device */

	ConsoleWrite->io_Data = (APTR) ConsoleWindow;
	ConsoleWrite->io_Length = sizeof(*ConsoleWindow);
	if ( OpenDevice("console.device", 0L, ConsoleWrite, 0L) != 0 )
	{
	    conerror("Can't open console for write");
	    return NULL;
	}

	ConsoleWrite->io_Command = CMD_WRITE;

    /* create and open console read  message */

	if ( (cr = ConsoleRead = CreateStdIO(ReadPort)) == 0 )
	{
	    conerror("Can't create Console read Message");
	    return NULL;
	}

	cr->io_Device = cw->io_Device;
	cr->io_Unit = cw->io_Unit;
	cr->io_Command = CMD_READ;
	cr->io_Data = (APTR) ConsoleBuffer;
	cr->io_Length = 1;

    /* set up signal mask */

   	ConsoleWaitMask = 
              (1L << ConsoleWindow->UserPort->mp_SigBit)
           |  (1L << ConsoleRead->io_Message.mn_ReplyPort->mp_SigBit);

    /* start input for first character */

	cr->io_Command = CMD_READ;
	cr->io_Length = 1;
	SendIO(cr);

	return ConsoleWindow;
}


unsigned int getchar()
/*
 *	Wait for a character from the keyboard, or some
 *	mouse events.
 */
{
	register struct IntuiMessage *msg;
	register unsigned int result;
	struct IntuiMessage *GetMsg();

	while (TRUE)
	{
	    if ( msg = GetMsg(ConsoleWindow->UserPort) )
	    {
		ReplyMsg(msg);
		if ( (result = handle_msg(msg)) == 0 )
		    continue;
		return result;
	    }

	    if ( msg = GetMsg(ReadPort) )
	    {
		result = ConsoleBuffer[0];
		SendIO(ConsoleRead);	/* restart IO */
        	return result;
	    }
	    Wait(ConsoleWaitMask);
	}
       	return result;		/* Just here to keep Lattice happy */
}

int handle_msg(windowmsg)
	struct IntuiMessage *windowmsg;
/*
 *	This routine is called from getchar() whenever a window event
 *	occurs (IDCMP event).
 *	Here, we convert the window event to a character code
 *	which is defined in "console.h".
 *	You may wish to configure this routine to your specific
 *	application.
 */
{
	register struct IntuiMessage *msg = windowmsg;

	cmClass = msg->Class;
	cmCode = msg->Code;
	cmQualifier = msg->Qualifier;

	switch (msg->Class)
	{
	    case  SIZEVERIFY   	:
		return CODE_SIZEVERIFY;

	    case  NEWSIZE      	:
		return CODE_NEWSIZE;

	    case  REFRESHWINDOW	:
		return CODE_REFRESHWINDOW;

	    case  MOUSEBUTTONS 	:
	    {
	    	if (msg->Code == SELECTDOWN)
	    	{
		    cmMouseX = msg->MouseX;
		    cmMouseY = msg->MouseY;
		    return CODE_MOUSEBUTTONS;
		}
		return 0;	/* No action for mouse up events */
	    }

	    case  MOUSEMOVE    	:
		return CODE_MOUSEMOVE;

	    case  GADGETDOWN   	:
		return CODE_GADGETDOWN;

	    case  GADGETUP     	:
		return CODE_GADGETUP;

	    case  REQSET       	:
		return CODE_REQSET;

	    case  MENUPICK     	:
		return CODE_MENUPICK;

	    case  CLOSEWINDOW  	:
		return CODE_CLOSEWINDOW;

	    case  RAWKEY       	:
		return CODE_RAWKEY;

	    case  REQVERIFY    	:
		return CODE_REQVERIFY;

	    case  REQCLEAR     	:
		return CODE_REQCLEAR;

	    case  MENUVERIFY   	:
		return CODE_MENUVERIFY;

	    case  NEWPREFS     	:
		return CODE_NEWPREFS;

	    case  DISKINSERTED 	:
		return CODE_DISKINSERTED;

	    case  DISKREMOVED  	:
		return CODE_DISKREMOVED;

	    case  WBENCHMESSAGE	:
		return CODE_WBENCHMESSAGE;

	    case  ACTIVEWINDOW 	:
		return CODE_ACTIVEWINDOW;

	    case  INACTIVEWINDOW :
		return CODE_INACTIVEWINDOW;

	    case  DELTAMOVE    	:
		return CODE_DELTAMOVE;

	    case  VANILLAKEY   	:
		return CODE_VANILLAKEY;

	    case  INTUITICKS   	:
		return CODE_INTUITICKS;

	    case  LONELYMESSAGE	:
		return CODE_LONELYMESSAGE;
	
	}
	return CODE_INVALID;
}

void putchar(ch)
	char ch;
/*
 *	Write a character to the console
 */
{
	register struct IOStdReq *wm = ConsoleWrite;

	wm->io_Data = (APTR) &ch;
	wm->io_Length = 1;

	DoIO(wm);
}

void putstr(str)
	char *str;
/*
 *	Write a null-terminated string to the console.
 */
{
	register struct IOStdReq *wm = ConsoleWrite;

	wm->io_Data = (APTR) str;
	wm->io_Length = -1;

	DoIO(wm);

}

void Close_Console()
/*
 *	Close the console and clean up after yourself
 */
{
	AbortIO(ConsoleRead);
		
	if (ConsoleWrite)
	{
	    CloseDevice(ConsoleWrite);
	    DeleteStdIO(ConsoleWrite);
	    ConsoleWrite = 0;
	}

	if (ConsoleRead)
	{
	    DeleteStdIO(ConsoleRead);
	    ConsoleRead = 0;
	}

	if (ConsoleWindow)
	{
	    CloseWindow(ConsoleWindow);
	    ConsoleWindow = 0;
	} 
	
	if (WritePort)
	{
	    DeletePort(WritePort);
	    WritePort = 0;
	}

	if (ReadPort)
	{
	    DeletePort(ReadPort);
	    ReadPort = 0;
	}

/* You may wish to put these in your "main()" routine. I put them here
 * because it is the only place where they are used
 */
	if (GfxBase)
	{
	    CloseLibrary(GfxBase);
	    GfxBase = 0;
	}

	if (IntuitionBase)
	{
 	    CloseLibrary(IntuitionBase);
 	    IntuitionBase = 0;
	}
}

void conerror(msg)	
	char *msg;
/*
 *	Called whenever there is an error in opening the console
 *	device.
 */
{
	Console_Err_Msg = msg;	/* save your error message to be printed later */
	Close_Console();
}
