// Lupe V1.8 - 1995-97 - © Frank Toepper
// a lens
// AmigaOS 3.0+
// Maxon C++
// last changes: 11.Apr.97

#include <string.h>
#include <iostream.h>
#include <ctype.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 <datatypes/pictureclass.h>
#include <graphics/displayinfo.h>
#include <graphics/gfxmacros.h>
#include <graphics/gfxbase.h>
#include <cybergraphics/cybergraphics.h>
#include <clib/dos_protos.h>
#include <pragma/dos_lib.h>
#include <clib/intuition_protos.h>
#include <pragma/intuition_lib.h>
#include <clib/graphics_protos.h>
#include <pragma/graphics_lib.h>
#include <clib/exec_protos.h>
#include <pragma/exec_lib.h>
#include <clib/commodities_protos.h>
#include <pragma/commodities_lib.h>
#include <clib/gadtools_protos.h>
#include <pragma/gadtools_lib.h>
#include <clib/icon_protos.h>
#include <pragma/icon_lib.h>
#include <clib/cybergraphics_protos.h>
#include <pragma/cybergraphics_lib.h>
#include <clib/wb_protos.h>
#include <pragma/wb_lib.h>
#include <libraries/asl.h>
#include <clib/asl_protos.h>
#include <pragma/asl_lib.h>
#include <clib/alib_protos.h>
#include <pragma/utility_lib.h>
#include <clib/utility_protos.h>
#include <wbstartup.h>

#pragma header

#include "lupe_cat.h"

#define VERSION "Lupe V1.8"

#define CYBERARRAY_BPP 3

#define MINWIDTH 56
#define MINHEIGHT 30

enum { SAMESCREEN, NEWSCREEN };

UWORD innerwidth = 150, innerheight = 100, sourcewidth, sourceheight;
UWORD scalefac = 5, maxscalefac = 15, minwidth;
UWORD leftoff, topoff, bottomoff, rightoff;
UWORD sizeiw, sizeih, winx, winy, winleft = 0, wintop = 11, originX = 0, originY = 0;
UWORD textoff, coordsstringlen, fontX, fontY, scrdepth;
WORD fixedX = 0, fixedY = 0;
ULONG cxsigflag, waitmask = 0, pens[6];
LONG oldx, oldy;
APTR VisualInfo = NULL;
BPTR fh;
struct MsgPort *broker_mp, *userport, *app_mp = NULL;
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, *maskbm = NULL;
struct TextFont *screenfont = NULL, *topazfont;
struct InputXpression setoriginix, resetoriginix, showhideix;
struct RastPort *scrrp, destrp, *winrp, maskrp;
struct Library *CxBase, *GadToolsBase, *CyberGfxBase, *IconBase;
struct Library *WorkbenchBase, *AslBase, *LocaleBase, *UtilityBase;
struct GfxBase *GfxBase;
struct AppIcon *appicon = NULL;
struct AppMessage *appmsg;
struct DiskObject *icon = NULL;
UBYTE timesig, showhidesig;
BOOL hires, newlook, jump = FALSE, fast = FALSE, pubscreenlocked = FALSE, enable = TRUE;
BOOL cybergfxscreen, coords = FALSE, crosshair = FALSE, origin = TRUE, fixed = FALSE;
BOOL jumpdirect, showhide = TRUE, hide = FALSE, hideonclose = FALSE, grid = FALSE;
BOOL showrgb = FALSE, jumptoactivescreen = FALSE, screentofront = TRUE;
CxObj *broker, *customcxobj;
Object *propgadget;
char pubscreenname[MAXPUBSCREENNAME];
char setoriginkey[50] = "lalt o", resetoriginkey[50] = "lalt r", showhidekey[50] = "lalt h";
char title[10], dispstring[12], language[20] = "";
char programname[110], iffpath[256] = "ram:Lupe.iff", iffname[110];
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, SHOWHIDEKEY=SH/K, ICONIFY=I/S/K, ICONIFYONCLOSE=IOC/S/K, "
                 "GRID=G/S/K, SHOWRGB=RGB/S/K, IFFFILE=IF/K, JUMPTOACTIVESCREEN=JAS/S/K, "
                 "LANGUAGE=U";

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
};

struct NewBroker newbroker = {
   NB_VERSION,
   "Lupe",
   VERSION " © 1995-97 Frank Toepper",
   0,
   3,
   COF_SHOW_HIDE,
   0,
   NULL,
   0
};

extern struct IntuitionBase *IntuitionBase;

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

//----------------------------------------------

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,
    GetString (TXT_ERROR),
    formatstring,
    GetString (TXT_OK)
 };

   if (Cli ())
   {
      VPrintf (formatstring, (LONG *) 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 (GetString (TXT_ERROR_NOOTHERSCREEN));
         return SAMESCREEN;
      }
      scr = tmpscr;
   }
   else if ((scr = LockPubScreen (NULL)) != 0)
   {
      pubscreenlocked = TRUE;
   }
   else
   {
      error (GetString (TXT_ERROR_LOCKPUBSCREEN));
      throw 1;
   }
   VisualInfo = GetVisualInfoA (scr, NULL);
   if (!VisualInfo)
   {
      error (GetString (TXT_ERROR_VISUALINFO));
      throw 2;
   }
   cybergfxscreen = FALSE;
   if (CyberGfxBase)
      if ((id = GetVPModeID (&scr->ViewPort)) != INVALID_ID)
         if (IsCyberModeID (id))
            if (GetCyberIDAttr (CYBRIDATTR_BPPIX, id) > 1)
               cybergfxscreen = TRUE;
   return NEWSCREEN;
}

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);
   SetAPen (&maskrp, 1);
   drawinfo = GetScreenDrawInfo (scr);
   if (drawinfo)
   {
      // dimension of the border
      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);
         SetAPen (&maskrp, drawinfo->dri_Pens[SHADOWPEN]);
         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];
      pens[4] = drawinfo->dri_Pens[SHINEPEN];
      pens[5] = drawinfo->dri_Pens[SHADOWPEN];
      FreeScreenDrawInfo (scr, drawinfo);
   }
   else
   {
      // default pens
      pens[0] = 2;
      pens[1] = 3;
      pens[2] = 1;
      pens[3] = 0;
      pens[4] = 2;
      pens[5] = 1;
   }  // if (drawinfo)
   scrrp = &scr->RastPort;
   topoff = scrrp->TxHeight + (UWORD) scr->WBorTop + 1;
   leftoff = scr->WBorLeft;
   scrbm = scrrp->BitMap;
   scrdepth = GetBitMapAttr (scrbm, BMA_DEPTH);
   fontX = topazfont->tf_XSize;
   fontY = topazfont->tf_YSize;
   minwidth = MINWIDTH;
   coordsstringlen = 1;
   if (scr->Width > 999)
   {
      coordsstringlen += 5;
   }
   else
   {
      coordsstringlen += 4;
   }
   if (scr->Height > 999)
   {
      coordsstringlen += 5;
   }
   else
   {
      coordsstringlen += 4;
   }
   if (coords || showrgb)
   {
      bottomoff = sizeih;
      // center the text in the bottomborder
      textoff = (bottomoff + fontY >> 1) - topazfont->tf_Baseline;
      minwidth = 0;
      if (coords)
      {
         minwidth = coordsstringlen * (fontX);
      }
      if (showrgb)
      {
         minwidth += 13 * (fontX);
      }
      // SysIHack or height of screenfont <= 8?
      if ((bottomoff - 2) >= scr->Font->ta_YSize)
      {
         screenfont = OpenFont (scr->Font);
         if (screenfont)
         {
            if (!(screenfont->tf_Flags & FPF_PROPORTIONAL))
            {
               fontX = screenfont->tf_XSize;
               fontY = screenfont->tf_YSize;
               minwidth = 0;
               if (coords)
               {
                  minwidth = coordsstringlen * (fontX);
               }
               if (showrgb)
               {
                  minwidth += 13 * (fontX);
               }
               // center the text in the bottomborder
               textoff = (bottomoff + fontY >> 1) - screenfont->tf_Baseline;
            }
            else
            {
               CloseFont (screenfont);
               screenfont = NULL;               
            }
         }
      }
   }  // if (coords
   else
   {
      bottomoff = scr->WBorBottom;
   }
}

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

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

