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

#include <stdio.h>
#include <string.h>
#include <stream.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, 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;
struct RastPort *scrrp, destrp, *winrp;
struct Library *CxBase, *GfxBase, *GadToolsBase, *CyberGfxBase, *IconBase;
BYTE timesig;
BOOL hires, newlook, jump = FALSE, fast = FALSE, pubscreenlocked = FALSE, enable = TRUE;
BOOL cybergfxscreen, coords = FALSE, crosshair = FALSE, origin = TRUE, fixed = FALSE, jumpdirect;
CxObj *broker, *customcxobj;
Object *propgadget;
char pubscreenname[MAXPUBSCREENNAME];
char coordstring[12], setoriginkey[50] = "lalt o", resetoriginkey[50] = "lalt r";
char title[10];
char *Template = "CX_PRIORITY=P/N/K,FAST=F/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";

extern struct IntuitionBase *IntuitionBase;

// functions declaration for menu
BOOL jumpFunc ();
BOOL fastFunc ();
BOOL AboutFunc ();
BOOL enableFunc ();
BOOL coordsFunc ();
BOOL crosshairFunc ();
BOOL fixedFunc ();
BOOL QuitFunc ();

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

char *era_about[] = {
   "Lupe V1.4 (" __DATE__ ")\n\n"
   "written by:\n"
   "  Frank Toepper\n"
   "  Maxim Gorki Strasse 5A\n"
   "  Greifswald\n"
   "  17491\n"
   "  GERMANY\n\n"
   "EMail:\n"
   "  toepper@rz.uni-greifswald.de\n\n"
   "This program is Public Domain", 
   "Ok"
};

struct NewBroker newbroker = {
   NB_VERSION,
   "Lupe",
   "Lupe V1.4 © 1995-96 Frank Toepper",
   "A magnifing glass program.",
   3,
   0,
   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;
 
   if (pubscreenlist = LockPubScreenList ())
   {
      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)
      {
         FreeVisualInfo (VisualInfo);
         if (pubscreenlocked)
         {
            UnlockPubScreen (NULL, scr);
         }
      }
      if (lockaspublicscreen (selectedscreen)) pubscreenlocked = TRUE;
      else pubscreenlocked = FALSE;
      scr = selectedscreen;
   }
   else if (scr)
   {
      if (tmpscr = scr->NextScreen)
      {
         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))
   {
      pubscreenlocked = TRUE;
   }
   else
   {
      error ("Can't lock publicscreen.");
      return 2;
   }
   if (!(VisualInfo = GetVisualInfoA (scr, NULL)))
   {
      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);
      scr = NULL;
   }
}

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);
   if (drawinfo = GetScreenDrawInfo (scr))
   {
      if (sizeobject = NewObject (NULL, SYSICLASS,
       SYSIA_Which, SIZEIMAGE,
       SYSIA_DrawInfo, drawinfo,
       SYSIA_Size, hires ? SYSISIZE_MEDRES : SYSISIZE_LOWRES,
       TAG_END))
      {
         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)
      {
         if (screenfont = OpenFont (scr->Font))
         {
            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)
      {
         tmpmenu[0].nm_Label = tmpscr->DefaultTitle;
         if (pubscreenlist = LockPubScreenList ())
         {
            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
         if (*itempointer = (struct MenuItem *) CreateMenusA (tmpmenu, NULL))
         {
            itempointer = &(*itempointer)->NextItem;
         }
         else error ("Can't create a screensmenu item.");
      }
   }
   else
   {
      tmpmenu[0].nm_Label = "No Other Screen";
      if (*itempointer = (struct MenuItem *) CreateMenusA (tmpmenu, NULL))
      {
         (*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, "Quit",           "Q", 0, 0, QuitFunc },
   {  NM_END, 0, 0, 0, 0, 0 }
 };
 
   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);
   }
   if (menu = CreateMenusA (standartmenu, NULL))
   {
      if (makescreensmenu ())
      {
         if (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 (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))
            {
               // 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]);
               return TRUE;
            }
            else error ("Can't open window.");
         }
         else error ("Can't create propgadget.");
      }
   }
   else error ("Can't create menu.");
   return FALSE;
}

void closewin ()
{
   if (menu) ClearMenuStrip (mywin);
   CloseWindow (mywin);
   if (menu)
   {
      freescreensmenu (ItemAddress (menu, FULLMENUNUM (0, 1, 0)));
      FreeMenus (menu);
      menu = NULL;
   }
   DisposeObject (propgadget);
   if (srcbm) FreeBitMap (srcbm);
   if (destbm) FreeBitMap (destbm);
   if (cyberdata) FreeVec (cyberdata);
   srcbm = NULL;
   destbm = NULL;
   cyberdata = NULL;
}

