/****************************************************************************

                         OSK - The OnScreen Keyboard

                     © Copyright 1989 by Jonathan Potter

       Ever been caught without your keyboard? This program is for you!

          Not public domain, but as always, freely redistributable.

****************************************************************************/

/* To make from Manx

   cc +L main
   cc +L data
   cc +L image
   ln +Cd -O OSK main data image -Lc32 */

/* Not using pre-compiled includes? Work them out yourself, then */

#include "OSK.h"

main(argc,argv)
int argc;
char *argv[];
{
	ULONG class;
	USHORT code,gadgetid,qual=0;
	int a,x,y;
	struct Gadget *gad;

	port=CreatePort(0,0);
	request=CreateStdIO(port);
	OpenDevice("input.device",0,request,0);

	IntuitionBase=(struct IntuitionBase *) OpenLibrary("intuition.library",0);
	GfxBase=(struct GfxBase *) OpenLibrary("graphics.library",0);

	Window=NULL;
	fakewindow=IntuitionBase->ActiveWindow;
	if (argc>1 && strncmp(argv[1],"-i",2)==0) iconify();
	else setupall();

	FOREVER {
		Wait(1<<Window->UserPort->mp_SigBit);
		while (Msg=(struct IntuiMessage *) GetMsg(Window->UserPort)) {
			class=Msg->Class; code=Msg->Code;
			if (class==GADGETUP || class==GADGETDOWN) {
				gad=(struct Gadget *) Msg->IAddress;
				gadgetid=gad->GadgetID;
			}
			ReplyMsg((struct Message *) Msg);
			switch (class) {
				case CLOSEWINDOW:
					quit();
				case GADGETUP:
				case GADGETDOWN:
					if (gadgetid==999) {
						/* Choose a new window */
						hilitegad(gad);
						while (mousebutton());
						while (!mousebutton());
						Delay(5); /* Give Intuition a chance to catch up */
						if (IntuitionBase->ActiveWindow) /* Make sure it was a window,
                                                not just an empty screen */
							fakewindow=IntuitionBase->ActiveWindow;
						hilitegad(gad);
						break;
					}
          /* Work out qualifiers */
					if (gadgetid==0x63) {
						if (qual&IEQUALIFIER_CONTROL) qual-=IEQUALIFIER_CONTROL;
						else qual+=IEQUALIFIER_CONTROL;
					}
					else if (gadgetid==0x62) {
						if (qual&IEQUALIFIER_CAPSLOCK) qual-=IEQUALIFIER_CAPSLOCK;
						else qual+=IEQUALIFIER_CAPSLOCK;
					}
					else if (gadgetid==0x60) {
						if (qual&IEQUALIFIER_LSHIFT) qual-=IEQUALIFIER_LSHIFT;
						else qual+=IEQUALIFIER_LSHIFT;
					}
					else if (gadgetid==0x61) {
						if (qual&IEQUALIFIER_RSHIFT) qual-=IEQUALIFIER_RSHIFT;
						else qual+=IEQUALIFIER_RSHIFT;
					}
					else if (gadgetid==0x64) {
						if (qual&IEQUALIFIER_LALT) qual-=IEQUALIFIER_LALT;
						else qual+=IEQUALIFIER_LALT;
					}
					else if (gadgetid==0x65) {
						if (qual&IEQUALIFIER_RALT) qual-=IEQUALIFIER_RALT;
						else qual+=IEQUALIFIER_RALT;
					}
					else if (gadgetid==0x66) {
						if (qual&IEQUALIFIER_LCOMMAND) qual-=IEQUALIFIER_LCOMMAND;
						else qual+=IEQUALIFIER_LCOMMAND;
					}
					else if (gadgetid==0x67) {
						if (qual&IEQUALIFIER_RCOMMAND) qual-=IEQUALIFIER_RCOMMAND;
						else qual+=IEQUALIFIER_RCOMMAND;
					}
					else if (gadgetid==1000) iconify();
					else {
            /* Activate window so key presses will be sent to it */
						ActivateWindow(fakewindow);
						hilitegad(gad);
						FakeKey(gadgetid,qual);
						if (!mousebutton()) {
							hilitegad(gad);
							break;
						}
						Delay(5);
						if (!mousebutton()) {
							hilitegad(gad);
							break;
						}
						FOREVER {
							FakeKey(gadgetid,qual);
							if (!mousebutton()) {
								hilitegad(gad);
								break;
							}
							Delay(1);
						}
					}
					break;
			}
		}
	}
}