void setmenu ()
{
 struct MenuItem *mitem;

   if (mywin)
   {
      ClearMenuStrip (mywin);

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

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

      mitem = mitem->NextItem;
      if (fixed) mitem->Flags |= CHECKED;
      else mitem->Flags &= ~CHECKED;

      mitem = mitem->NextItem;
      if (fast) mitem->Flags |= CHECKED;
      else mitem->Flags &= ~CHECKED;

      mitem = mitem->NextItem;
      if (coords) mitem->Flags |= CHECKED;
      else mitem->Flags &= ~CHECKED;

      mitem = mitem->NextItem;
      if (crosshair) mitem->Flags |= CHECKED;
      else mitem->Flags &= ~CHECKED;

      mitem = mitem->NextItem;
      if (grid) mitem->Flags |= CHECKED;
      else mitem->Flags &= ~CHECKED;

      mitem = mitem->NextItem;
      if (showrgb) mitem->Flags |= CHECKED;
      else mitem->Flags &= ~CHECKED;

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

      ResetMenuStrip (mywin, menu);
   }
}

void setupgrid ()
{
 LONG i, j;
 
   if (maskbm)
   {
      WaitBlit ();
      FreeBitMap (maskbm);
      maskbm = NULL;
   }
   if (grid)
   {
      maskbm = AllocBitMap (innerwidth, innerheight, 1, BMF_CLEAR, destbm);
      if (!maskbm)
      {
         error (GetString (TXT_ERROR_GRIDMASK));
         grid = FALSE;
         setmenu ();
      }
      else
      {
         maskrp.BitMap = maskbm;
         for (i = scalefac - 1; i < innerwidth; i += scalefac)
         {
            RectFill (&maskrp, i, 0, i, innerheight - 1)
         }
         for (i = scalefac - 1; i < innerheight; i += scalefac)
         {
            for (j = 0; j <= (innerwidth - scalefac); j += scalefac)
            {
               RectFill (&maskrp, j, i, j + scalefac - 2, i);
            }
         }
      }
   }
}

void allocbm ()
{
   if (srcbm)
   {
      FreeBitMap (srcbm);
      srcbm = NULL;
   }
   if (destbm)
   {
      FreeBitMap (destbm);
      destbm = 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 (!(srcbm = AllocBitMap (sourcewidth, sourceheight, cybergfxscreen ? 24 : scrdepth, cybergfxscreen ? (BMF_CLEAR | BMF_MINPLANES | PIXFMT_RGB24) : BMF_CLEAR, scrbm)))
      {
         error (GetString (TXT_ERROR_SOURCEBMP));
         throw 1;
      }
      if (!(destbm = AllocBitMap (innerwidth, innerheight, cybergfxscreen ? 24 : scrdepth, cybergfxscreen ? (BMF_CLEAR | BMF_MINPLANES | PIXFMT_RGB24) : BMF_CLEAR, srcbm)))
      {
         error (GetString (TXT_ERROR_DESTBMP));
         throw 2;
      }
      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;
      setupgrid ();
      destrp.BitMap = destbm;
   }
}

void makescreenmenu ()
{
 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;

   lock = LockIBase (0);
   tmpscr = IntuitionBase->FirstScreen;
   if (tmpscr->NextScreen)
   {
      while ((tmpscr = tmpscr->NextScreen) != 0)
      {
         (*tmpmenu).nm_Label = tmpscr->DefaultTitle;
         pubscreenlist = LockPubScreenList ();
         if (pubscreenlist)
         {
            pubscreennode = (struct PubScreenNode *) pubscreenlist->lh_Head;
            while (pubscreennode)
            {
               if (pubscreennode->psn_Screen == tmpscr)
               {
                  if (pubscreennode->psn_Node.ln_Name)
                  {
                     tmpmenu->nm_Label = pubscreennode->psn_Node.ln_Name;
                  }
                  break;
               }
               pubscreennode = (struct PubScreenNode *) pubscreennode->psn_Node.ln_Succ;
            }
            UnlockPubScreenList ();
         }
         if (!tmpmenu->nm_Label)
         {
            tmpmenu->nm_Label = GetString (TXT_UNNAMED);
         }
         tmpmenu->nm_UserData = tmpscr;
         // each item single created
         *itempointer = (struct MenuItem *) CreateMenusA (tmpmenu, NULL);
         if (*itempointer)
         {
            itempointer = &(*itempointer)->NextItem;
         }
         else
         {
            error (GetString (TXT_ERROR_SCREENMENUITEM));
         }
      }
   }
   else
   {
      tmpmenu->nm_Label = GetString (TXT_NOOTHERSCREEN);
      *itempointer = (struct MenuItem *) CreateMenusA (tmpmenu, NULL);
      if (*itempointer)
      {
         (*itempointer)->Flags |= HIGHNONE;
         itempointer = &(*itempointer)->NextItem;
      }
      else
      {
         error (GetString (TXT_ERROR_SCREENMENU));
      }
   }
   UnlockIBase (lock);
   if (!LayoutMenus (menu, VisualInfo, GTMN_NewLookMenus, TRUE, TAG_DONE))
   {
      error (GetString (TXT_ERROR_LAYOUTMENU));
      throw 1;
   }
}

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

