/*------------------------------------------------*
  $Id: pointer.c,v 1.5 92/11/03 17:14:37 tf Exp $
  MousePointer images and support functions
  written by Tobias Ferber on 17-Feb-92
 *------------------------------------------------*/

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/preferences.h>

static char rcs_id[] = "$Id: pointer.c,v 1.5 92/11/03 17:14:37 tf Exp $";

/*
 * This code imports both:
 */

extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;

/*
 * A mouse pointer is nothing else than a sprite. Here we expect the
 * mouse pointer to be sprite #0 (with color registers 17,18 and 19)
 * Sprites are handled with the SpriteImage structure which *MUST*
 * be located in CHIP memory (!)  This structure looks as follows:
 *
 * struct SpriteImage
 * { USHORT posctl[2];         * used by simple sprite machine
 *   USHORT data[HEIGHT][2];   * actual sprite image, 2 planes
 *   USHORT reserved[2];       * initialized to 0x0,0x0
 * };
 *
 */

#define SPRITEIMAGE_SIZE(h) (4+4*(h)+4)

/*
 * We need this to restore the old color map
 */

static USHORT MouseColors[3];

#define SetColor(vp,reg,val) SetRGB4(vp,reg,((val)>>8)&0x0F,   \
                                            ((val)>>4)&0x0F,   \
                                            ((val)>>0)&0x0F )


/*
 * FUNCTIONs
 *
 *   SaveMouseColors(), ResoreMouseColors()
 *
 * SYNOPSIS
 *
 *   void SaveMouseColors(Window);
 *   void RestoreMouseColors(Windo);
 *
 *   struct Window *Window;
 *
 * DESCRIPTION
 *
 *   Save/resore the curent mouse pointer colors in the given window.
 *   The mouse pointer is assumed to be sprite #0 and so (only) the color
 *   registers 17, 18 and 19 are saved.
 *
 * NOTE
 *
 *   RestoreMouseColors() can only restore the *LAST* saved colors.
 */

void SaveMouseColors(struct Window *Window)
{ struct ColorMap *cm= Window->WScreen->ViewPort.ColorMap;
  MouseColors[0]= GetRGB4(cm,17);
  MouseColors[1]= GetRGB4(cm,18);
  MouseColors[2]= GetRGB4(cm,19);
}

void RestoreMouseColors(struct Window *Window)
{ struct ViewPort *vp= &Window->WScreen->ViewPort;
  SetColor(vp,17,MouseColors[0]);
  SetColor(vp,18,MouseColors[1]);
  SetColor(vp,19,MouseColors[2]);
}

/*
 * FUNCTION
 *
 *   SetMousePointer -- Change the mouse pointer image in a window
 *
 * SYNOPSIS
 *
 *   image= SetMousePointer(Window, data, height, width, dx, dy, colors);
 *
 *   struct SpriteImage *image;
 *   struct Window *Window;
 *   USHORT data[height][2];
 *   WORD   height, width;
 *   WORD   dx, dy;
 *   USHORT colors[3];
 *
 * DESCRIPTION
 *
 *   Set the mouse pointer image in the given window.  The image `data' does
 *   *NOT* need to be located in CHIP memory, `height' and `width' define the
 *   image dimensions.  The mouse pointer is expected to be sprite 0 and so
 *   it's `colors' (reg. 17,18,19) will be set according to the given color
 *   table.  The hot spot is set to (-dx|-dy).
 *   A pointer to the allocated SpriteImage structure will be returned, NULL
 *   if there was an error.
 *
 * NOTE
 *
 *   Maximum width for sprites is 16 !
 *   Do not forget to UnsetMousePointer() before calling SetMousePointer()
 *   again or before closing your window !
 */