quit()
{
	freekeys(); /* De-allocate key gadgets */
	if (Window) CloseWindow(Window);
	if (GfxBase) CloseLibrary((struct Library *) GfxBase);
	if (IntuitionBase) CloseLibrary((struct IntuitionBase *) IntuitionBase);
	CloseDevice(request);
	DeleteStdIO(request);
	DeletePort(port);
	exit(0);
}

setupall()
{
	int a;
	char buf[80];

	if (!(Window=OpenWindow(&win))) quit();
	rp=Window->RPort;
	SetAPen(rp,1);
	RectFill(rp,4,11,499,103);
	SetAPen(rp,2);

	first=NULL; gadget=NULL;

	AddGList(Window,&iconGadget,1,1,NULL);
	RefreshGList(&iconGadget,Window,NULL,1);

	/* Setup keyboard */

	addkey("ESC",10,15,26,0,0x45); /* <-- last value is GadgetID.
                                        We use the rawkey code for that key
                                        to make things much easier */
	for (a=0;a<10;a++) {
		sprintf(buf,"F%d",a+1);
		addkey(buf,50+(a*32),15,26,0,0x50+a);
	}
	addkey("DEL",378,15,26,0,0x46);
	addkey("`",10,30,27,0,0);
	for (a=0;a<10;a++) {
		if (a<9) sprintf(buf,"%d",a+1);
		else strcpy(buf,"0");
		addkey(buf,45+(a*22),30,15,0,a+1);
	}
	addkey("-",265,30,15,0,0xb);
	addkey("=",287,30,15,0,0xc);
	addkey("\\",309,30,15,0,0xd);
	addkey("BACKSPACE",331,30,73,0,0x41);

	addkey("TAB",10,45,37,0,0x42);
	addkey("q",55,45,15,0,0x10);
	addkey("w",77,45,15,0,0x11);
	addkey("e",99,45,15,0,0x12);
	addkey("r",121,45,15,0,0x13);
	addkey("t",143,45,15,0,0x14);
	addkey("y",165,45,15,0,0x15);
	addkey("u",187,45,15,0,0x16);
	addkey("i",209,45,15,0,0x17);
	addkey("o",231,45,15,0,0x18);
	addkey("p",253,45,15,0,0x19);
	addkey("[",275,45,15,0,0x1a);
	addkey("]",297,45,15,0,0x1b);
	addkey("CR",319,45,43,0,0x44);
	addkey("HELP",369,45,35,0,0x5f);

	addkey("CT",10,60,18,1,0x63);
	addkey("CP",35,60,18,1,0x62);
	addkey("a",60,60,15,0,0x20);
	addkey("s",82,60,15,0,0x21);
	addkey("d",104,60,15,0,0x22);
	addkey("f",126,60,15,0,0x23);
	addkey("g",148,60,15,0,0x24);
	addkey("h",170,60,15,0,0x25);
	addkey("j",192,60,15,0,0x26);
	addkey("k",214,60,15,0,0x27);
	addkey("l",236,60,15,0,0x28);
	addkey(";",258,60,15,0,0x29);
	addkey("'",280,60,15,0,0x2a);
	addkey("CR",302,60,60,0,0x44);
	addkey("/\\",369,60,35,0,0x4c);

	addkey("SHIFT",10,75,50,1,0x60);
	addkey("z",67,75,15,0,0x31);
	addkey("x",89,75,15,0,0x32);
	addkey("c",111,75,15,0,0x33);
	addkey("v",133,75,15,0,0x34);
	addkey("b",155,75,15,0,0x35);
	addkey("n",177,75,15,0,0x36);
	addkey("m",199,75,15,0,0x37);
	addkey(",",221,75,15,0,0x38);
	addkey(".",243,75,15,0,0x39);
	addkey("/",265,75,15,0,0x3a);
	addkey("SHIFT",287,75,50,1,0x61);
	addkey("<-",344,75,35,0,0x4f);
	addkey("->",386,75,35,0,0x4e);

	addkey("ALT",30,90,30,1,0x64);
	addkey("A",67,90,30,1,0x66);
	addkey("SPACE",104,90,146,0,0x40);
	addkey("A",257,90,30,1,0x67);
	addkey("ALT",294,90,30,1,0x65);
	addkey("\\/",369,90,35,0,0x4d);

	addkey("7",435,30,15,0,0x3d);
	addkey("8",457,30,15,0,0x3e);
	addkey("9",479,30,15,0,0x3f);
	addkey("4",435,45,15,0,0x2d);
	addkey("5",457,45,15,0,0x2e);
	addkey("6",479,45,15,0,0x2f);
	addkey("1",435,60,15,0,0x1d);
	addkey("2",457,60,15,0,0x1e);
	addkey("3",479,60,15,0,0x1f);
	addkey("0",435,75,37,0,0xf);
	addkey(".",479,75,15,0,0x3c);
	addkey("-",435,90,15,0,0x4a);
	addkey("CR",457,90,37,0,0x43);

	addkey("Window",435,15,59,0,999); /* <-- This is the only non-key gadget
                                           and lets us choose a new window */
}