void openwin ()
{ 
 struct NewMenu standardmenu[] = {
   { NM_TITLE, GetString (TXT_PROJECT), 0, 0, 0, 0 },
   {  NM_ITEM, GetString (TXT_JUMP), GetString (KEY_JUMP), 0, 0, jumpFunc },
   {  NM_ITEM, GetString (TXT_JUMPTOSCREEN), 0, 0, 0, 0 },
   {  NM_ITEM, GetString (TXT_JUMPTOACTIVESCREEN), GetString (KEY_JUMPTOACTIVESCREEN), CHECKIT | MENUTOGGLE, 0, jumptoactiveFunc },
   {  NM_ITEM, NM_BARLABEL, 0, 0, 0, 0 },
   {  NM_ITEM, GetString (TXT_SAVEIFF), GetString (KEY_SAVEIFF), 0, 0, saveFunc },
   {  NM_ITEM, NM_BARLABEL, 0, 0, 0, 0 },
   {  NM_ITEM, GetString (TXT_ENABLE), GetString (KEY_ENABLE), CHECKIT | MENUTOGGLE, 0, enableFunc },
   {  NM_ITEM, GetString (TXT_FIXED), GetString (KEY_FIXED), CHECKIT | MENUTOGGLE, 0, fixedFunc },
   {  NM_ITEM, GetString (TXT_FAST), GetString (KEY_FAST), CHECKIT | MENUTOGGLE, 0, fastFunc },
   {  NM_ITEM, GetString (TXT_COORDINATES), GetString (KEY_COORDINATES), CHECKIT | MENUTOGGLE, 0, coordsFunc },
   {  NM_ITEM, GetString (TXT_CROSSHAIR), GetString (KEY_CROSSHAIR), CHECKIT | MENUTOGGLE, 0, crosshairFunc },
   {  NM_ITEM, GetString (TXT_GRID), GetString (KEY_GRID), CHECKIT | MENUTOGGLE, 0, gridFunc },
   {  NM_ITEM, GetString (TXT_SHOWRGB), GetString (KEY_SHOWRGB), CHECKIT | MENUTOGGLE, 0, showrgbFunc },
   {  NM_ITEM, NM_BARLABEL, 0, 0, 0, 0 },
   {  NM_ITEM, GetString (TXT_ABOUT), GetString (KEY_ABOUT), 0, 0, AboutFunc },
   {  NM_ITEM, NM_BARLABEL, 0, 0, 0, 0 },
   {  NM_ITEM, GetString (TXT_ICONIFYONCLOSE), GetString (KEY_ICONIFYONCLOSE), CHECKIT | MENUTOGGLE, 0, hideoncloseFunc },
   {  NM_ITEM, GetString (TXT_ICONIFY), GetString (KEY_ICONIFY), 0, 0, hideFunc },
   {  NM_ITEM, GetString (TXT_QUIT), GetString (KEY_QUIT), 0, 0, QuitFunc },
   {  NM_END, 0, 0, 0, 0, 0 }
 };
 ULONG lock;
 WORD bw, rh;
 struct Rectangle zoom = {
    minwidth + leftoff + rightoff,
    MINHEIGHT + topoff + bottomoff + sizeih,
    9999, 9999
 };

   if (!mywin)
   {
      if (hires)
      {
         bw = rh = 2;
      }
      else
      {
         bw = rh = 1;
      }
      if (screentofront)
      {
         // 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);
            }
         }
      }
      else
      {
         // auto jump
         screentofront = TRUE;
      }
      menu = CreateMenusA (standardmenu, NULL);
      if (!menu)
      {
         error (GetString (TXT_ERROR_MENU));
         throw (1);
      }
      makescreenmenu ();
      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)
      {
         error (GetString (TXT_ERROR_PROPGADGET));
         throw (2);
      }
      if (innerwidth < minwidth)
      {
         innerwidth = minwidth;
      }
      mywin = OpenWindowTags (NULL,
        WA_Flags,          WFLG_ACTIVATE | WFLG_CLOSEGADGET | WFLG_DEPTHGADGET
                            | WFLG_DRAGBAR | WFLG_SIZEGADGET | WFLG_SIMPLE_REFRESH
                            | WFLG_NEWLOOKMENUS | WFLG_REPORTMOUSE | WFLG_SIZEBRIGHT
                            | ((coords || showrgb) ? WFLG_SIZEBBOTTOM : 0),
        WA_AutoAdjust,     1,
        WA_MaxHeight,      9999,
        WA_MaxWidth,       9999,
        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
                            | IDCMP_REFRESHWINDOW,
        WA_InnerHeight,    innerheight,
        WA_InnerWidth,     innerwidth,
        WA_Left,           winleft,
        WA_Gadgets,        propgadget,
        WA_Top,            wintop,
        pubscreenlocked ? WA_PubScreen : WA_CustomScreen, scr,
        WA_Zoom,           &zoom,
        TAG_DONE);
      if (!mywin)
      {
         error (GetString (TXT_ERROR_WINDOW));
         throw (3);
      }
      // shit patches
      leftoff = mywin->BorderLeft;
      bottomoff = mywin->BorderBottom;
      SetMenuStrip (mywin, menu);
      setmenu ();
      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;
   }
}

