/* Copyright 1986-1996 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_qCursor[] =
		"$Id: qCursor.c 87 2005-05-25 01:57:33Z ctm $";
#endif

/* Forward declarations in QuickDraw.h (DO NOT DELETE THIS LINE) */

#include "rsys/common.h"
#include "QuickDraw.h"
/* ack */
#include "WindowMgr.h"
#include "CQuickDraw.h"
#include "EventMgr.h"
#include "OSEvent.h"
#include "MemoryMgr.h"

#include "rsys/notmac.h"
#include "rsys/cquick.h"
#include "rsys/mman.h"
#include "rsys/resource.h"
#include "rsys/host.h"

#include "rsys/fatal.h"
#include "rsys/osevent.h"

#if !defined (CURSOR_DEBUG)

#define HOST_SET_CURSOR(d,m,x,y) host_set_cursor (d,m,x,y)

#else

#include "rsys/dirtyrect.h"
#include "rsys/vdriver.h"

PRIVATE void
cursor_debug (const uint8 *datap, const uint8 *maskp, int hot_x, int hot_y)
{
  int y;
  uint8 *pixel;
  int offset;

  pixel = vdriver_fbuf;
  offset = vdriver_row_bytes - 16;
  for (y = 0; y < 16; ++y)
    {
      uint16 u, bit;

      u = *datap++;
      u = (u << 8) | *datap++;
      for (bit = 1 << 15; bit; bit >>= 1)
	*pixel++ = u & bit ? 0 : 255;
      pixel += offset;
    }
  dirty_rect_accrue (0, 0, 16, 16);
  dirty_rect_update_screen ();
  vdriver_flush_display ();
}

#define HOST_SET_CURSOR(d,m,x,y)		\
do						\
{						\
  cursor_debug (d, m, x, y);			\
  host_set_cursor (d, m, x, y);			\
}						\
while (FALSE)

#endif


static CCrsrHandle current_ccrsr;
static Cursor current_crsr;

static boolean_t current_cursor_valid_p = FALSE;
static int current_cursor_color_p;

void
cursor_reset_current_cursor (void)
{
  current_cursor_valid_p = FALSE;
  if (current_cursor_color_p)
    SetCCursor (current_ccrsr);
  else
    SetCursor (&current_crsr);
}

A0 (PUBLIC, void, ROMlib_showcursor)
{
  if (!CrsrVis)
    {
      host_set_cursor_visible (TRUE);
      CrsrVis = TRUE;
    }
}

A0 (PUBLIC, void, ROMlib_restorecursor)
{
  if (CrsrVis)
    {
      host_set_cursor_visible (FALSE);
      CrsrVis = FALSE;
    }
}

A0(PUBLIC, void, ROMlib_showhidecursor)				/* INTERNAL */
{
    if (CrsrState == 0)
	ROMlib_showcursor();
    else
	ROMlib_restorecursor();
}

P1(PUBLIC pascal trap, void, SetCursor, Cursor *, cp)
{
  if (current_cursor_valid_p
      && !current_cursor_color_p
      && ! memcmp (&current_crsr, cp, sizeof (Cursor)))
    return;
  
  if (host_cursor_depth == 1)
    {
      HOST_SET_CURSOR ((char *) cp->data, (unsigned short *) cp->mask,
		       CW (cp->hotSpot.h), CW (cp->hotSpot.v));
    }
  else
    {
      PixMap data_pixmap, target_pixmap;
      char *data_baseaddr;
      /* rowbytes of expanded cursor data */
      int rowbytes;
      
      data_pixmap.baseAddr = RM ((Ptr) cp->data);
      data_pixmap.rowBytes = CWC (2);
      data_pixmap.bounds = ROMlib_cursor_rect;
      data_pixmap.cmpCount = CWC (1);
      data_pixmap.pixelType = CWC (0);
      data_pixmap.pixelSize = data_pixmap.cmpSize = CWC (1);
      data_pixmap.pmTable = RM (validate_relative_bw_ctab ());
      
      rowbytes = (16 * host_cursor_depth) / 8;
      data_baseaddr = alloca (16 * rowbytes);
      
      target_pixmap.baseAddr = (Ptr) RM (data_baseaddr);
      target_pixmap.rowBytes = CWC (rowbytes);
      target_pixmap.bounds = ROMlib_cursor_rect;
      target_pixmap.cmpCount = CWC (1);
      target_pixmap.pixelType = CWC (0);
      target_pixmap.pixelSize = target_pixmap.cmpSize 
	= CW (host_cursor_depth);
      /* the target pixmap colortable is not used by `convert_pixmap ()'
	 target_pixmap.pmTable = ...; */
      
      convert_pixmap (&data_pixmap, &target_pixmap,
		      &ROMlib_cursor_rect, NULL);
      
      HOST_SET_CURSOR (data_baseaddr, (unsigned short *) cp->mask,
		       CW (cp->hotSpot.h), CW (cp->hotSpot.v));
    }
  
  current_crsr = *cp;
  current_cursor_color_p = FALSE;
  current_cursor_valid_p = TRUE;
  
  if (CrsrState == 0)
    {
      CrsrVis = FALSE;
      ROMlib_showcursor();
    }
}