iconify()
{
	ULONG prevsec=0,prevmic=0,sec,mic;
	freekeys();
	if (Window) {
		win.LeftEdge=Window->LeftEdge; win.TopEdge=Window->TopEdge;
		CloseWindow(Window);
	}
	Window=(struct Window *) OpenWindow(&iconwin);
	FOREVER {
		Wait(1<<Window->UserPort->mp_SigBit);
		while (Msg=(struct IntuiMessage *) GetMsg(Window->UserPort)) {
			sec=Msg->Seconds; mic=Msg->Micros;
			ReplyMsg((struct Message *) Msg);
			if (DoubleClick(prevsec,prevmic,sec,mic)) {
				iconwin.LeftEdge=Window->LeftEdge; iconwin.TopEdge=Window->TopEdge;
				CloseWindow(Window);
				setupall();
				return(0);
			}
			prevsec=sec; prevmic=mic;
		}
	}
}

/* A routine to generate gadgets.. shorter than having data
   for hundreds of gadgets */

addkey(str,x,y,w,t,id)
char *str;
SHORT x,y,w,t;
USHORT id;
{
	temp=(struct GList *) AllocMem(sizeof(struct GList),MEMF_CLEAR);
	if (first==NULL) first=temp;
	else gadget->next=temp;
	gadget=temp;
	gadget->next=NULL;

	gadget->text.FrontPen=0;
	gadget->text.BackPen=0;
	gadget->text.DrawMode=JAM1;
	gadget->text.LeftEdge=(w-(strlen(str)*8))/2;
	gadget->text.TopEdge=1;
	gadget->text.ITextFont=NULL;
	gadget->text.IText=(UBYTE *) str;
	gadget->text.NextText=NULL;

	gadget->gadget.NextGadget=NULL;
	gadget->gadget.LeftEdge=x;
	gadget->gadget.TopEdge=y;
	gadget->gadget.Width=w;
	gadget->gadget.Height=10;
	gadget->gadget.Flags=GADGHCOMP;
	if (t==0) gadget->gadget.Activation=GADGIMMEDIATE;
	else gadget->gadget.Activation=GADGIMMEDIATE|TOGGLESELECT;
	gadget->gadget.GadgetType=BOOLGADGET;
	gadget->gadget.GadgetRender=NULL;
	gadget->gadget.SelectRender=NULL;
	gadget->gadget.GadgetText=&gadget->text;
	gadget->gadget.MutualExclude=NULL;
	gadget->gadget.SpecialInfo=NULL;
	gadget->gadget.GadgetID=id;
	gadget->gadget.UserData=NULL;

	AddGList(Window,&gadget->gadget,-1,1,NULL);
	RefreshGList(&gadget->gadget,Window,NULL,1);

	/* Draw the border ourselves */
	Move(rp,x-1,y-1);
	Draw(rp,x+w,y-1);
	Draw(rp,x+w,y+10);
	Draw(rp,x-1,y+10);
	Draw(rp,x-1,y-1);
}

/* Deallocate gadget structures */
freekeys()
{
	gadget=first;
	while (gadget) {
		temp=gadget->next;
		if (gadget) FreeMem(gadget,sizeof(struct GList));
		gadget=temp;
	}
}

/* Generate a fake key press */
FakeKey(key,qual)
USHORT key,qual;
{
	request->io_Command=IND_WRITEEVENT;
	request->io_Flags=0;
	request->io_Length=sizeof(struct InputEvent);
	request->io_Data=(APTR)&Event;
	Event.ie_NextEvent=NULL;
	Event.ie_Class=IECLASS_RAWKEY;
	Event.ie_TimeStamp.tv_secs=0;
	Event.ie_TimeStamp.tv_micro=0;
	Event.ie_Code=key;
	Event.ie_Qualifier=qual;
	Event.ie_X=0;
	Event.ie_Y=0;
	DoIO(request);
}

/* Return state of left mousebutton */
mousebutton()
{
	return(!((USHORT)ciaa.ciapra>>6&1));
}

hilitegad(gad)
struct Gadget *gad;
{
	SetDrMd(rp,COMPLEMENT);
	RectFill(rp,gad->LeftEdge,gad->TopEdge,
		gad->LeftEdge+gad->Width-1,gad->TopEdge+gad->Height-1);
	SetDrMd(rp,JAM1);
}