void refresh ()
{
 WORD x, y, xoff = 0, yoff = 0;
 
   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
   {
      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 ((ie->ie_Class == IECLASS_TIMER) || fast)
      Signal (thistask, 1 << timesig);
}

BOOL initbroker ()
{
 LONG errorcode;
 
   if (broker_mp = CreateMsgPort ())
   {
      newbroker.nb_Port = broker_mp;
      cxsigflag = 1 << broker_mp->mp_SigBit;
      if (broker = CxBroker (&newbroker, &errorcode))
      {
         if (customcxobj = CxCustom (CxFunction, 0))
         {
            if (!CxObjError (customcxobj))
            {
               AttachCxObj (broker, customcxobj);
               if (ParseIX (setoriginkey, &setoriginix) || ParseIX (resetoriginkey, &resetoriginix))
               {
                  origin = FALSE;
                  error ("Can't init Hotkeys.\n'Set Origin' disabled.");
               }
               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;
}

// enable-flag only
void setmenu ()
{
 struct MenuItem *mitem;
 
   ClearMenuStrip (mywin);
   mitem = ItemAddress (menu, FULLMENUNUM (0, 3, NOSUB));
   if (enable) mitem->Flags |= CHECKED;
      else mitem->Flags &= ~CHECKED;
   ResetMenuStrip (mywin, menu);
}

BOOL reopenwin ()
{
   waitmask &= ~(1 << userport->mp_SigBit);
   wintop = mywin->TopEdge;
   winleft = mywin->LeftEdge;
   closewin ();
   getoffsets ();
   if (!openwin ()) return FALSE;
   waitmask |= 1 << userport->mp_SigBit;
   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;
}

void processmsg ()
{
 struct IntuiMessage *intuimsg = NULL, m;
 CxMsg *msg;
 BOOL cont = TRUE, fixedmove = FALSE;
 LONG sigmask, msgid, msgtype;;
 UWORD code;
 ULONG lock;
 static BOOL (*func)();
 
   waitmask = (1 << userport->mp_SigBit) | (1 << timesig) | cxsigflag | SIGBREAKF_CTRL_C;
   while (cont)
   {
      sigmask = Wait (waitmask);
      if (sigmask & cxsigflag)
      {
         while (msg = (CxMsg *) GetMsg (broker_mp))
         {
            msgid = CxMsgID (msg);
            msgtype = CxMsgType (msg);
            ReplyMsg ((struct Message *) msg);
            switch (msgtype)
            {
             case CXM_COMMAND:
               switch (msgid)
               {
                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;
               }
            }
         }
      }
      if (sigmask & (1 << userport->mp_SigBit))
      {
         jump = FALSE;
         while (intuimsg = (struct IntuiMessage *) GetMsg (userport))
         {
            CopyMem ((char *) intuimsg, (char *) &m, (long) sizeof (struct IntuiMessage));
            ReplyMsg ((struct Message *) intuimsg);
            switch (m.Class)
            {
             case IDCMP_CLOSEWINDOW:
               cont = FALSE;
               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:
               code = m.Code;
               while ((code != MENUNULL) && cont && !jump)
               {
                  item = ItemAddress (menu, code);
                  if (SUBNUM (code) != NOSUB)
                  {
                     if (GTMENUITEM_USERDATA (item))  // 'no other screen' - submenu
                     {
                        try
                        {
                           if (setupscreen ((struct Screen *) GTMENUITEM_USERDATA (item))) throw 1;
                           waitmask &= ~(1 << userport->mp_SigBit);
                           wintop = mywin->TopEdge;
                           winleft = mywin->LeftEdge;
                           closewin ();
                           getoffsets ();
                           if (!openwin ()) throw 2;
                           waitmask |= 1 << userport->mp_SigBit;
                           if (allocbm ()) throw 3;
                        }
                        catch (int)
                        {
                           cont = FALSE;
                        }
                        jump = TRUE;
                     }
                  }
                  else
                  {
                     func = (BOOL (*)()) GTMENUITEM_USERDATA (item);
                     cont = func ();
                  }
                  if (!jump) code = item->NextSelect;
               }
               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:
               switch (m.Code)
               {
                case 'J':
                case 'j':
                  cont = jumpFunc ();
                  break;
                case 'E':
                case 'e':
                  enable = !enable;
                  ActivateCxObj (broker, enable);
                  setmenu ();
                case 'F':
                case 'f':
                  fast = !fast;
                  break;
                case 'C':
                case 'c':
                  coords = !coords;
                  cont = reopenwin ();
                  break;
                case 'R':
                case 'r':
                  crosshair = !crosshair;
                  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;
                  break;
                case '?':
                case 'A':
                case 'a':
                	AboutFunc ();
                  break;
                case 'Q':
                case 'q':
                case 27:
                  cont = FALSE;
                  break;
                case '+':
                  if (scalefac < maxscalefac)
                  {
                     scalefac++;
                     cont = setpropgad ();
                  }
                  break;
                case '-':
                  if (scalefac > 1)
                  {
                     scalefac--;
                     cont = setpropgad ();
                  }
                  break;
               }
               break;
             case IDCMP_RAWKEY:
               switch (m.Code)
               {
                // up
                case 76:
                	if (m.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
                  {
                     fixedY -= sourceheight - 1;
                     if (fixedY < 0) fixedY = 0;
                  }
                  else if (m.Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
                  {
                     fixedY = 0;
                  }
                	else if (fixedY) fixedY--;
                  break;
                // down
                case 77:
                	if (m.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
                  {
                     fixedY += sourceheight - 1;
                     if (fixedY > (scr->Height - 1)) fixedY = scr->Height - 1;
                  }
                  else if (m.Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
                  {
                     fixedY = scr->Height - 1;
                  }
                	else if (fixedY < (scr->Height - 1)) fixedY++;
                  break;
                // right
                case 78:
                	if (m.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
                  {
                     fixedX += sourcewidth - 1;
                     if (fixedX > (scr->Width - 1)) fixedX = scr->Width - 1;
                  }
                  else if (m.Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
                  {
                     fixedX = scr->Width - 1;
                  }
                	else if (fixedX < (scr->Width - 1)) fixedX++;
                  break;
                // left
                case 79:
                	if (m.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
                  {
                     fixedX -= sourcewidth - 1;
                     if (fixedX < 0) fixedX = 0;
                  }
                  else if (m.Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
                  {
                     fixedX = 0;
                  }
                	else if (fixedX) fixedX--;
                  break;
               }
            }
         }
      }
      if ((sigmask & (1 << timesig)) && cont)
      {
         // 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 QuitFunc ()
{
   return FALSE;
}

BOOL argbool (char **argv, char *name, BOOL def)
{
char *value;
   
   if (value = (char *) FindToolType ((UBYTE **) argv, name))
   {
      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)
{
APTR value;
LONG returnvalue;

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

void argstring (char *result, char **argv, char *name)
{
 char *value;
   
   if (value = FindToolType ((UBYTE **) argv, name))
      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;
 LONG args[13];
 struct DrawInfo *drawinfo;
 
   wintop = scr->RastPort.TxHeight + (UWORD) scr->WBorTop + 1; 
   // make window as square as possible
   if (drawinfo = GetScreenDrawInfo (scr))
   {
      innerheight = innerwidth * drawinfo->dri_Resolution.X / drawinfo->dri_Resolution.Y;
      FreeScreenDrawInfo (scr, drawinfo);
   }
   // a Maxon C++ flag (wbstartup.h)
   if (_wbflag) strcpy (programname, thistask->tc_Node.ln_Name);
   else GetProgramName (programname, 32);
   if (IconBase = OpenLibrary ("icon.library", 37))
   {
      if (programicon = GetDiskObject (programname))
      {
         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");
         argbool (argv, "Fixed", fixed);
         FreeDiskObject (programicon);
      }
      CloseLibrary (IconBase);
   }
   if (Cli ())
   {
      memset (args, '\0', sizeof (args));
      args[1] = fast;
      args[8] = coords;
      args[9] = crosshair;
      args[12] = fixed;
      if (rdargs = ReadArgs (Template, args, NULL))
      {
         if (args[0]) newbroker.nb_Pri = *(LONG *) args[0];
         fast = args[1];
         if (args[2]) maxscalefac = *(LONG *) args[2];
         if (args[3]) scalefac = *(LONG *) args[3];
         if (args[4]) winleft = *(LONG *) args[4];
         if (args[5]) wintop = *(LONG *) args[5];
         if (args[6]) innerwidth = *(LONG *) args[6];
         if (args[7]) innerheight = *(LONG *) args[7];
         coords = args[8];
         crosshair = args[9];
         if (args[10]) strcpy (setoriginkey, (char *) args[10]);
         if (args[11]) strcpy (resetoriginkey, (char *) args[11]);
         fixed = args[12];
         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))
   {
      if (GfxBase = OpenLibrary ("graphics.library", 39))
      {
         if (GadToolsBase = OpenLibrary ("gadtools.library", 37))
         {
            CyberGfxBase = OpenLibrary ("cybergraphics.library", 40);
            InitRastPort (&destrp);
            SetDrMd (&destrp, COMPLEMENT);
            thistask = FindTask (NULL);
            if (topazfont = OpenFont (&topaz_attr))
            {
               if (!setupscreen (NULL))
               {
                  readargs ();
                  getoffsets ();
                  if (openwin ())
                  {
                     if (!allocbm ())
                     {
                        if ((timesig = AllocSignal (-1L)) != -1)
                        {
                           if (initbroker ())
                           {
                              processmsg ();
                              DeleteCxObjAll (broker);
                              DeleteMsgPort (broker_mp);
                           }
                           FreeSignal (timesig);
                        }
                        else error ("Can't allocate signal");
                     } 
                     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);
}

