// Lupe V1.5a - 1995-96 - © Frank Toepper
// a lens
// AmigaOS 3.0+
// Maxon C++ V3
// last changes: 28.July.96

#include <string.h>
#include <iostream.h>
#include <intuition/intuition.h>
#include <intuition/imageclass.h>
#include <intuition/icclass.h>
#include <intuition/gadgetclass.h>
#include <intuition/intuitionbase.h>
#include <exec/exec.h>
#include <dos/dos.h>
#include <graphics/scale.h>
#include <devices/inputevent.h>
#include <libraries/commodities.h>
#include <libraries/gadtools.h>
#include <graphics/displayinfo.h>
#include <graphics/gfxmacros.h>
#include <graphics/gfxbase.h>
#include <cybergraphics/cybergraphics.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/exec_protos.h>
#include <clib/commodities_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/icon_protos.h>
#include <clib/cybergraphics_protos.h>
#include <pragma/cybergraphics_lib.h>
#include <wbstartup.h>

#pragma header

#define CYBERARRAY_BPP 3
#define MINWIDTH 88
#define MINHEIGHT 30

WORD innerwidth = 150, innerheight = 100, sourcewidth, sourceheight;
WORD scalefac = 5, maxscalefac = 15, minwidth, fixedX = 0, fixedY = 0;
WORD leftoff, topoff, bottomoff, rightoff;
WORD sizeiw, sizeih, winx, winy, winleft = 0, wintop = 11, originX = 0, originY = 0;
WORD textoff;
ULONG cxsigflag, scrdepth;
LONG waitmask = 0, pens[4] = {2, 3, 1, 0};
APTR VisualInfo, cyberdata = NULL;
struct MsgPort *broker_mp, *userport;
struct Task *thistask;
struct Window *mywin;
struct Screen *scr = NULL, *tmpscr;
struct Menu *menu = NULL;
struct MenuItem *item;
struct BitMap *srcbm = NULL, *destbm = NULL, *scrbm;
struct TextFont *screenfont = NULL, *topazfont;
struct InputXpression setoriginix, resetoriginix, showhideix;
struct RastPort *scrrp, destrp, *winrp;
struct Library *CxBase, *GfxBase, *GadToolsBase, *CyberGfxBase, *IconBase;
BYTE timesig, showhidesig;
BOOL hires, newlook, jump = FALSE, fast = FALSE, pubscreenlocked = FALSE, enable = TRUE, cybergfxscreen, coords = FALSE;
BOOL crosshair = FALSE, origin = TRUE, fixed = FALSE, jumpdirect, showhide = TRUE, hide = FALSE, hideonclose = FALSE;
CxObj *broker, *customcxobj;
Object *propgadget;
char pubscreenname[MAXPUBSCREENNAME];
char coordstring[12], setoriginkey[50] = "lalt o", resetoriginkey[50] = "lalt r", showhidekey[50] = "lalt h";
char title[10];
char *Template = "CX_PRIORITY=P/N/K, F=FAST/S/K, MAXSCALEFACTOR=M/N/K, SCALEFACTOR=S/N/K, WINLEFT=L/N/K, WINTOP=T/N/K, "
					  "WINWIDTH=W/N/K, WINHEIGHT=H/N/K, COORDINATES=C/S/K, CROSSHAIR=R/S/K, SETORIGINKEY=O/K, "
					  "RESETORIGINKEY=K/K, FIXED=D/S/K, SHOWHIDEKEY=SH/K, HIDE/S/K, HIDEONCLOSE=HOC/S/K";

extern struct IntuitionBase *IntuitionBase;

// function declarations for menu
BOOL jumpFunc ();
BOOL fastFunc ();
BOOL AboutFunc ();
BOOL enableFunc ();
BOOL coordsFunc ();
BOOL crosshairFunc ();
BOOL fixedFunc ();
BOOL QuitFunc ();
BOOL hideFunc ();
BOOL hideoncloseFunc ();

struct EasyStruct es_about = {
   20, 0,
   "Lupe",
   "%s",
   "%s"
};

char *era_about[] = {
   "Lupe V1.5a (" __DATE__ ")\n\n"
   "written by:\n"
   "  Frank Toepper\n"
   "  Maxim Gorki Straße 5A\n"
   "  Greifswald\n"
   "  17491\n"
   "  GERMANY\n\n"
   "E-Mail:\n"
   "  toepper@rz.uni-greifswald.de\n"
   "WWW:\n"
   "  http://www.user.fh-stralsund.de/~rwermke/di.html\n\n"
   "This program is Public Domain",
   "Ok"
};

struct NewBroker newbroker = {
   NB_VERSION,
   "Lupe",
   "Lupe V1.5a © 1995-96 Frank Toepper",
   "A magnifing glass program.",
   3,
   COF_SHOW_HIDE,
   0,
   NULL,
   0
};

struct BitScaleArgs bsa = {
   // Src Koords
   0, 0,
   innerwidth / scalefac,
   innerheight / scalefac,
   // Src Faktoren
   1, 1,
   0, 0,
   innerwidth,
   innerheight,
   // Dest Faktoren
   scalefac, scalefac,
   // Bitmaps
   NULL, NULL,
   0,
   0, 0,
   0, 0
};

struct TextAttr topaz_attr = {
   "topaz.font",
   8,
   0,
   FPF_ROMFONT
};

void Putchar (register __d0 char zeichen, register __a3 char *putchdata)
{
   *putchdata++ = zeichen;
}

void error (char *formatstring, ...)
{
 APTR args = &formatstring + 1;
 struct EasyStruct easystruct = {
    sizeof (struct EasyStruct),
    0,
    "Error",
    formatstring,
    "Ok"
 };

   if (Cli ())
   {
      VPrintf (formatstring, args);
      FPutC (Output (), '\n');
      Flush (Output ());
   }
   else
   {
      EasyRequestArgs (NULL, &easystruct, NULL, args);
   }
}