void closewin ()
{
   if (mywin)
   {
      if (menu)
      {
         ClearMenuStrip (mywin);
      }
      wintop = mywin->TopEdge;
      winleft = mywin->LeftEdge;
      waitmask &= ~(1 << userport->mp_SigBit);
      CloseWindow (mywin);
      mywin = NULL;
      userport = NULL;
      jump = TRUE;
   }
   if (menu)
   {
      freescreenmenu (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 (maskbm)
   {
      FreeBitMap (maskbm);
      maskbm = NULL;
   }
}

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

void freeappicon ()
{
   if (appicon)
   {
      RemoveAppIcon (appicon);
      appicon = NULL;
   }
   if (app_mp)
   {
      waitmask &= ~(1 << app_mp->mp_SigBit);
      DeleteMsgPort (app_mp);
      app_mp = NULL;
   }
   if (icon)
   {
      FreeDiskObject (icon);
      icon = NULL;
   }
}

void showwindow ()
{
   if (!mywin)
   {
      if (!screen_is_still_open ())
      {
         scr = NULL;
      }
      setupscreen (scr);
      getoffsets ();
      openwin ();
      allocbm ();
      freeappicon ();
   }
}

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

   freeappicon ();

   if (Cli ())
   {
      GetProgramName (programname, 108);
   }
   else
   {
      strncpy (programname, thistask->tc_Node.ln_Name, 108);
   }
   icon = GetDiskObject ("ENV:sys/def_Lupe");
   if (!icon)
   {
      icon = GetDiskObject (programname);
      if (!icon)
      {
         icon = GetDefDiskObject (WBTOOL);
      }
   }
   if (icon)
   {
      app_mp = CreateMsgPort ();
      if (app_mp)
      {
         appicon = AddAppIcon (1, 0, "Lupe", app_mp, NULL, icon, NULL);
         if (appicon)
         {
            waitmask |= 1 << app_mp->mp_SigBit;
         }
      }
   }
}

//- refresh ------------------------------------

void printcoords (LONG x, LONG y)
{
   if (coords)
   {
    LONG args[] = {x - originX, y - originY};

      memset (dispstring, ' ', coordsstringlen);
      RawDoFmt ("%ld,%ld", args, (void (*)()) Putchar, dispstring);
      dispstring[strlen (dispstring)] = ' ';
      mywin->Flags &= ~SIZEGADGET;
      if ((winx == mywin->Width) && (winy == mywin->Height))
      {
         Move (winrp, leftoff, mywin->Height - textoff);
         Text (winrp, dispstring, coordsstringlen);
      }
      mywin->Flags |= SIZEGADGET;
   }
}

void printrgb (LONG x, LONG y)
{
 ULONG colorreg, colors[3], apen, textlen, cybercolor;
 
   if (showrgb)
   {
      colorreg = ReadPixel (scrrp, x, y);
      if (cybergfxscreen)
      {
         cybercolor = ReadRGBPixel (scrrp, x, y);
         colors[0] = (cybercolor >> 16) & 0xff;
         colors[1] = (cybercolor >> 8) & 0xff;
         colors[2] = cybercolor & 0xff;
      }
      else
      {
         GetRGB32 (scr->ViewPort.ColorMap, colorreg, 1, colors);
         colors[0] &= 0xff;
         colors[1] &= 0xff;
         colors[2] &= 0xff;
      }
      RawDoFmt ("%3ld,%3ld,%3ld", colors, (void (*)()) Putchar, dispstring);
      textlen = TextLength (winrp, dispstring, 11);
      apen = GetAPen (winrp);
      mywin->Flags &= ~SIZEGADGET;
      if ((winx == mywin->Width) && (winy == mywin->Height))
      {        
         Move (winrp, innerwidth - textlen, mywin->Height - textoff);
         Text (winrp, dispstring, 11);
         SetAPen (winrp, pens[5]);
         Move (winrp, innerwidth - textlen - fontX - 1, mywin->Height - (bottomoff - fontY >> 1) - 2);
         Draw (winrp, innerwidth - textlen - fontX - 1, mywin->Height - (bottomoff - fontY >> 1) - fontY);
         Draw (winrp, innerwidth - textlen - 2, mywin->Height - (bottomoff - fontY >> 1) - fontY);
         SetAPen (winrp, pens[4]);
         Move (winrp, innerwidth - textlen - 1, mywin->Height - (bottomoff - fontY >> 1) - fontY + 1);
         Draw (winrp, innerwidth - textlen - 1, mywin->Height - (bottomoff - fontY >> 1) - 1);
         Draw (winrp, innerwidth - textlen - fontX, mywin->Height - (bottomoff - fontY >> 1) - 1);
         if (cybergfxscreen)
         {
            FillPixelArray (winrp, innerwidth - textlen - fontX, mywin->Height - (bottomoff - fontY >> 1) - fontY + 1,
              fontX - 1, fontY - 2, cybercolor);
         }
         else
         {
            SetAPen (winrp, colorreg);
            RectFill (winrp, innerwidth - textlen - fontX, mywin->Height - (bottomoff - fontY >> 1) - fontY + 1,
              innerwidth - textlen - 2, mywin->Height - (bottomoff - fontY >> 1) - 2);
         }
      }
      mywin->Flags |= SIZEGADGET;
      SetAPen (winrp, apen);
   }  // if (showrgb)
}

LONG adjustX (LONG &x)
{
 LONG xoff = 0;
 
   x -= sourcewidth >> 1;
   if (x < 0)
   {
      xoff = x;
      x = 0;
   }
   else if (x > (scr->Width - sourcewidth))
   {
      xoff = -(scr->Width - sourcewidth - x);
      x = scr->Width - sourcewidth;
   }
   return xoff;
}

LONG adjustY (LONG &y)
{
 LONG yoff = 0;

   y -= sourceheight >> 1;
   if (y < 0)
   {
      yoff = y;
      y = 0;
   }
   else if (y > (scr->Height - sourceheight))
   {
      yoff = -(scr->Height - sourceheight - y);
      y = scr->Height - sourceheight;
   }
   return yoff;
}

void refresh ()
{
 LONG x, y, xoff, yoff;

   if (mywin)
   {
      // set the coordinates
      if (enable)
      {
         if (fixed)
         {
            x = fixedX;
            y = fixedY;
         }
         else
         {
            x = scr->MouseX;
            y = scr->MouseY;
         }
         oldx = x;
         oldy = y;
      }
      else
      {
         x = oldx;
         y = oldy;
      }
      
      printcoords (x, y);
      printrgb (x, y);

      xoff = adjustX (x);
      yoff = adjustY (y);

      // calculate the crosshair
      if (crosshair)
      {
         xoff = scalefac * ((sourcewidth >> 1) + xoff) + (scalefac - 1 >> 1);
         yoff = scalefac * ((sourceheight >> 1) + yoff) + (scalefac - 1 >> 1);
      }

      if (scalefac > 1)
      {
         // blit and scale
         BltBitMap (scrbm, x, y, srcbm, 0, 0, sourcewidth, sourceheight, ABNC | ABC, ~0, NULL);
         BitMapScale (&bsa);
         // draw the grid
         if (grid)
         {
            SetDrMd (&destrp, JAM1);
            BltTemplate (maskbm->Planes[0], 0, maskbm->BytesPerRow, &destrp, 0, 0, sourcewidth * scalefac, sourceheight * scalefac);
         }
         // draw the crosshair
         if (crosshair)
         {
            SetDrMd (&destrp, COMPLEMENT);
            RectFill (&destrp, xoff, 0, xoff, sourceheight * scalefac - 1);
            RectFill (&destrp, 0, yoff, 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
      {
         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
            if (crosshair)
            {
               SetDrMd (winrp, COMPLEMENT);
               RectFill (winrp, leftoff + xoff, topoff, leftoff + xoff, topoff + innerheight - 1);
               RectFill (winrp, leftoff, topoff + yoff, leftoff + innerwidth - 1, topoff + yoff);
               SetDrMd (winrp, JAM2);
            }
         }
         mywin->Flags |= SIZEGADGET;
      }
      if (!fast)
      {
         SetSignal (0, 1 << timesig);
      }
   }
}

//----------------------------------------------

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);
   }
}