USHORT *SetMousePointer(Window, data, height, width, dx, dy, colors)
struct Window *Window;
USHORT *data;
WORD height, width, dx,dy;
USHORT colors[3];
{ USHORT *image= (USHORT *)AllocMem(SPRITEIMAGE_SIZE(height),MEMF_CHIP|MEMF_CLEAR);
  if(image)
  { int i;
    USHORT *ptr= &image[2];  /* pointer to SpriteImage->data */
    for(i=0; i<height; i++)
    { *ptr++ = *data++;  /* copy plane 0 */
      *ptr++ = *data++;  /* copy plane 1 */
    }
    SaveMouseColors(Window);
    SetPointer(Window, image, height, width, dx, dy);
    if(colors)
    { struct ViewPort *vp= &Window->WScreen->ViewPort;
      SetColor(vp,17,colors[0]);
      SetColor(vp,18,colors[1]);
      SetColor(vp,19,colors[2]);
    }
  }
  return(image);
}

/*
 * and...
 */

void UnsetMousePointer(struct Window *Window, USHORT *image, WORD height)
{ if(image) FreeMem(image,SPRITEIMAGE_SIZE(height));
  ClearPointer(Window);
  RestoreMouseColors(Window);
}

/*
 * FUNCTIONs
 *
 *   FlipPointerX(),FlipPointerY(),FlipPointerZ()
 *
 * SYNOPSIS
 *
 *   void FlipPointerX(Window);
 *   void FlipPointerY(Window);
 *   void FlipPointerZ(Window);
 *
 *   struct Window *Window;
 *
 * DESCRIPTION
 *
 *   Flip the current mouse pointer image either along the x-axis (horizontally)
 *   or along the y-axis (vertically).  FlipPointerZ() will simply swap the 2
 *   planes of the current pointer.  In any case we will me flip the hot spot
 *   with the image.
 *
 */

void FlipPointerX(struct Window *Window)
{ if(Window->Pointer && Window->PtrHeight > 0)
  { BYTE height= Window->PtrHeight,
          width= Window->PtrWidth,
             dy= Window->YOffset,
             dx= -(15+ Window->XOffset);
                 /* ^ NOT Window->PtrWidth! (we flipped a WORD!) */
    USHORT t, *image= Window->Pointer,
               *data= &Window->Pointer[2]; /* SpriteImage->data */
    int i, r, n= 2* Window->PtrHeight;
    ClearPointer(Window);
    for(i=0; i<n; i++)
    { t= 0;
      for(r=0; r<16; r++)
        t |= ( ((data[i]>>r) & 0x0001) << (15-r) );
      data[i]= t;
    }
    SetPointer(Window,image,height,width,dx,dy);
  }
}

void FlipPointerY(struct Window *Window)
{ if(Window->Pointer && Window->PtrHeight > 1)
  { BYTE height= Window->PtrHeight,
          width= Window->PtrWidth,
             dx= Window->XOffset,
             dy= -(Window->PtrHeight+Window->YOffset);
    ULONG t, *ptr= (ULONG *)Window->Pointer; /* SpriteImage */
    int i, n= 1+ Window->PtrHeight;
    ClearPointer(Window);
    for(i=1; i<=n/2; i++)  /* begin with i=1: SpriteImage->data */
    { t= ptr[i];
      ptr[i]= ptr[n-i];
      ptr[n-i]= t;
    }
    SetPointer(Window,ptr,height,width,dx,dy);
  }
}

void FlipPointerZ(struct Window *Window)
{ if(Window->Pointer && Window->PtrHeight > 0);
  { USHORT t, *ptr= &Window->Pointer[2];
    int i, height= Window->PtrHeight;
    for(i=0; i<2*height; i+=2)
    { t= ptr[i];
      ptr[i]= ptr[i+1];
      ptr[i+1]= t;
    }
  }
}

#include "images.h"  /* images.S declarations */

struct PointerImage
{ USHORT *Data;            /* the pointer matrix */
  WORD Height, Width;      /* the dimensions */
  WORD xOffset, yOffset;   /* the hot spot position */
  UWORD *ColorTable;       /* the sprite colors */
  USHORT *Image;           /* the SpriteImage structure */
};