P0(PUBLIC pascal trap, void, InitCursor)
{
    CrsrState = 0;
    SetCursor(&arrowX);
    CrsrVis = FALSE;
    ROMlib_showcursor();
}

P0(PUBLIC pascal trap, void, HideCursor)	/* IMI-168 */
{
    ROMlib_restorecursor();
    CrsrState = CW(CW(CrsrState) - 1);
}

P0(PUBLIC pascal trap, void, ShowCursor)	/* IMI-168 */
{
    if ( (CrsrState = CW(CW(CrsrState) + 1)) == 0 )
	ROMlib_showcursor();
    if (CW(CrsrState) > 0)
	CrsrState = 0;
}

A1(PRIVATE, void, wewantpointermovements, INTEGER, x)
{
    CrsrState = CW(CW(CrsrState) + x);
    ROMlib_bewaremovement = TRUE;
}

P0(PUBLIC pascal trap, void, ObscureCursor)	/* IMI-168 */
{
    ROMlib_restorecursor();
    wewantpointermovements(0);
}

P2(PUBLIC pascal trap, void, ShieldCursor, Rect *, rp, Point, p) /* IMI-474 */
{
    EventRecord evt;

    GetOSEvent(0, &evt);
    evt.where.v = CW(evt.where.v) + p.v;
    evt.where.h = CW(evt.where.h) + p.h;
    if (PtInRect(evt.where, rp))
	HideCursor();
    else
	wewantpointermovements(-1);
}

typedef struct ccrsr_res
{
  CCrsr crsr 		LPACKED;
  PixMap crsr_map	LPACKED;
  char data		LPACKED;
} ccrsr_res, *ccrsr_res_ptr;

typedef struct { ccrsr_res_ptr p PACKED_P; } HIDDEN_ccrsr_res_ptr;
typedef HIDDEN_ccrsr_res_ptr *ccrsr_res_handle;

P1 (PUBLIC pascal trap, CCrsrHandle, GetCCursor, INTEGER, crsr_id)
{
  CCrsrHandle ccrsr_handle;
  ccrsr_res_handle res_handle;

  ccrsr_handle = (CCrsrHandle) NewHandle (sizeof (CCrsr));
  
  res_handle = (ccrsr_res_handle) ROMlib_getrestid (TICK ("crsr"), crsr_id);
  if (res_handle == NULL)
    return NULL;

  LOCK_HANDLE_EXCURSION_2
    (ccrsr_handle, res_handle,
     {
       ccrsr_res_ptr resource;
       CCrsrPtr ccrsr;
       CTabPtr tmp_ctab;
       int ccrsr_data_size;
       int ccrsr_data_offset;
       int ccrsr_ctab_size;
       int ccrsr_ctab_offset;
       
       int cursor_pixel_map_offset;
       PixMapPtr cursor_pixel_map_resource;
       PixMapHandle cursor_pixel_map;
       
       Handle h;
       
       resource = STARH (res_handle);
       ccrsr = STARH (ccrsr_handle);
       
       BlockMove ((Ptr) &resource->crsr, (Ptr) ccrsr,
		  sizeof (CCrsr));
       
       /* NOTE: use CL below instead of MR because they're overloading
	  crsrMap to have an offset rather than a handle */

       cursor_pixel_map_offset = CL ((uint32) ccrsr->crsrMap);
       cursor_pixel_map_resource
	 = (PixMapPtr) ((char *) resource + cursor_pixel_map_offset);
       
       cursor_pixel_map = NewPixMap ();
       ccrsr->crsrMap = RM (cursor_pixel_map);
       BlockMove ((Ptr) cursor_pixel_map_resource,
		  (Ptr) STARH (cursor_pixel_map),
		  sizeof *cursor_pixel_map_resource);
       
       ccrsr_data_offset = CL ((long) ccrsr->crsrData);
       
       ccrsr_ctab_offset = (int) PIXMAP_TABLE_AS_OFFSET (MR (ccrsr->crsrMap));
       ccrsr_data_size = ccrsr_ctab_offset - ccrsr_data_offset;
       
       ccrsr->crsrData = RM (NewHandle (ccrsr_data_size));
       BlockMove ((Ptr) resource + ccrsr_data_offset,
		  STARH (MR (ccrsr->crsrData)),
		  ccrsr_data_size);
       
       tmp_ctab = (CTabPtr) ((char *) resource + ccrsr_ctab_offset);
       ccrsr_ctab_size = CTAB_STORAGE_FOR_SIZE (CW (tmp_ctab->ctSize));
       h = RM (NewHandle (ccrsr_ctab_size));
       PIXMAP_TABLE_X (MR (ccrsr->crsrMap)) = (CTabHandle) h;
       BlockMove ((Ptr) tmp_ctab,
		  (Ptr) STARH (PIXMAP_TABLE (MR (ccrsr->crsrMap))),
		  ccrsr_ctab_size);
     });
  
  return ccrsr_handle;
}