BOOL lockaspublicscreen (struct Screen *screen)
{
 struct List *pubscreenlist;
 struct PubScreenNode *pubscreennode;

	pubscreenlist = LockPubScreenList ();
   if (pubscreenlist)
   {
      pubscreennode = (struct PubScreenNode *) pubscreenlist->lh_Head;
      while (pubscreennode)
      {
         if (pubscreennode->psn_Screen == screen)
         {
            if (LockPubScreen (pubscreennode->psn_Node.ln_Name))
            {
               UnlockPubScreenList ();
               return TRUE;
            }
         }
         pubscreennode = (struct PubScreenNode *) pubscreennode->psn_Node.ln_Succ;
      }
      UnlockPubScreenList ();
   }
   return FALSE;
}

int setupscreen (struct Screen *selectedscreen)
{
 ULONG id;

   jumpdirect = FALSE;
   if (selectedscreen)
   {
      jumpdirect = TRUE;
      if (scr)
      {
      	if (VisualInfo) FreeVisualInfo (VisualInfo);
         if (pubscreenlocked) UnlockPubScreen (NULL, scr);
      }
      if (lockaspublicscreen (selectedscreen)) pubscreenlocked = TRUE;
      else pubscreenlocked = FALSE;
      scr = selectedscreen;
   }
   else if (scr)
   {
   	tmpscr = scr->NextScreen;
      if (tmpscr)
      {
         FreeVisualInfo (VisualInfo);
         VisualInfo = NULL;
         if (pubscreenlocked)
         {
            UnlockPubScreen (NULL, scr);
            pubscreenlocked = FALSE;
         }
         if (lockaspublicscreen (tmpscr)) pubscreenlocked = TRUE;
      }
      else
      {
         error ("There is no other screen.");
         return 1;  // not real an error
      }
      scr = tmpscr;
   }
   else if ((scr = LockPubScreen (NULL)) != 0)
   {
      pubscreenlocked = TRUE;
   }
   else
   {
      error ("Can't lock publicscreen.");
      return 2;
   }
   VisualInfo = GetVisualInfoA (scr, NULL);
   if (!VisualInfo)
   {
      error ("Can't get visualinfo from screen.");
      return 3;
   }
   cybergfxscreen = FALSE;
   if (CyberGfxBase)
      if ((id = GetVPModeID (&scr->ViewPort)) != INVALID_ID)
         if (IsCyberModeID (id))
            if (GetCyberIDAttr (CYBRIDATTR_BPPIX, id) > 1)
               cybergfxscreen = TRUE;
   return 0;
}

void CloseDownScreen ()
{
   if (VisualInfo)
   {
      FreeVisualInfo (VisualInfo);
      VisualInfo = NULL;
   }
   if (scr && pubscreenlocked)
   {
      UnlockPubScreen (NULL, scr);
      pubscreenlocked = FALSE;
   }
}

void getoffsets ()
{
 struct DrawInfo *drawinfo;
 APTR sizeobject;
 ULONG attr;

   if (screenfont)
   {
      CloseFont (screenfont);
      screenfont = NULL;
   }
   hires = (scr->Flags & SCREENHIRES) != 0;
   hires ? (rightoff = 18) : (rightoff = 13);
   drawinfo = GetScreenDrawInfo (scr);
   if (drawinfo)
   {
      sizeobject = NewObject (NULL, SYSICLASS,
       SYSIA_Which, SIZEIMAGE,
       SYSIA_DrawInfo, drawinfo,
       SYSIA_Size, hires ? SYSISIZE_MEDRES : SYSISIZE_LOWRES,
       TAG_END);
      if (sizeobject)
      {
         if (GetAttr (IA_Width, sizeobject, &attr))
         {
            sizeiw = attr;
            rightoff =  (UWORD) attr;
         }
         if (GetAttr (IA_Height, sizeobject, &attr))
         {
            sizeih = attr;
         }
         newlook = (drawinfo->dri_Flags & DRIF_NEWLOOK) && (drawinfo->dri_Depth != 1);
         DisposeObject (sizeobject);
      }
      pens[0] = drawinfo->dri_Pens[FILLTEXTPEN];
      pens[1] = drawinfo->dri_Pens[FILLPEN];
      pens[2] = drawinfo->dri_Pens[TEXTPEN];
      pens[3] = drawinfo->dri_Pens[BACKGROUNDPEN];
      FreeScreenDrawInfo (scr, drawinfo);
   }
   scrrp = &scr->RastPort;
   topoff = scrrp->TxHeight + (UWORD) scr->WBorTop + 1;
   leftoff = scr->WBorLeft;
   scrbm = scrrp->BitMap;
   scrdepth = GetBitMapAttr (scrbm, BMA_DEPTH);
   minwidth = MINWIDTH;
   if (coords)
   {
      bottomoff = sizeih;
      // center the text in the bottomborder
      textoff = (bottomoff + topazfont->tf_YSize >> 1) - topazfont->tf_Baseline;
      // SysIHack or height of screenfont <= 8?
      if ((bottomoff - 2) >= scr->Font->ta_YSize)
      {
      	screenfont = OpenFont (scr->Font);
         if (screenfont)
         {
            minwidth = 11 * (screenfont->tf_XSize);
            // center the text in the bottomborder
            textoff = (bottomoff + screenfont->tf_YSize >> 1) - screenfont->tf_Baseline;
         }
      }
   }
   else bottomoff = scr->WBorBottom;
}

void setwintitle ()
{
 LONG args[] = { (LONG) "Lupe", scalefac };

   RawDoFmt ("%s %ld:1", args, (void (*)()) Putchar, title);
   SetWindowTitles (mywin, title, (STRPTR) ~0);
}