struct PointerImage PointerImages[] = {
  (USHORT *)NULL,                  0, 0, 0, 0,(USHORT *)NULL,         NULL,
  (USHORT *)&GlovePointer[0][0],  14,15, 0, 0,&GloveColors[0],        NULL,
  (USHORT *)&GrabbingGlove[0][0], 14,15, 0, 0,&GloveColors[0],        NULL,
  (USHORT *)&SleepingGlove[0][0], 14,15, 0, 0,&GloveColors[0],        NULL,
  (USHORT *)&DragingGlove[0][0],  12,15,-4,-6,&GloveColors[0],        NULL,
  (USHORT *)&Nessie[0][0],        16,16, 0,-6,&NessieColors[0],       NULL,
  (USHORT *)&BusyBee[0][0],       16,16, 0, 0,&BusyBeeColors[0],      NULL,
  (USHORT *)&ZZBubble[0][0],      22,15,-5, 0,&ZZBubbleColors[0],     NULL,
  (USHORT *)&StopWatch[0][0],     16,16,-5, 0,&StopWatchColors[0],    NULL,
  (USHORT *)&HourGlass[0][0],     24,15, 0, 0,&HourGlassColors[0],    NULL,
  (USHORT *)&DottyPointer[0][0],  13,14, 0, 0,&DottyPointerColors[0], NULL,
  (USHORT *)&Standard20[0][0],    11,11, 0, 0,&Standard20Colors[0],   NULL,
  (USHORT *)&CrossHair[0][0],     13,13,-6,-6,&CrossHairColors[0],    NULL,
  (USHORT *)&TinyPointer[0][0],    5, 5, 0, 0,&TinyColors[0],         NULL,
  (USHORT *)&BugPointer[0][0],    12,11,-6,-2,&BugPointerColors[0],   NULL,
  (USHORT *)&Invisible[0][0],      1,16, 0, 0,(USHORT *)NULL,         NULL,
};

/* image numbers */

#define SMI_NONE         -1
#define SMI_PREFS         0
#define SMI_KLONDIKE      1
#define SMI_GRABGLOVE     2
#define SMI_SLEEPGLOVE    3
#define SMI_DRAGGLOVE     4
#define SMI_NESSIE        5
#define SMI_BUSYBEE       6
#define SMI_ZZBUBBLE      7
#define SMI_STOPWATCH     8
#define SMI_HOURGLASS     9
#define SMI_DOTTY        10
#define SMI_STANDARD20   11
#define SMI_CROSSHAIR    12
#define SMI_TINY         13
#define SMI_BUG          14
#define SMI_INVISIBLE    15

/* current pointer image */

static struct PointerImage *cpi= (struct PointerImage *)NULL;

/*
 * FUNCTION
 *
 *   SetPointerImage() -- Set a pre-defined pointer image in a window
 *
 * SYNOPSIS
 *
 *   void SetPointerImage(Window, num);
 *
 *   struct Window *Window;
 *   int num;                 * should be one of SMI_xxxxx defined in pointer.h
 *
 * DESCRIPTION
 *
 *   Set the mouse pointer image in the given window.  The pre-defined images
 *   are taken from "images.S".
 *
 * NOTE
 *
 *   Do not forget to UnsetMouseImage() i.e. SetMouseImage(SMI_NONE) before
 *   closing your window !
 */

void SetMouseImage(struct Window *Window,int num)
{ if(cpi && cpi->Image)
  { UnsetMousePointer(Window, cpi->Image, cpi->Height);
    cpi->Image= (USHORT *)NULL;
    cpi= (struct PointerImage *)NULL;
  }
  else SaveMouseColors(Window);  /* first call */

  if(num>=0) cpi= &PointerImages[num];

  if(num==0) /* Set the preferences mouse pointer */
  { struct Preferences prefs;
    GetPrefs(&prefs, sizeof(struct Preferences));

    /* init values for UnsetMousePointer() */

    cpi->Height= 16; /* Kickstart 1.3 standard height */

    /* Preferences.PointerMatrix[] is a SpriteImage structure */

    cpi->Image= SetMousePointer(Window, &prefs.PointerMatrix[2], 16, 16,
                                prefs.XOffset, prefs.YOffset, &prefs.color17);
  }
  else if(num>0)
  { cpi->Image= SetMousePointer(Window, cpi->Data,
                                        cpi->Height,
                                        cpi->Width,
                                        cpi->xOffset,
                                        cpi->yOffset,
                                        cpi->ColorTable );
  }
}