void initbroker ()
{
 LONG errorcode;

   broker_mp = CreateMsgPort ();
   if (!broker_mp)
   {
      error (GetString (TXT_ERROR_COMMODITYMP));
      throw (1);
   }
   newbroker.nb_Port = broker_mp;
   newbroker.nb_Descr = GetString (TXT_DESCRIPTION),
   cxsigflag = 1 << broker_mp->mp_SigBit;
   broker = CxBroker (&newbroker, &errorcode);
   if (!broker)
   {
      if (errorcode == CBERR_SYSERR)
      {
         error (GetString (TXT_ERROR_COMMODITYBROKER));
      }
      throw (2);
   }
   customcxobj = CxCustom (CxFunction, 0);
   if (!customcxobj)
   {
      error (GetString (TXT_ERROR_COMMODITYCUSTOM));
      throw (3);
   }
   AttachCxObj (broker, customcxobj);
   if (CxObjError (customcxobj))
   {
      error (GetString (TXT_ERROR_COMMODITYBROKER));
      throw (4);
   }
   if (ParseIX (setoriginkey, &setoriginix) || ParseIX (resetoriginkey, &resetoriginix))
   {
      origin = FALSE;
      error (GetString (TXT_ERROR_HOTKEY), "'Set Origin'");
   }
   if (ParseIX (showhidekey, &showhideix))
   {
      showhide = FALSE;
      error (GetString (TXT_ERROR_HOTKEY), "'Show/Hide Window'");
   }
   ActivateCxObj (broker, 1);
}

void reopenwin ()
{
   closewin ();
   getoffsets ();
   openwin ();
   allocbm ();
}

void setpropgad ()
{
   allocbm ();
   SetAttrs (propgadget, PGA_Top, scalefac - 1);
   RefreshGList ((struct Gadget *) propgadget, mywin, NULL, 1);
   setwintitle ();
}

BOOL menupick (UWORD code)
{
 static BOOL (*func)();

   while ((code != MENUNULL) && !jump)
   {
      item = ItemAddress (menu, code);
      if (SUBNUM (code) != NOSUB)
      {
         if (GTMENUITEM_USERDATA (item))  // 'no other screen'-submenu == 0
         {
            setupscreen ((struct Screen *) GTMENUITEM_USERDATA (item));
            reopenwin ();
         }
      }
      else
      {
         func = (BOOL (*)()) GTMENUITEM_USERDATA (item);
         if (func () == FALSE)
         {
            return FALSE;
         }
      }
      if (!jump) code = item->NextSelect;
   }
   return TRUE;
}

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

   code = ToUpper (code);

   if (code == ToUpper (*GetString (KEY_JUMP)))
   {
      returnvalue = jumpFunc ();    
   }
   else if (code == ToUpper (*GetString (KEY_JUMPTOACTIVESCREEN)))
   {
      jumptoactivescreen = !jumptoactivescreen;
      setmenu ();    
   }
   else if (code == ToUpper (*GetString (KEY_SAVEIFF)))
   {
      returnvalue = saveFunc ();
   }
   else if (code == ToUpper (*GetString (KEY_ENABLE)))
   {
      enable = !enable;
      ActivateCxObj (broker, enable);
      setmenu ();
   }
   else if (code == ToUpper (*GetString (KEY_FIXED)))
   {
      fixed = !fixed;
      fixedX = scr->MouseX;
      fixedY = scr->MouseY;
      setmenu ();
   }
   else if (code == ToUpper (*GetString (KEY_FAST)))
   {
      fast = !fast;
      setmenu ();
   }
   else if (code == ToUpper (*GetString (KEY_COORDINATES)))
   {
      coords = !coords;
      reopenwin ();
   }
   else if (code == ToUpper (*GetString (KEY_CROSSHAIR)))
   {
      crosshair = !crosshair;
      setmenu ();
   }
   else if (code == ToUpper (*GetString (KEY_GRID)))
   {
      grid = !grid;
      setupgrid ();
      setmenu ();
   }
   else if (code == ToUpper (*GetString (KEY_SHOWRGB)))
   {
      showrgb = !showrgb;
      reopenwin ();
   }
   else if (code == ToUpper (*GetString (KEY_ABOUT)))
   {
      returnvalue = AboutFunc ();
   }
   else if (code == ToUpper (*GetString (KEY_ICONIFYONCLOSE)))
   {
      hideonclose = !hideonclose;
      setmenu ();
   }
   else if (code == ToUpper (*GetString (KEY_ICONIFY)))
   {
      returnvalue = hideFunc ();
   }
   else if (code == ToUpper (*GetString (KEY_QUIT)))
   {
      returnvalue = FALSE;
   }
   else switch (code)
   {
    case 27:  // ESC
      if (hideonclose)
      {
         hidewindow ();
         jump = TRUE;
      }
      else
      {
         returnvalue = FALSE;
      }
      break;
    case '+':
      if (scalefac < maxscalefac)
      {
         scalefac++;
         setpropgad ();
      }
      break;
    case '-':
      if (scalefac > 1)
      {
         scalefac--;
         setpropgad ();
      }
      break;
   }
   return returnvalue;
}