int allocbm ()
{
   if (destbm) FreeBitMap (destbm);
   if (srcbm) FreeBitMap (srcbm);
   destbm = srcbm = NULL;
   if (cyberdata) FreeVec (cyberdata);
   cyberdata = NULL;
   winx = mywin->Width;
   winy = mywin->Height;
   innerwidth = winx - (leftoff + rightoff);
   innerheight = winy - (topoff + bottomoff);
   sourcewidth = innerwidth / scalefac;
   sourceheight = innerheight / scalefac;
   if (scalefac > 1)
   {
      if (cybergfxscreen)
      {
         if (!(cyberdata = AllocVec (sourcewidth * sourceheight * CYBERARRAY_BPP, MEMF_PUBLIC)))
         {
            error ("Can't allocate %ld Bytes for CyberPixelArray.", sourcewidth * sourceheight * CYBERARRAY_BPP);
            return 1;
         }
         if (!(destbm = AllocBitMap (innerwidth, innerheight, scrdepth, BMF_CLEAR | BMF_MINPLANES, scrbm)))
         {
            error ("'AllocBitMap' for destination bitmap failed.");
            return 3;
         }
      }
      else
      {
         if (!(srcbm = AllocBitMap (sourcewidth, sourceheight, scrdepth, BMF_CLEAR, scrbm)))
         {
            error ("'AllocBitMap' for source bitmap failed.");
            return 2;
         }
         if (!(destbm = AllocBitMap (innerwidth, innerheight, scrdepth, BMF_CLEAR, scrbm)))
         {
            error ("'AllocBitMap' for destination bitmap failed.");
            return 3;
         }
         bsa.bsa_SrcWidth = sourcewidth;
         bsa.bsa_SrcHeight = sourceheight;
         bsa.bsa_DestWidth = innerwidth;
         bsa.bsa_DestHeight = innerheight;
         bsa.bsa_SrcBitMap = srcbm;
         bsa.bsa_DestBitMap = destbm;
         bsa.bsa_XDestFactor = bsa.bsa_YDestFactor = scalefac;
      }
      destrp.BitMap = destbm;
   }
   return 0;
}

BOOL makescreensmenu ()
{
 struct NewMenu tmpmenu[] = {
  { NM_SUB, 0, 0, 0, 0, 0 },
  { NM_END, 0, 0, 0, 0, 0 }
 };
 ULONG lock;
 struct List *pubscreenlist;
 struct PubScreenNode *pubscreennode;
 struct MenuItem **itempointer = &menu->FirstItem->NextItem->SubItem;
 int i = 0;

   lock = LockIBase (0);
   tmpscr = IntuitionBase->FirstScreen;
   if (tmpscr->NextScreen)
   {
      while ((tmpscr = tmpscr->NextScreen) != 0)
      {
         tmpmenu[0].nm_Label = tmpscr->DefaultTitle;
         pubscreenlist = LockPubScreenList ();
         if (pubscreenlist)
         {
            pubscreennode = (struct PubScreenNode *) pubscreenlist->lh_Head;
            while (pubscreennode)
            {
               if (pubscreennode->psn_Screen == tmpscr)
               {
                  tmpmenu[0].nm_Label = pubscreennode->psn_Node.ln_Name;
                  break;
               }
               pubscreennode = (struct PubScreenNode *) pubscreennode->psn_Node.ln_Succ;
            }
            UnlockPubScreenList ();
         }
         tmpmenu[0].nm_UserData = tmpscr;
         // each item single created
         *itempointer = (struct MenuItem *) CreateMenusA (tmpmenu, NULL);
         if (*itempointer)
         {
            itempointer = &(*itempointer)->NextItem;
         }
         else error ("Can't create a screensmenu item.");
      }
   }
   else
   {
      tmpmenu[0].nm_Label = "No Other Screen";
      *itempointer = (struct MenuItem *) CreateMenusA (tmpmenu, NULL);
      if (*itempointer)
      {
         (*itempointer)->Flags |= HIGHNONE;
         itempointer = &(*itempointer)->NextItem;
      }
      else error ("Can't create screensmenu.");
   }
   UnlockIBase (lock);
   if (LayoutMenus (menu, VisualInfo, GTMN_NewLookMenus, TRUE, TAG_DONE))
   {
      return TRUE;
   }
   else
   {
   	error ("Can't layout screensmenu.");
   }
   return FALSE;
}

void freescreensmenu (struct MenuItem *menuitem)
{
   if (menuitem)
   {
      freescreensmenu (menuitem->NextItem);
      FreeMenus ((struct Menu *) menuitem);
   }
}