Rect ROMlib_cursor_rect =
{
  CWC (0), CWC (0), CWC (16), CWC (16),
};

/* ### this isn't a cursor specific routine, and it may be helpful
   elsewhere, but since it isn't clear exactly what to compare, we'll
   leave it here for now */

static boolean_t
pixmap_eq_p (PixMapHandle pm0, PixMapHandle pm1)
{
  return (PIXMAP_PIXEL_SIZE_X (pm0) == PIXMAP_PIXEL_SIZE_X (pm1)
	  && PIXMAP_PIXEL_TYPE_X (pm0) == PIXMAP_PIXEL_TYPE_X (pm1)
	  && PIXMAP_CMP_COUNT_X (pm0) == PIXMAP_CMP_COUNT_X (pm1)
	  && PIXMAP_CMP_SIZE_X (pm0) == PIXMAP_CMP_SIZE_X (pm1)
	  && (CTAB_SEED_X
	      (PIXMAP_TABLE (pm0)) == CTAB_SEED_X (PIXMAP_TABLE (pm1)))
	  && !memcmp (&PIXMAP_BOUNDS (pm0),
		      &PIXMAP_BOUNDS (pm1),
		      sizeof (Rect)));
}

P1 (PUBLIC pascal trap, void, SetCCursor,
    CCrsrHandle, ccrsr)
{
  if (current_cursor_valid_p
      && current_cursor_color_p)
    {
      int current_ccrsr_data_size, ccrsr_data_size;
      
      current_ccrsr_data_size = GetHandleSize (CCRSR_DATA (current_ccrsr));
      ccrsr_data_size = GetHandleSize (CCRSR_DATA (ccrsr));
      
      if (current_ccrsr_data_size == ccrsr_data_size
	  && !memcmp (STARH (CCRSR_DATA (current_ccrsr)),
		      STARH (CCRSR_DATA (ccrsr)),
		      ccrsr_data_size)
	  && CCRSR_TYPE_X (current_ccrsr) == CCRSR_TYPE_X (ccrsr)
	  && !memcmp (CCRSR_1DATA (current_ccrsr),
		      CCRSR_1DATA (ccrsr),
		      sizeof (Bits16))
	  && !memcmp (CCRSR_MASK (current_ccrsr),
		      CCRSR_MASK (ccrsr),
		      sizeof (Bits16))
	  && !memcmp (&CCRSR_HOT_SPOT (current_ccrsr),
		      &CCRSR_HOT_SPOT (ccrsr),
		      sizeof (Point))
	  && pixmap_eq_p (CCRSR_MAP (current_ccrsr),
			  CCRSR_MAP (ccrsr)))
	return;
    }
  
  LOCK_HANDLE_EXCURSION_1
    ((Handle) ccrsr,
     {
       GDHandle gdev;
       PixMapHandle gd_pmap;
       Point *hot_spot;

       gdev = MR (MainDevice);
       gd_pmap = GD_PMAP (gdev);
       
       hot_spot = &CCRSR_HOT_SPOT (ccrsr);
       
       if (host_cursor_depth > 2)
	 {
	   Handle ccrsr_xdata;

	   ccrsr_xdata = CCRSR_XDATA (ccrsr);
	   if (!ccrsr_xdata)
	     {
	       ccrsr_xdata = NewHandle (32 * host_cursor_depth);
	       CCRSR_XDATA_X (ccrsr) = RM (ccrsr_xdata);
	     }
      
	   if (CCRSR_XVALID_X (ccrsr) == CWC (0)
	       || (CCRSR_XVALID (ccrsr) != host_cursor_depth)
	       || (CCRSR_ID_X (ccrsr) != CTAB_SEED_X (PIXMAP_TABLE (gd_pmap))))
	     {
	       SetHandleSize (ccrsr_xdata, 32 * host_cursor_depth);
	       
	       LOCK_HANDLE_EXCURSION_2
		 (CCRSR_MAP (ccrsr), CCRSR_DATA (ccrsr),
		  {
		    PixMap src;
		    PixMap ccrsr_xmap;

		    /* only fields used by `convert_pixmap ()',
		       baseAddr is filled in below */
		    memset (&ccrsr_xmap, 0, sizeof ccrsr_xmap);
		    ccrsr_xmap.rowBytes = CW (2 * host_cursor_depth);
		    ccrsr_xmap.pixelSize = CW (host_cursor_depth);
		    
		    src = *STARH (CCRSR_MAP (ccrsr));
		    src.baseAddr = CCRSR_DATA (ccrsr)->p;
		    LOCK_HANDLE_EXCURSION_1
		      (ccrsr_xdata,
		       {
			 ccrsr_xmap.baseAddr = ccrsr_xdata->p;
			 convert_pixmap (&src, &ccrsr_xmap,
					 &ROMlib_cursor_rect, NULL);
		       });
		  });
	       CCRSR_XVALID_X (ccrsr) = CW (host_cursor_depth);
	       CCRSR_ID_X (ccrsr) = CTAB_SEED_X (PIXMAP_TABLE (gd_pmap));
	     }
      
	   /* Actually set the current cursor. */
	   LOCK_HANDLE_EXCURSION_1
	     (ccrsr_xdata,
	      {
		HOST_SET_CURSOR ((char *) STARH (ccrsr_xdata),
				 (unsigned short *) CCRSR_MASK (ccrsr),
				 CW (hot_spot->h), CW (hot_spot->v));
	      });
	 }
       else
	 {
	   HOST_SET_CURSOR ((char *) CCRSR_1DATA (ccrsr),
			    (unsigned short *) CCRSR_MASK (ccrsr),
			    CW (hot_spot->h), CW (hot_spot->v));
	 }
     });

  /* copy the cursor so if there is a depth change, we have the cursor
     data around to reset the cursor; a pain in the butt */
  if (current_ccrsr != ccrsr)
    {
      int data_size;
    
      if (!current_ccrsr)
	{
	  ZONE_SAVE_EXCURSION
	    (SysZone,
	     {
	       current_ccrsr = (CCrsrHandle) NewHandle (sizeof (CCrsr));
	       CCRSR_DATA_X (current_ccrsr) = RM (NewHandle (0));
	       CCRSR_XDATA_X (current_ccrsr) = RM (NULL);
	       CCRSR_MAP_X (current_ccrsr) = RM (NewPixMap ());
	     });
	}
    
      /* copy the cursor structure */
      CCRSR_TYPE_X (current_ccrsr) = CCRSR_TYPE_X (ccrsr);
      memcpy (CCRSR_1DATA (current_ccrsr), CCRSR_1DATA (ccrsr),
	      sizeof (Bits16));
      memcpy (CCRSR_MASK (current_ccrsr), CCRSR_MASK (ccrsr),
	      sizeof (Bits16));
      CCRSR_HOT_SPOT (current_ccrsr) = CCRSR_HOT_SPOT (ccrsr);
    
      /* copy the pixmap */
      CopyPixMap (CCRSR_MAP (ccrsr), CCRSR_MAP (current_ccrsr));
    
      /* copy the cursor data */
      data_size = GetHandleSize (CCRSR_DATA (ccrsr));
      SetHandleSize (CCRSR_DATA (current_ccrsr), data_size);
      memcpy (STARH (CCRSR_DATA (current_ccrsr)),
	      STARH (CCRSR_DATA (ccrsr)), data_size);
    
      /* invalidate this cursor */
      CCRSR_XVALID_X (current_ccrsr) = CWC (0);
      CCRSR_ID_X (current_ccrsr) = CLC (-1);
      
      current_cursor_valid_p = TRUE;
      current_cursor_color_p = TRUE;
    }
}

P1 (PUBLIC pascal trap, void, DisposCCursor,
    CCrsrHandle, ccrsr)
{
/* #warning "implement DisposCCursor" */
  warning_unimplemented (NULL_STRING);
}

P0 (PUBLIC pascal trap, void, AllocCursor)
{
  /* This function is a NOP for us as far as I know. */
  warning_unexpected ("AllocCursor called -- this is unusual");
}