void rawkey (UWORD code, UWORD qualifier)
{
   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, app_clicked = 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:
                  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:
                  showwindow ();
                  break;
                case CXCMD_KILL:
                  cont = FALSE;
                  break;
               }
            }
         }  // while ((msg
      }
      if (userport)
      {
         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;
                  }
                  break;
                case IDCMP_NEWSIZE:
                  allocbm ();
                  break;
                case IDCMP_IDCMPUPDATE:
                  ULONG h;
                  GetAttr (PGA_Top, propgadget, &h);
                  if (++h != scalefac)
                  {
                     scalefac = h;
                     allocbm ();
                     setwintitle ();
                     setmenu ();
                  }
                  break;
                case IDCMP_MENUPICK:
                  cont = menupick (m.Code);
                  break;
                case IDCMP_ACTIVEWINDOW:
                  SetAPen (winrp, pens[0]);
                  SetBPen (winrp, pens[1]);
                  printcoords (oldx, oldy);
                  printrgb (oldx, oldy);
                  ClearMenuStrip (mywin);
                  freescreenmenu (ItemAddress (menu, FULLMENUNUM (0, 1, 0)));
                  makescreenmenu ();
                  SetMenuStrip (mywin, menu);
                  break;
                case IDCMP_INACTIVEWINDOW:
                  SetAPen (winrp, pens[2]);
                  SetBPen (winrp, pens[3]);
                  printcoords (oldx, oldy);
                  printrgb (oldx, oldy);
                  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;
                case IDCMP_REFRESHWINDOW:
                  refresh ();
                  break;
               }
               if (jump)
               {
                  break;
               }
            }  // while (intuimsg
         }
      }  // if (userport)
      if (app_mp)
      {
         if (sigmask & (1 << app_mp->mp_SigBit))
         {
            app_clicked = FALSE;
            while ((appmsg = (struct AppMessage *) GetMsg (app_mp)) != 0)
            {
               ReplyMsg ((struct Message *) appmsg);
               app_clicked = TRUE;
            }
            if (app_clicked)
            {
               showwindow ();
            }
         }
      }
      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 ();
            }
            else if (jumptoactivescreen)
            {
               setupscreen (tmpscr);
               screentofront = FALSE;
               reopenwin ();
            }
         }
      }
      if (sigmask & SIGBREAKF_CTRL_C)
      {
         cont = FALSE;
      }
   }  // while (cont)
}

//- iff save -----------------------------------

BOOL openfilereq ()
{
 APTR request;
 
   request = AllocAslRequestTags (ASL_FileRequest,
    ASLFR_InitialHeight, scr->Height / 2,
    ASLFR_Screen, scr,
    ASLFR_SleepWindow, TRUE,
    ASLFR_Window, mywin,
    ASLFR_TitleText, GetString (TXT_SAVEIFF),
    ASLFR_InitialDrawer, iffpath,
    ASLFR_InitialFile, iffname,
    ASLFR_DoSaveMode, TRUE,
    ASLFR_RejectIcons, TRUE,
    ASLFR_Screen, OpenWorkBench (),
    TAG_DONE);
   if (request)
   {
      if (AslRequest (request, NULL))
      {
         strncpy (iffpath, ((struct FileRequester *) request)->fr_Drawer, 255);
         strncpy (iffname, ((struct FileRequester *) request)->fr_File, 108);
         FreeAslRequest (request);
         return TRUE;
      }
   }
   return FALSE;
}

void write (register __d4 APTR buffer, register __d5 LONG size)
{
   if (Write (fh, buffer, size) != size)
   {
      throw (1);
   }
}

ULONG compressline (register __a3 UBYTE *src, register __a4 UBYTE *dest, ULONG size)
{
 register UBYTE x, y, *tmp, *deststart;
   
   deststart = dest;
   while (size > 1)
   {
      x = 0;
      y = *src++;
      if (y == *src)
      {
         size--;
         while (size && (x < 127) && (*src == y))
         {
            size--;
            x++;
            src++;
         }
         *dest++ = -x;
         *dest++ = y;
      }
      else
      {
         tmp = dest++;
         while (size && (x < 128) && (*src != y))
         {
            size--;
            x++;
            *dest++ = y;
            y = *src++;
         }
         *tmp = x - 1;
         src--;
      }
   }
   if (size)
   {
      *dest++ = 0;
      *dest++ = *src;
   }
   return dest - deststart;
}

void savetruecolor ()
{
 UWORD bpr = ((innerwidth >> 3) + 2) & 0xfffe, byteoff, i, j;
 UBYTE *mem, *mem2, *offset, r, g, b;
 ULONG packlen;
 register UWORD x, y;
 struct BitMap *bm = NULL;
 BOOL ownbitmap = FALSE;
 
   if (GetBitMapAttr (destbm, BMA_FLAGS) & PIXFMT_RGB24)
   {
      bm = destbm;
   }
   else
   {
      bm = AllocBitMap (innerwidth, innerheight, 24, BMF_CLEAR | BMF_MINPLANES | PIXFMT_RGB24, 0);
      if (!bm)
      {
         error (GetString (TXT_ERROR_ALLOCBITMAPSAVE));
         throw (1);
      }
      ownbitmap = TRUE;
      BltBitMap (destbm, 0, 0, bm, 0, 0, innerwidth, innerheight, ABNC | ABC, ~0, NULL);
   }
   mem = (UBYTE *) AllocVec (bpr * 24, 0);
   if (!mem)
   {
      throw (2);
   }
   mem2 = (UBYTE *) AllocVec (bpr << 1, 0);
   if (!mem2)
   {
      throw (3);
   }
   for (i = 0; i < destbm->Rows; i++)
   {
      memset (mem, 0, bpr * 24);
      offset = bm->Planes[0] + i * bpr * 24;
      for (j = 0, y = 7; j < (bpr * 24); j++, y--)
      {
         r = *(offset + j++);
         g = *(offset + j++);
         b = *(offset + j);
         if (y > 7) y = 7;
         byteoff = j / 24;
         for (x = 0; x < 8; x++)
         {
            mem[x*bpr+byteoff] |= ((r >> x) & 1u) << y;
            mem[(x+8)*bpr+byteoff] |= ((g >> x) & 1u) << y;
            mem[(x+16)*bpr+byteoff] |= ((b >> x) & 1u) << y;
         }
      }
      for (j = 0; j < (bpr * 24); j += bpr)
      {
         packlen = compressline (&mem[j], mem2, bpr);
         write (mem2, packlen);
      }
      RectFill (winrp, leftoff, i + topoff, innerwidth + leftoff - 1, i + topoff);
   }
   FreeVec (mem);
   FreeVec (mem2);
   if (ownbitmap && bm)
   {
      FreeBitMap (bm);
   }
}