BOOL openwin ()
{
 ULONG lock;
 WORD bw, rh;
 struct Rectangle zoom = {
    minwidth + leftoff + rightoff,
    MINHEIGHT + topoff + bottomoff + sizeih,
    -1, -1
 };
 struct NewMenu standartmenu[] = {
   { NM_TITLE, "Project",        0, 0, 0, 0 },
   {  NM_ITEM, "Jump",           "J", 0, 0, jumpFunc },
   {  NM_ITEM, "Jump To Screen", 0, 0, 0, 0 },
   {  NM_ITEM, NM_BARLABEL,      0, 0, 0, 0 },
   {  NM_ITEM, "Enable",         "E", (enable ? CHECKED : 0) | CHECKIT | MENUTOGGLE, 0, enableFunc },
   {  NM_ITEM, "Fixed",          "D", (fixed ? CHECKED : 0) | CHECKIT | MENUTOGGLE, 0, fixedFunc },
   {  NM_ITEM, "Fast",           "F", (fast ? CHECKED : 0) | CHECKIT | MENUTOGGLE, 0, fastFunc },
   {  NM_ITEM, "Coordinates",    "C", (coords ? CHECKED : 0) | CHECKIT | MENUTOGGLE, 0, coordsFunc },
   {  NM_ITEM, "Crosshair",      "R", (crosshair ? CHECKED : 0) | CHECKIT | MENUTOGGLE, 0, crosshairFunc },
   {  NM_ITEM, NM_BARLABEL,      0, 0, 0, 0 },
   {  NM_ITEM, "About",          "?", 0, 0, AboutFunc },
   {  NM_ITEM, NM_BARLABEL,      0, 0, 0, 0 },
   {  NM_ITEM, "Hide On Close",  "I", (hideonclose ? CHECKED : 0) | CHECKIT | MENUTOGGLE, 0, hideoncloseFunc },
   {  NM_ITEM, "Hide",           "H", 0, 0, hideFunc },
   {  NM_ITEM, "Quit",           "Q", 0, 0, QuitFunc },
   {  NM_END, 0, 0, 0, 0, 0 }
 };

	if (!mywin)
	{
	   if (hires) bw = rh = 2;
   	else bw = rh = 1;
	   // first bring the screen to front
   	//  -> screen order in menu correct
	   lock = LockIBase (0);
   	tmpscr = IntuitionBase->FirstScreen;
	   UnlockIBase (lock);
   	if (tmpscr != scr)
	   {
   	   if (jumpdirect) ScreenToFront (scr);
      	else ScreenToBack (tmpscr);
	   }
	   menu = CreateMenusA (standartmenu, NULL);
   	if (menu)
	   {
   	   if (makescreensmenu ())
      	{
         	propgadget = (Object *) NewObject (NULL, PROPGCLASS,
	          PGA_Freedom,     FREEVERT,
   	       ICA_TARGET,      ICTARGET_IDCMP,
      	    PGA_NewLook,     TRUE,
         	 PGA_Borderless,  newlook,
	          PGA_Total,       maxscalefac,
   	       PGA_Visible,     1,
      	    PGA_Top,         scalefac - 1,
         	 GA_RelRight,     bw - sizeiw + 3,
	          GA_Top,          topoff + rh,
   	       GA_Width,        sizeiw - bw - bw - 4,
      	    GA_RelHeight,    -topoff - sizeih - rh - rh,
         	 GA_RightBorder,  TRUE,
	          TAG_DONE);
	         if (propgadget)
   	      {
      	      mywin = OpenWindowTags (NULL,
         	    WA_Flags,          WFLG_ACTIVATE | WFLG_CLOSEGADGET | WFLG_DEPTHGADGET | WFLG_DRAGBAR
            	                     | WFLG_SIZEGADGET | WFLG_SIMPLE_REFRESH | WFLG_NEWLOOKMENUS
               	                  | WFLG_NOCAREREFRESH | WFLG_REPORTMOUSE
                  	               | WFLG_SIZEBRIGHT | (coords ? WFLG_SIZEBBOTTOM : 0),
      	       WA_AutoAdjust,     1,
	             WA_MaxHeight,      -1,
   	          WA_MaxWidth,       -1,
      	       WA_MinHeight,      MINHEIGHT + topoff + bottomoff,
	             WA_MinWidth,       minwidth + leftoff + rightoff,
   	          WA_IDCMP,          IDCMP_CLOSEWINDOW | IDCMP_IDCMPUPDATE | IDCMP_NEWSIZE | IDCMP_MENUPICK
         	                        | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW | IDCMP_MOUSEBUTTONS
            	                     | IDCMP_MOUSEMOVE | IDCMP_VANILLAKEY | IDCMP_RAWKEY,
	             WA_InnerHeight,    innerheight,
   	          WA_InnerWidth,     innerwidth,
      	       WA_Left,           winleft,
         	    WA_Gadgets,        propgadget,
            	 WA_Top,            wintop,
	             WA_PubScreen,      scr,
   	          WA_Zoom,           &zoom,
      	       TAG_DONE);
      	      if (mywin)
         	   {
            	   // shit patches
               	leftoff = mywin->BorderLeft;
	               bottomoff = mywin->BorderBottom;
   	            SetMenuStrip (mywin, menu);
      	         setwintitle ();
         	      userport = mywin->UserPort;
            	   winrp = mywin->RPort;
               	if (screenfont)
               	{
               		SetFont (winrp, screenfont);
               	}
	               else
	               {
	               	SetFont (winrp, topazfont);
	               }
   	            SetAPen (winrp, pens[0]);
      	         SetBPen (winrp, pens[1]);
					   waitmask |= 1 << userport->mp_SigBit;
         	      return TRUE;
            	}
	            else error ("Can't open window.");
   	      }
      	   else error ("Can't create propgadget.");
	      }
   	}
	   else error ("Can't create menu.");
	   return FALSE;
	}
	return TRUE;  // win already open
}

void closewin ()
{
   if (mywin)
	{
	   if (menu) ClearMenuStrip (mywin);
   	wintop = mywin->TopEdge;
	   winleft = mywin->LeftEdge;
   	waitmask &= ~(1 << userport->mp_SigBit);
	   CloseWindow (mywin);
   	mywin = NULL;
   }
   if (menu)
   {
      freescreensmenu (ItemAddress (menu, FULLMENUNUM (0, 1, 0)));
      FreeMenus (menu);
      menu = NULL;
   }
   if (propgadget)
   {
   	DisposeObject (propgadget);
	   propgadget = NULL;
	}
   if (srcbm)
   {
   	FreeBitMap (srcbm);
	   srcbm = NULL;
   }
   if (destbm)
   {
   	FreeBitMap (destbm);
	   destbm = NULL;
   }
   if (cyberdata)
   {
   	FreeVec (cyberdata);
	   cyberdata = NULL;
   }
}

BOOL screen_is_still_open ()
{
	tmpscr = IntuitionBase->FirstScreen;
	while (tmpscr)
 	{
 		if (tmpscr == scr) return TRUE;
 		tmpscr = tmpscr->NextScreen;
 	}
	return FALSE;
}

BOOL showwindow ()
{
 BOOL returnvalue = TRUE;

  	if (!mywin)
  	{
  		if (!screen_is_still_open ()) scr = NULL;
  		try
  		{
 			if (setupscreen (scr)) throw 1;
			getoffsets ();
		   if (!openwin ()) throw 2;
		   if (allocbm ()) throw 3;
		}
		catch (int)
		{
			returnvalue = FALSE;
		}
  	}
  	return returnvalue;
}

void hidewindow ()
{
	closewin ();
	CloseDownScreen ();
}