void saveiff ()
{
 ULONG len, bodylen, packlen, vpmodeid, colors[3];;
 UWORD i, j, depth;
 UBYTE cmap[3], *mem;
 struct BitMapHeader bh;
 struct DisplayInfo di;
 char header[] = "FORM....ILBMANNO";
 char anno[] = "Written by " VERSION "\0";
 
   try
   {
      // save header
      depth = GetBitMapAttr (destbm, BMA_DEPTH);
      write (header, strlen (header));
      len = (strlen (anno) + 2) & 0xfe;
      write (&len, 4);
      write (anno, len);
      write ("BMHD", 4);
      len = sizeof (struct BitMapHeader);
      write (&len, 4);
      bh.bmh_Width = innerwidth;
      bh.bmh_Height = innerheight;
      bh.bmh_Left = bh.bmh_Top = 0;
      bh.bmh_Depth = cybergfxscreen ? 24 : depth;
      bh.bmh_Masking = 0;
      bh.bmh_Compression = 1;
      bh.bmh_Pad = 0;
      bh.bmh_Transparent = 0;
      vpmodeid = GetVPModeID (&scr->ViewPort);
      if (GetDisplayInfoData (NULL, (UBYTE *) &di, sizeof (struct DisplayInfo), DTAG_DISP, vpmodeid))
      {
         bh.bmh_XAspect = di.Resolution.x;
         bh.bmh_YAspect = di.Resolution.y;
      }
      else
      {
         bh.bmh_XAspect = bh.bmh_YAspect = 0;
      }
      bh.bmh_PageWidth = innerwidth;
      bh.bmh_PageHeight = innerheight;
      write (&bh, len);
      write ("CAMG", 4);
      len = 4;
      write (&len, 4);
      write (&vpmodeid, 4);
      // no "CMAP" in true color
      if (!cybergfxscreen)
      {
         write ("CMAP", 4);
         len = 3 * (1 << depth);
         write (&len, 4);
         for (i = 0; i < (1 << depth); i++)
         {
            GetRGB32 (scr->ViewPort.ColorMap, i, 1, colors);
            cmap[0] = colors[0] & 0xff;
            cmap[1] = colors[1] & 0xff;
            cmap[2] = colors[2] & 0xff;
            write (cmap, 3);
         }
      }
      write ("BODY....", 8);
      bodylen = Seek (fh, 0, OFFSET_CURRENT) - 4;
      
      if (cybergfxscreen)
      {
         savetruecolor ();
      }
      else
      {
         mem = (UBYTE *) AllocVec (destbm->BytesPerRow << 1, 0);
         if (mem)
         {
            if (GetBitMapAttr (destbm, BMA_FLAGS) & BMF_INTERLEAVED)
            {
               // interleaved
               for (i = 0, len = 0; i < (destbm->Rows * depth); i++, len += destbm->BytesPerRow / depth)
               {
                  packlen = compressline (destbm->Planes[0] + len, mem, destbm->BytesPerRow / depth);
                  write (mem, packlen);
                  if (!(i % depth))
                  {
                     RectFill (winrp, leftoff, i / depth + topoff, innerwidth + leftoff - 1, i / depth + topoff);
                  }
               }
            }
            else
            {
               len = 0;
               for (i = 0; i < destbm->Rows; i++)
               {
                  for (j = 0; j < depth; j++)
                  {
                     packlen = compressline (destbm->Planes[j] + len, mem, destbm->BytesPerRow);
                     write (mem, packlen);
                  }
                  len += destbm->BytesPerRow;
                  RectFill (winrp, leftoff, i + topoff, innerwidth + leftoff - 1, i + topoff);
               }
            }
            FreeVec (mem);
         }
         else
         {
            throw (3);
         }
      }  // if (cybergfxscreen)
      if (Seek (fh, 0, OFFSET_CURRENT) & 1)
      {
         *cmap = 0x80;
         write (cmap, 1);
      }
      len = Seek (fh, 4, OFFSET_BEGINNING) - 8;
      write (&len, 4);
      Seek (fh, bodylen, OFFSET_BEGINNING);
      len = len - bodylen + 4;
      write (&len, 4);
   }
   catch (int) { }
}

//- menu ---------------------------------------

BOOL fixedFunc ()
{
   if (!fixed && (item->Flags & CHECKED))
   {
      fixed = TRUE;
      fixedX = scr->MouseX;
      fixedY = scr->MouseY;
   }
   else if (fixed && !(item->Flags & CHECKED))
   {
      fixed = FALSE;
   }
   return TRUE;
}

BOOL fastFunc ()
{
   fast = (item->Flags & CHECKED) != 0;
   return TRUE;
}

BOOL crosshairFunc ()
{
   crosshair = (item->Flags & CHECKED) != 0;
   return TRUE;
}