void refresh ()
{
 WORD x, y, xoff = 0, yoff = 0;

	if (mywin)
	{
	   if (fixed)
   	{
      	x = fixedX;
	      y = fixedY;
   	}
	   else
   	{
      	x = scr->MouseX;
	      y = scr->MouseY;
   	}
	   // print the coordinates
   	if (coords)
	   {
   	   LONG args[] = {x - originX, y - originY};
      	memset (coordstring, ' ', 11);
	      RawDoFmt ("%ld,%ld", args, (void (*)()) Putchar, coordstring);
   	   coordstring[strlen (coordstring)] = ' ';
      	Move (winrp, leftoff, mywin->Height - textoff);
	      Text (winrp, coordstring, 11);
   	}
	   // adjust the coordinates
   	x -= sourcewidth >> 1;
	   y -= sourceheight >> 1;
   	if (x < 0)
	   {
   	   xoff = x;
      	x = 0;
	   }
   	else if (x > (scr->Width - sourcewidth))
	   {
   	   xoff = -(scr->Width - sourcewidth - x);
      	x = scr->Width - sourcewidth;
	   }
   	if (y < 0)
	   {
   	   yoff = y;
      	y = 0;
	   }
   	else if (y > (scr->Height - sourceheight))
	   {
   	   yoff = -(scr->Height - sourceheight - y);
      	y = scr->Height - sourceheight;
	   }
   	// calculate the crosshair
	   if (crosshair)
   	{
      	xoff = scalefac * ((sourcewidth >> 1) + xoff) + (scalefac >> 1);
	      yoff = scalefac * ((sourceheight >> 1) + yoff) + (scalefac >> 1);
   	}
	   if (scalefac > 1)
   	{
      	// blit and scale
	      if (cybergfxscreen)
   	   {
      	   // BitMapScale don't work with bitmaps >8 Bit (CyberGfx 2.15)
         	ReadPixelArray (cyberdata, 0, 0, sourcewidth * CYBERARRAY_BPP, scrrp, x, y, sourcewidth, sourceheight, RECTFMT_RGB);
	         ScalePixelArray (cyberdata, sourcewidth, sourceheight, sourcewidth * CYBERARRAY_BPP, &destrp, 0, 0, sourcewidth * scalefac, sourceheight * scalefac, RECTFMT_RGB);
   	   }
      	else
	      {
   	      BltBitMap (scrbm, x, y, srcbm, 0, 0, sourcewidth, sourceheight, ABNC | ABC, ~0, NULL);
      	   BitMapScale (&bsa);
	      }
   	   // draw the crosshair
      	if (crosshair)
	      {
   	      Move (&destrp, xoff, 0);
      	   Draw (&destrp, xoff, sourceheight * scalefac - 1);
         	Move (&destrp, 0, yoff);
	         Draw (&destrp, sourcewidth * scalefac - 1, yoff);
   	   }
      	mywin->Flags &= ~SIZEGADGET;
	      if ((winx == mywin->Width) && (winy == mywin->Height))
   	      BltBitMapRastPort (destbm, 0, 0, winrp, leftoff, topoff, innerwidth, innerheight, ABNC | ABC);
      	mywin->Flags |= SIZEGADGET;
	   }
   	else  // 1:1 - mode
	   {
   	   mywin->Flags &= ~SIZEGADGET;
      	if ((winx == mywin->Width) && (winy == mywin->Height))
	      {
   	      BltBitMapRastPort (scrbm, x, y, winrp, leftoff, topoff, innerwidth, innerheight, ABNC | ABC);
      	   // draw the crosshair in 1:1 - mode
	         if (crosshair)
   	      {
      	      SetDrMd (winrp, COMPLEMENT);
         	   Move (winrp, leftoff + xoff, topoff);
            	Draw (winrp, leftoff + xoff, topoff + innerheight - 1);
	            Move (winrp, leftoff, topoff + yoff);
   	         Draw (winrp, leftoff + innerwidth - 1, topoff + yoff);
      	      SetDrMd (winrp, JAM2);
         	}
	      }
   	   mywin->Flags |= SIZEGADGET;
	   }
	}
}

void CxFunction (CxMsg *cxm, CxObj *co)
{
 struct InputEvent *ie = (struct InputEvent *) CxMsgData (cxm);

   if (origin)
   {
      if (MatchIX (ie, &setoriginix))
      {
         originX = scr->MouseX;
         originY = scr->MouseY;
         DisposeCxMsg (cxm);
         return;
      }
      if (MatchIX (ie, &resetoriginix))
      {
         originX = originY = 0;
         DisposeCxMsg (cxm);
         return;
      }
   }
   if (showhide)
   {
   	if (MatchIX (ie, &showhideix))
   	{
   		Signal (thistask, 1 << showhidesig);
   	}
   }
   if ((ie->ie_Class == IECLASS_TIMER) || fast)
      Signal (thistask, 1 << timesig);
}

BOOL initbroker ()
{
 LONG errorcode;

	broker_mp = CreateMsgPort ();
   if (broker_mp)
   {
      newbroker.nb_Port = broker_mp;
      cxsigflag = 1 << broker_mp->mp_SigBit;
      broker = CxBroker (&newbroker, &errorcode);
      if (broker)
      {
      	customcxobj = CxCustom (CxFunction, 0);
         if (customcxobj)
         {
            if (!CxObjError (customcxobj))
            {
               AttachCxObj (broker, customcxobj);
               if (ParseIX (setoriginkey, &setoriginix) || ParseIX (resetoriginkey, &resetoriginix))
               {
                  origin = FALSE;
                  error ("Can't init a Hotkey.\n%s disabled.", "'Set Origin'");
               }
               if (ParseIX (showhidekey, &showhideix))
               {
               	showhide = FALSE;
                  error ("Can't init a Hotkey.\n%s disabled.", "'Show/Hide Window'");
               }
               ActivateCxObj (broker, 1);
               return TRUE;
            }
            DeleteCxObj (customcxobj);
         }
         else error ("Can't create commodity-custom-objekt.");
         DeleteCxObjAll (broker);
      }
      else if (errorcode == CBERR_SYSERR)
         error ("Can't create commodity-broker-objekt.");
      DeleteMsgPort (broker_mp);
   }
   else error ("Can't create messageport for commodity-broker");
   return FALSE;
}