BOOL AboutFunc ()
{
 static struct EasyStruct es_about = {
   20, 0,
   "Lupe",
   "%s%s%s%s",
   "%s"
 };

 char *era_about[] = {
   VERSION " (" __DATE__ ")\n\n",
   GetString (TXT_ABOUT_WRITTENBY),
   "\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",
   GetString (TXT_ABOUT_PD),
   "Ok"
 };

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

BOOL jumpFunc ()
{
   if (setupscreen (NULL) == NEWSCREEN)
   {
      reopenwin ();
   }
   return TRUE;
}

BOOL jumptoactiveFunc ()
{
   jumptoactivescreen = (item->Flags & CHECKED) != 0;
   return TRUE;
}

BOOL saveFunc ()
{
 BPTR dirlock, olddirlock;
 ULONG reg;
 
   if (scalefac == 1)
   {
    LONG x = oldx, y = oldy;
      if (!(destbm = AllocBitMap (innerwidth, innerheight, cybergfxscreen ? 24 : scrdepth, cybergfxscreen ? (BMF_CLEAR | BMF_MINPLANES | PIXFMT_RGB24) : BMF_CLEAR, 0)))
      {
         error (GetString (TXT_ERROR_ALLOCBITMAPSAVE));
         return TRUE;
      }
      adjustX (x);
      adjustY (y);
      BltBitMap (scrbm, x, y, destbm, 0, 0, innerwidth, innerheight, ABNC | ABC, ~0, NULL);
   }
   if (openfilereq ())
   {
      BltBitMapRastPort (destbm, 0, 0, winrp, leftoff, topoff, innerwidth, innerheight, ABNC | ABC);
      dirlock = Lock (iffpath, SHARED_LOCK);
      if (dirlock)
      {
         olddirlock = CurrentDir (dirlock);
         fh = Open (iffname, MODE_NEWFILE);
         if (fh)
         {
            mywin->Flags &= ~SIZEGADGET;
            reg = GetAPen (winrp);
            SetAPen (winrp, 0);
            saveiff ();
            Close (fh);
            BltBitMapRastPort (destbm, 0, 0, winrp, leftoff, topoff, innerwidth, innerheight, ABNC | ABC);
            SetAPen (winrp, reg);
            mywin->Flags |= SIZEGADGET;
         }
         else
         {
            error (GetString (TXT_ERROR_CREATE), iffname);
         }
         CurrentDir (olddirlock);
         UnLock (dirlock);
      }
      else
      {
         error (GetString (TXT_ERROR_LOCK), iffpath);
      }
   }
   if ((scalefac == 1) && destbm)
   {
      FreeBitMap (destbm);
      destbm = NULL;
   }
   return TRUE;
}

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

BOOL gridFunc ()
{
   grid = (item->Flags & CHECKED) != 0;
   setupgrid ();
   return TRUE;
}

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

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 ()
{
   hideonclose = (item->Flags & CHECKED) != 0;
   return TRUE;
}

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

BOOL QuitFunc ()
{
   return FALSE;
}

//- args ---------------------------------------

BOOL argbool (char **argv, char *name, BOOL def)
{
 char *value;

   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;

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

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

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

   wintop = scr->RastPort.TxHeight + (UWORD) scr->WBorTop + 1;
   // make the window as square as possible
   drawinfo = GetScreenDrawInfo (scr);
   if (drawinfo)
   {
      innerheight = innerwidth * drawinfo->dri_Resolution.X / drawinfo->dri_Resolution.Y;
      FreeScreenDrawInfo (scr, drawinfo);
   }
   if (Cli ())
   {
      GetProgramName (programname, 108);
   }
   else
   {
      strcpy (programname, thistask->tc_Node.ln_Name);
   }
   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, "Iconify", hide);
         hideonclose = argbool (argv, "IconifyOnClose", hideonclose);
         grid = argbool (argv, "Grid", grid);
         showrgb = argbool (argv, "ShowRGB", showrgb);
         argstring (iffpath, argv, "IFFFile", 255);
         jumptoactivescreen = argbool (argv, "JumpToActiveScreen", jumptoactivescreen);
         argstring (language, argv, "LANGUAGE", 20);
         FreeDiskObject (programicon);
      }
      CloseLibrary (IconBase);
   }
   if (Cli ())
   {
      memset (args, '\0', 21 * sizeof (LONG));
      args[1] = fast;
      args[8] = coords;
      args[9] = crosshair;
      args[12] = fixed;
      args[14] = hide;
      args[15] = hideonclose;
      args[16] = grid;
      args[17] = showrgb;
      args[19] = jumptoactivescreen;
      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, 49);
         if (*++args) strncpy (resetoriginkey, (char *) *args, 49);
         args++;
         fixed = *args++;
         if (*args) strncpy (showhidekey, (char *) *args, 49);
         args++;
         hide = *args++;
         hideonclose = *args++;
         grid = *args++;
         showrgb = *args++;
         if (*args) strncpy (iffpath, (char *) *args, 255);
         args++;
         jumptoactivescreen = *args++ != 0;
         if (*args) strncpy (language, (char *) *args, 19);
         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;
   
   char *str;
   str = strrchr (iffpath, '/');
   if (str)
   {
      strncpy (iffname, str + 1, 108);
      *str = '\0';
   }
   else if ((str = strrchr (iffpath, ':')) != 0)
   {
      strncpy (iffname, ++str, 108);
      *str = '\0';
   }
   else
   {
      strncpy (iffname, iffpath, 108);
      iffpath[0] = '\0';
   }
}

//- main ---------------------------------------

struct Library *openlib (char *libname, ULONG ver)
{
 struct Library *lib;
   
   lib = OpenLibrary (libname, ver);
   if (!lib)
   {
      error (GetString (TXT_ERROR_LIBRARY), libname, ver);
      throw (1);
   }
   return lib;
}

void main ()
{
   try
   {
      CxBase = openlib ("commodities.library", 37);
      GfxBase = (struct GfxBase *) openlib ("graphics.library", 39);
      GadToolsBase = openlib ("gadtools.library", 37);
      WorkbenchBase = openlib ("workbench.library", 37);
      AslBase = openlib ("asl.library", 37);
      UtilityBase = openlib ("utility.library", 37);
      CyberGfxBase = OpenLibrary ("cybergraphics.library", 40);
      LocaleBase = OpenLibrary ("locale.library", 38);
      InitRastPort (&destrp);
      SetAPen (&destrp, 0);
      InitRastPort (&maskrp);
      SetDrMd (&maskrp, COMPLEMENT);
      thistask = FindTask (NULL);
      if ((topazfont = OpenFont (&topaz_attr)) != 0)
      {
         try
         {
            setupscreen (NULL);  // needed before readargs ()
            readargs ();
            OpenlupeCatalog (0, language[0] ? language : 0);
            getoffsets ();
            if (hide)
            {
               hidewindow ();
            }
            else
            {
               openwin ();
               allocbm ();
            }
            if ((timesig = AllocSignal (-1L)) != -1)
            {
               if ((showhidesig = AllocSignal (-1L)) != -1)
               {
                  try
                  {
                     initbroker ();
                     processmsg ();
                  }
                  catch (int) { };
                  if (broker)
                  {
                     DeleteCxObjAll (broker);
                     broker = NULL;
                  }
                  if (broker_mp)
                  {
                     DeleteMsgPort (broker_mp);
                     broker_mp = NULL;
                  }
                  FreeSignal (showhidesig);
               }
               else
               {
                  error (GetString (TXT_ERROR_SIGNAL));
               }
               FreeSignal (timesig);
            }
            else
            {
               error (GetString (TXT_ERROR_SIGNAL));
            }
         }  // try
         catch (int) { }
         closewin ();
         CloseDownScreen ();
         freeappicon ();
         CloseFont (topazfont);
         if (screenfont)
         {
            CloseFont (screenfont);
         }
      }
      else
      {
         error (GetString (TXT_ERROR_FONT));
      }
      CloselupeCatalog ();
      if (LocaleBase)
      {
         CloseLibrary (LocaleBase);
      }
      if (CyberGfxBase)
      {
         CloseLibrary (CyberGfxBase);
      }
      CloseLibrary (UtilityBase);
      CloseLibrary (AslBase);
      CloseLibrary (WorkbenchBase);
      CloseLibrary (GadToolsBase);
      CloseLibrary ((struct Library *) GfxBase);
      CloseLibrary (CxBase);
   }
   catch (int) { }
}