void setmenu ()
{
 struct MenuItem *mitem;

	if (mywin)
	{
	   ClearMenuStrip (mywin);

   	mitem = ItemAddress (menu, FULLMENUNUM (0, 3, NOSUB));
	   if (enable) mitem->Flags |= CHECKED;
   	   else mitem->Flags &= ~CHECKED;

   	mitem = ItemAddress (menu, FULLMENUNUM (0, 4, NOSUB));
	   if (fixed) mitem->Flags |= CHECKED;
   	   else mitem->Flags &= ~CHECKED;

   	mitem = ItemAddress (menu, FULLMENUNUM (0, 5, NOSUB));
	   if (fast) mitem->Flags |= CHECKED;
   	   else mitem->Flags &= ~CHECKED;

   	mitem = ItemAddress (menu, FULLMENUNUM (0, 7, NOSUB));
	   if (crosshair) mitem->Flags |= CHECKED;
   	   else mitem->Flags &= ~CHECKED;

   	mitem = ItemAddress (menu, FULLMENUNUM (0, 11, NOSUB));
	   if (hideonclose) mitem->Flags |= CHECKED;
   	   else mitem->Flags &= ~CHECKED;

	   ResetMenuStrip (mywin, menu);
	}
}

BOOL reopenwin ()
{
   closewin ();
   getoffsets ();
   if (!openwin ()) return FALSE;
   if (allocbm ()) return FALSE;
   jump = TRUE;
   return TRUE;
}

BOOL setpropgad ()
{
   if (allocbm ()) return FALSE;
   SetAttrs (propgadget, PGA_Top, scalefac - 1);
   RefreshGList ((struct Gadget *) propgadget, mywin, NULL, 1);
	setwintitle ();
   return TRUE;
}

BOOL menupick (UWORD code)
{
 BOOL returnvalue = TRUE;
 static BOOL (*func)();

   while ((code != MENUNULL) && returnvalue && !jump)
   {
      item = ItemAddress (menu, code);
      if (SUBNUM (code) != NOSUB)
      {
         if (GTMENUITEM_USERDATA (item))  // 'no other screen'-submenu == 0
         {
            try
            {
               if (setupscreen ((struct Screen *) GTMENUITEM_USERDATA (item))) throw 1;
               closewin ();
               getoffsets ();
               if (!openwin ()) throw 2;
               if (allocbm ()) throw 3;
            }
            catch (int)
            {
               returnvalue = FALSE;
            }
            jump = TRUE;
         }
      }
      else
      {
         func = (BOOL (*)()) GTMENUITEM_USERDATA (item);
         returnvalue = func ();
      }
      if (!jump) code = item->NextSelect;
   }
   return returnvalue;
}

BOOL vanillakey (UWORD code)
{
 BOOL returnvalue = TRUE;

   switch (code)
   {
    case 'J':
    case 'j':
      returnvalue = jumpFunc ();
      break;
    case 'E':
    case 'e':
      enable = !enable;
      ActivateCxObj (broker, enable);
      setmenu ();
    case 'F':
    case 'f':
      fast = !fast;
      setmenu ();
      break;
    case 'C':
    case 'c':
      coords = !coords;
      returnvalue = reopenwin ();
      break;
    case 'R':
    case 'r':
      crosshair = !crosshair;
      setmenu ();
      break;
    case 'O':
    case 'o':
      originX = scr->MouseX;
      originY = scr->MouseY;
      break;
    case 'K':
    case 'k':
      originX = originY = 0;
      break;
    case 'D':
    case 'd':
      fixed = !fixed;
      setmenu ();
      break;
    case '?':
    case 'A':
    case 'a':
     	returnvalue = AboutFunc ();
      break;
    case 'I':
    case 'i':
    	hideonclose = !hideonclose;
    	setmenu ();
    	break;
    case 'H':
    case 'h':
    	returnvalue = hideFunc ();
    	break;
    case 'Q':
    case 'q':
    case 27:  // ESC
      returnvalue = FALSE;
      break;
    case '+':
      if (scalefac < maxscalefac)
      {
         scalefac++;
         returnvalue = setpropgad ();
      }
      break;
    case '-':
      if (scalefac > 1)
      {
         scalefac--;
         returnvalue = setpropgad ();
      }
      break;
   }
   return returnvalue;
}

void rawkey (UWORD code, UWORD qualifier)
{
 BOOL returnvalue = TRUE;

   switch (code)
   {
    // up
    case 76:
     	if (qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
      {
         fixedY -= sourceheight - 1;
         if (fixedY < 0) fixedY = 0;
      }
      else if (qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
      {
         fixedY = 0;
      }
     	else if (fixedY) fixedY--;
      break;
    // down
    case 77:
     	if (qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
      {
         fixedY += sourceheight - 1;
         if (fixedY > (scr->Height - 1)) fixedY = scr->Height - 1;
      }
      else if (qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
      {
         fixedY = scr->Height - 1;
      }
        	else if (fixedY < (scr->Height - 1)) fixedY++;
         break;
    // right
    case 78:
     	if (qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
      {
         fixedX += sourcewidth - 1;
         if (fixedX > (scr->Width - 1)) fixedX = scr->Width - 1;
      }
      else if (qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
      {
         fixedX = scr->Width - 1;
      }
     	else if (fixedX < (scr->Width - 1)) fixedX++;
      break;
    // left
    case 79:
     	if (qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
      {
         fixedX -= sourcewidth - 1;
         if (fixedX < 0) fixedX = 0;
      }
      else if (qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
      {
         fixedX = 0;
      }
     	else if (fixedX) fixedX--;
      break;
   }
}

void processmsg ()
{
 struct IntuiMessage *intuimsg = NULL, m;
 CxMsg *msg;
 BOOL cont = TRUE, fixedmove = FALSE;
 LONG sigmask, msgid, msgtype;;
 ULONG lock;

   waitmask |= (1 << timesig) | (1 << showhidesig) | cxsigflag | SIGBREAKF_CTRL_C;
   while (cont)
   {
      sigmask = Wait (waitmask);
      if (sigmask & cxsigflag)
      {
         while ((msg = (CxMsg *) GetMsg (broker_mp)) != 0)
         {
            msgid = CxMsgID (msg);
            msgtype = CxMsgType (msg);
            ReplyMsg ((struct Message *) msg);
            switch (msgtype)
            {
             case CXM_COMMAND:
               switch (msgid)
               {
                case CXCMD_APPEAR:
                	cont = showwindow ();
                	break;
                case CXCMD_DISAPPEAR:
                	hidewindow ();
                	break;
                case CXCMD_DISABLE:
                  ActivateCxObj (broker, 0);
                  enable = FALSE;
                  setmenu ();
                  break;
                case CXCMD_ENABLE:
                  ActivateCxObj (broker, 1);
                  enable = TRUE;
                  setmenu ();
                  break;
                case CXCMD_UNIQUE:
                case CXCMD_KILL:
                  cont = FALSE;
                  break;
               }
            }
         } // while (msg
      }
      if (sigmask & (1 << userport->mp_SigBit))
      {
         jump = FALSE;
         while ((intuimsg = (struct IntuiMessage *) GetMsg (userport)) != 0)
         {
            CopyMem ((char *) intuimsg, (char *) &m, (long) sizeof (struct IntuiMessage));
            ReplyMsg ((struct Message *) intuimsg);
            switch (m.Class)
            {
             case IDCMP_CLOSEWINDOW:
            	if (hideonclose)
					{
						hidewindow ();
					}
               else cont = FALSE;
               jump = TRUE;
               break;
             case IDCMP_NEWSIZE:
               if (allocbm ()) cont = FALSE;
               break;
             case IDCMP_IDCMPUPDATE:
               ULONG h;
               GetAttr (PGA_Top, propgadget, &h);
               if (++h != scalefac)
               {
                  scalefac = h;
                  if (allocbm ()) cont = FALSE;
                  setwintitle ();
               }
               break;
             case IDCMP_MENUPICK:
             	cont = menupick (m.Code);
               break;
             case IDCMP_ACTIVEWINDOW:
               SetAPen (winrp, pens[0]);
               SetBPen (winrp, pens[1]);
               ClearMenuStrip (mywin);
               freescreensmenu (ItemAddress (menu, FULLMENUNUM (0, 1, 0)));
               if (makescreensmenu ()) SetMenuStrip (mywin, menu);
               else cont = FALSE;
               break;
             case IDCMP_INACTIVEWINDOW:
               SetAPen (winrp, pens[2]);
               SetBPen (winrp, pens[3]);
               break;
             case IDCMP_MOUSEBUTTONS:
               if (fixed)
               {
                  if (m.Code == SELECTDOWN) fixedmove = TRUE;
                  else fixedmove = FALSE;
               }
               break;
             case IDCMP_MOUSEMOVE:
               if (fixedmove)
               {
                  fixedX = scr->MouseX;
                  fixedY = scr->MouseY;
               }
               break;
             case IDCMP_VANILLAKEY:
             	cont = vanillakey (m.Code);
               break;
             case IDCMP_RAWKEY:
             	rawkey (m.Code, m.Qualifier);
             	break;
            }
            if (jump) break;
         } // while (intuimsg
      }
      if ((sigmask & (1 << showhidesig)) && cont)
      {
   		if (mywin) hidewindow ();
   		else showwindow ();
      }
      if ((sigmask & (1 << timesig)) && cont)
      {
         if (mywin)
         {
	         // refresh only if we living on the active screen
	         lock = LockIBase (0);
   	      tmpscr = IntuitionBase->ActiveScreen;
      	   UnlockIBase (lock);
         	if (scr == tmpscr) refresh ();
         }
      }
      if (sigmask & SIGBREAKF_CTRL_C)
      {
         cont = FALSE;
      }
   }
}

BOOL checkmenuitem (BOOL &value)
{
   if (!value && (item->Flags & CHECKED)) value = TRUE;
   else if (value && !(item->Flags & CHECKED)) value = FALSE;
   return TRUE;
}

BOOL fixedFunc ()
{
   return checkmenuitem (fixed);
}

BOOL fastFunc ()
{
   return checkmenuitem (fast);
}

BOOL crosshairFunc ()
{
   return checkmenuitem (crosshair);
}

BOOL AboutFunc ()
{
   EasyRequestArgs (NULL, &es_about, NULL, era_about);
   return TRUE;
}

BOOL jumpFunc ()
{
 int returnvalue;

   if (!(returnvalue = setupscreen (NULL)))
   {
      return reopenwin ();
   }
   else if (returnvalue == 1) return TRUE;      // no other screen
   return FALSE;
}

BOOL coordsFunc ()
{
   if (!coords && (item->Flags & CHECKED)) coords = TRUE;
   else if (coords && !(item->Flags & CHECKED)) coords = FALSE;
   else return TRUE;  // nothing changed
   return reopenwin ();
}

BOOL enableFunc ()
{
   if (item->Flags & CHECKED)
   {
      ActivateCxObj (broker, 1);
      enable = TRUE;
   }
   else if (!(item->Flags & CHECKED))
   {
      ActivateCxObj (broker, 0);
      enable = FALSE;
   }
   return TRUE;
}

BOOL hideoncloseFunc ()
{
	return checkmenuitem (hideonclose);
}

BOOL hideFunc ()
{
  	hidewindow ();
  	jump = TRUE;
	return TRUE;
}

BOOL QuitFunc ()
{
   return FALSE;
}

BOOL argbool (char **argv, char *name, BOOL def)
{
	char *value = (char *) FindToolType ((UBYTE **) argv, name);
   if (value)
   {
      if ((MatchToolValue (value, "YES")) || (MatchToolValue (value, "ON"))) return TRUE;
      if ((MatchToolValue (value, "OFF")) || (MatchToolValue (value, "NO"))) return FALSE;
   }
   return def;
}

LONG argint (char **argv, UBYTE *name, LONG def)
{
 LONG returnvalue;

	APTR value = FindToolType ((UBYTE **) argv, name);
	if (value)
	{
      if (StrToLong ((UBYTE *) value, &returnvalue))
      {
      	return returnvalue;
      }
   }
   return def;
}

void argstring (char *result, char **argv, char *name)
{
	char *value = FindToolType ((UBYTE **) argv, name);
   if (value)
   {
   	strncpy (result, value, 50);
   }
}

void readargs ()
{
 // first time always the tooltypes will be evaluated
 // if we started from Cli the tooltypes can be overloaded
 char programname[32];
 char **argv;
 struct DiskObject *programicon;
 struct RDArgs * rdargs;
 struct DrawInfo *drawinfo;
 LONG a[16];
 long *args = a;

   wintop = scr->RastPort.TxHeight + (UWORD) scr->WBorTop + 1;
   // make window as square as possible
   drawinfo = GetScreenDrawInfo (scr);
   if (drawinfo)
   {
      innerheight = innerwidth * drawinfo->dri_Resolution.X / drawinfo->dri_Resolution.Y;
      FreeScreenDrawInfo (scr, drawinfo);
   }
   // a Maxon C++ flag (wbstartup.h)
   if (!Cli ()) strcpy (programname, thistask->tc_Node.ln_Name);
   else GetProgramName (programname, 32);
   IconBase = OpenLibrary ("icon.library", 37);
   if (IconBase)
   {
   	programicon = GetDiskObject (programname);
      if (programicon)
      {
         argv = programicon->do_ToolTypes;
         newbroker.nb_Pri = argint (argv, "CX_Priority", newbroker.nb_Pri);
         fast = argbool (argv, "Fast", fast);
         maxscalefac = argint (argv, "MaxScaleFactor", maxscalefac);
         scalefac = argint (argv, "ScaleFactor", scalefac);
         winleft = argint (argv, "WinLeft", winleft);
         wintop = argint (argv, "WinTop", wintop);
         innerheight = argint (argv, "WinHeight", innerheight);
         innerwidth = argint (argv, "WinWidth", innerwidth);
         coords = argbool (argv, "Coordinates", coords);
         crosshair = argbool (argv, "Crosshair", crosshair);
         argstring (setoriginkey, argv, "SetOriginKey");
         argstring (resetoriginkey, argv, "ResetOriginKey");
         fixed = argbool (argv, "Fixed", fixed);
         argstring (showhidekey, argv, "ShowHideKey");
         hide = argbool (argv, "Hide", hide);
         hideonclose = argbool (argv, "HideOnClose", hideonclose);
         FreeDiskObject (programicon);
      }
      CloseLibrary (IconBase);
   }
   if (Cli ())
   {
      memset (args, '\0', 16 * sizeof (LONG));
      args[1] = fast;
      args[8] = coords;
      args[9] = crosshair;
      args[12] = fixed;
      args[14] = hide;
      args[15] = hideonclose;
      rdargs = ReadArgs (Template, args, NULL);
      if (rdargs)
      {
         if (*args) newbroker.nb_Pri = *args;
         args++;
         fast = *args++;
         if (*args) maxscalefac = *(LONG *) *args;
         if (*++args) scalefac = *(LONG *) *args;
         if (*++args) winleft = *(LONG *) *args;
         if (*++args) wintop = *(LONG *) *args;
         if (*++args) innerwidth = *(LONG *) *args;
         if (*++args) innerheight = *(LONG *) *args;
         args++;
         coords = *args++;
         crosshair = *args++;
         if (*args) strncpy (setoriginkey, (char *) *args, 50);
         if (*++args) strncpy (resetoriginkey, (char *) *args, 50);
         args++;
         fixed = *args++;
         if (*args) strncpy (showhidekey, (char *) *args, 50);
         args++;
         hide = *args++;
         hideonclose = *args;
         FreeArgs (rdargs);
      }
   }
   if (maxscalefac < 5) maxscalefac = 5;
   // not lower than innerheight or innerwidth
   // otherwise 'innerheight / scalefac' or 'innerwidth / scalefac'
   // can go to 0 -> 'AllocBitMap ()' failed
   if (maxscalefac > MINHEIGHT) maxscalefac = MINHEIGHT;
   if (scalefac > maxscalefac) scalefac = maxscalefac;
   if (innerheight < MINHEIGHT) innerheight = MINHEIGHT;
   if (innerwidth < MINWIDTH) innerwidth = MINWIDTH;
}

void main ()
{
   if ((CxBase = OpenLibrary ("commodities.library", 37)) != 0)
   {
      if ((GfxBase = OpenLibrary ("graphics.library", 39)) != 0)
      {
         if ((GadToolsBase = OpenLibrary ("gadtools.library", 37)) != 0)
         {
            CyberGfxBase = OpenLibrary ("cybergraphics.library", 40);
            InitRastPort (&destrp);
            SetDrMd (&destrp, COMPLEMENT);
            thistask = FindTask (NULL);
            if ((topazfont = OpenFont (&topaz_attr)) != 0)
            {
               if (!setupscreen (NULL))
               {
                  readargs ();
                  getoffsets ();
                  try
                  {
	                  if (!hide)
   	               {
	   	               if (!openwin ()) throw 1;
      		            if (allocbm ()) throw 2;
      		         }
           	         if ((timesig = AllocSignal (-1L)) != -1)
                     {
                       	if ((showhidesig = AllocSignal (-1L)) != -1)
                       	{
                           if (initbroker ())
  	                        {
     	                        processmsg ();
        	                     DeleteCxObjAll (broker);
           	                  DeleteMsgPort (broker_mp);
              	            }
              	            FreeSignal (showhidesig);
              	         }
                        FreeSignal (timesig);
                     }
                     else error ("Can't allocate signal");
                  }
                  catch (int) { }
                  closewin ();
                  CloseDownScreen ();
               }
               CloseFont (topazfont);
               if (screenfont) CloseFont (screenfont);
            }
            else error ("Can't open Topas.font");
            if (CyberGfxBase) CloseLibrary (CyberGfxBase);
            CloseLibrary (GadToolsBase);
         }
         else error ("can't open '%s' version %ld or higher.", "gadtools.library", 37L);
         CloseLibrary (GfxBase);
      }
      else error ("can't open '%s' version %ld or higher.", "graphics.library", 39L);
      CloseLibrary (CxBase);
   }
   else error ("can't open '%s' version %ld or higher.", "commodities.library", 37L);
}
