/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:apa8loc.c 12.0$ */
/* $ACIS:apa8loc.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/cacons/RCS/apa8loc.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:apa8loc.c 12.0$";
#endif

/*
 * Written by Daniel Stone, November 11, 1985.
 */

#include "apaeight.h"
#if NAPAEIGHT > 0

#ifdef SYS5
 #include "screen_conf.h"
 #include "apaEtty.h"
#else
#include "../machinecons/screen_conf.h"
#include "../machinecons/apa8tty.h"
#endif

#ifdef DEBUG
int loc8_debug = 0;
#endif

#ifdef USR
extern int loc8_debug;
typedef QIOLocator Locator;
#endif USR

/*
 * Masks used to write the locator on the screen.
 */
static unsigned short leftMasks[] = {
	0xffff,0x7fff,0x3fff,0x1fff,0x0fff,0x07ff,0x03ff,0x01ff,
	0x00ff,0x007f,0x003f,0x001f,0x000f,0x0007,0x0003,0x0001,
	0x0000
};

static unsigned short rightMasks[] = {
	0xffff,0x8000,0xc000,0xe000,0xf000,0xf800,0xfc00,0xfe00,
	0xff00,0xff80,0xffc0,0xffe0,0xfff0,0xfff8,0xfffc,0xfffe,
	0x0000
};

#define BPL		16	/* Bit Per Locator */
#define LOG2_BPL	4	/* for quicker math with BPL	*/
#define MOD_BPL(value)	((value) & (BPL-1))
#define DIV_BPL(value)	((value) >> LOG2_BPL)
#define MUL_BPL(value)	((value) << LOG2_BPL)

/*
 * Another (possibly clearer) way to express the width and height in bits
 * and scanlines of the locator device.
 */
#define LOC_HT	BPL
#define LOC_WD	BPL

/*
 * Constants for how to write the locator on the screen.
 */
#define LOCATOR_SKEW 0
#define LOCATOR_NO_SKEW 1
#define LOCATOR_CLIPPED_LEFT 2
#define LOCATOR_CLIPPED_RIGHT 3

#define SAVE_1	1
#define SAVE_2	2

static Blt_Rectangle critical_rect; /* Temporary rectangle passed to 
				       apa8_loc_in_bounds. */

static short loc_hidden;/* Indicates whether or not the locator is logically
			   on the screen or off of it. */
static short on_screen; /* Indicates whether or not the locator is
			   physically on the screen or not. */

static long x_hotspot,y_hotspot; /* Previous hot spot given */
static Locator cur_locator;	 /* The current locator data, mask and
				    hotspots */
static unsigned short screen_data[LOC_HT*SAVE_2]; /* Place where the screen
						     under the locator is
						     saved. */
static unsigned short *locator_p;/* Pointer to the upper left corner of the
				    locator spot. */

/*
 * These variables are used to restore the screen.
 */
static short shorts_saved;	/* The number of shorts per scanline on the
				   screen saved. Its either 1 or 2. */
static short restore_ht;	/* height (in scanlines) of the area under
				  the locator. */

/*
 * Initialize the locator to be invisible.  This should be called from
 * the emulator initialize routines.  It assumes that there is an APA-8
 * screen out there.
 */
apa8_init_loc()
{
	register i;

	DEBUGF((loc8_debug > 3),printf("Enter apa8_init_loc\n"));

	/*
	 * Initialize all internal variables.
	 */
	loc_hidden = FALSE;
	on_screen = FALSE;

	/*
	 * Set up so that if the locator is shown before it is loaded with any
	 * real values, it is invisible. (This may not be a good idea.)
	 */
	x_hotspot = 0;
	y_hotspot = 0;
	restore_ht = 0;
	shorts_saved = 0;

	/*
	 * Zero hotspot
	 */
	cur_locator.hotSpot.h = 0;
	cur_locator.hotSpot.v  = 0;
 
	/*
	 * Clear locator data.
	 */
	for (i = 0; i < BPL; i++)
	        cur_locator.data[i] = 0;
 
	/*
	 * Clear locator mask.
	 */
	for (i = 0; i < BPL; i++)
	        cur_locator.mask[i] = 0;
}

/*
 * Restore the spot on the screen where the locator was and turn the loc_hidden
 * flag off.
 */
apa8_remove_locator()
{
	register unsigned short *src,*dst;
	register i;

	DEBUGF((loc8_debug > 3),printf("Enter apa8_remove_locator\n"));

	DEBUGF((loc8_debug > 2),
		printf("restore_screen: on:%d h,s:%d,%d d:%x\r\n",loc_hidden,
			restore_ht,shorts_saved,(long)locator_p));

	if (on_screen == FALSE) {
		/*
		 * The locator is not on the screen... nothing to do.
		 */
		return;
	}

	/*
	 * The locator is no longer on the screen.(soon)
	 */
	on_screen = FALSE;

	src = (unsigned short *)screen_data;
	dst = (unsigned short *)locator_p;

	/*
	 * To remove the locator use the same mode as used to write
	 * the locator.
	 */
	if (shorts_saved == SAVE_2) {
		/*
		 * The cursor was situated on 2 words.
		 * NOTE: the +=2 because of the APA-8 addressing.
		 */
		for (i = restore_ht + 1; --i;) {
			*dst = *src++;
			dst += 2;
			*dst = *src++;
			dst += (MUL_2(BITMAP_WORD_WD - 1));
		}
	}
	else {
		for (i = restore_ht + 1; --i;) {
			*dst = *src++;
			dst += MUL_2(BITMAP_WORD_WD);
		}
	}
}

/*
 * Apa8_show_loc actually puts the current locator onto the screen.
 * To make it look like the APA-16, the typical mac like formula of
 * "dst = ((dst & ~mask) ^ locator)" was changed to
 * "dst = ((~dst & ~mask) ^ ~locator)".
 * This routine changes the on_screen flag if the locator is put on the
 * screen.
 *
 * NOTE: We assume that x_hotspot and y_hotspot have been clipped to the
 *	 screen.
 */
apa8_put_locator(ms_info)
MSBox *ms_info;
{
	register unsigned short *data,*mask,*dst;/* pointers to source (data,
						    and mask) and destination */
	register i;		/* counter for all loops */
	register short skew;	/* amount to shift the locator data and mask */
	register long x,y;	/* Point of locator on the screen */ 
	register long clipped_ht;/* offset in locator */
	register long clipped_wd;/* actual width of the locator */
	short mode;		 /* Indicates how to write the locator on the
				   screen. */
	unsigned short leftmask; /* mask for the left edge of the locator */
	unsigned short rtmask;	 /* mask for the right edge of the locator */

	DEBUGF((loc8_debug > 3),printf("Enter apa8_put_locator\n"));

	/*
	 * The locator may now be in the critical area represented
	 * by the data in ms_info.
	 */
	if (ms_info && (ms_info->flags)) {
		critical_rect.origin_x = ms_info->left;
		critical_rect.origin_y = ms_info->top;
		critical_rect.corner_x = ms_info->right;
		critical_rect.corner_y = ms_info->bottom;
		if (apa8_loc_in_bounds(&critical_rect) == TRUE) {
			return;
		}
	}

	if (on_screen == TRUE) {
		/*
		 * The locator is still physically on the screen.
		 */
		return;
	}

	/*
	 * The locator is on the screen. (soon.)
	 */
	on_screen = TRUE;

	/*
	 * Default is the height was not clipped.
	 */
	clipped_ht = 0;

	/*
	 * Size of the locator before clipping.
	 */
	restore_ht = LOC_HT;
	clipped_wd = LOC_WD;

	/*
	 * Clip x coordinate, set the mode,skew and width.
	 */
	if ((x = (x_hotspot - cur_locator.hotSpot.h)) < 0) {
		clipped_wd -= (-x);
		x = 0;
		skew = BPW - clipped_wd;

		/*
		 * Only need to save 1 short per scanline because the locator
		 * is only going to overlay 1.
		 */
		shorts_saved = SAVE_1;
		mode = LOCATOR_CLIPPED_LEFT;
	}
	else {
		if (x > (SCREEN_WD - LOC_WD)){
			/*
			 * Shorten the right side.
			 */
			clipped_wd -= (x - (SCREEN_WD - LOC_WD));

			/*
			 * Only need to save one short per scanline on the
			 * screen.
			 */
			shorts_saved = SAVE_1;
			skew = BPW - clipped_wd;
			mode = LOCATOR_CLIPPED_RIGHT;
		}
		else if ((skew = MOD_BPW(x))) {
			/*
			 * Two words per scanline will be destroyed by the
			 * locator so indicate the 2 must be saved.
			 */
			shorts_saved = SAVE_2;

			mode = LOCATOR_SKEW;
		}
		else {
			/*
			 * Only need to save one short per scanline on the
			 * screen.
			 */
			shorts_saved = SAVE_1;
			mode = LOCATOR_NO_SKEW;
		}
	}

	/*
	 * Clip y coordinate, set the height.
	 */
	if ((y = (y_hotspot - cur_locator.hotSpot.v)) < 0){
		clipped_ht = -y;
		restore_ht -= clipped_ht;
		y = 0;
	}
	else if (y > (SCREEN_HT - LOC_HT))
		restore_ht -= (y - (SCREEN_HT - LOC_HT));

	/*
	 * NOTE: If clipped_ht is too big then restore_ht will be
	 *	 negative and all these loops with --i will screw
	 * 	 up. So we garrentee that restore_ht is > -1.
	 *
	 *	 THIS SHOULD NEVER HAPPEN IF x_hotspot AND y_hotspot
	 * 	 ARE CLIPPED TO THE SCREEN.
	 * 
	 * However clipped_wd could equal zero if the hotspot was 16
	 * and y_hotspot was zero.  Anyway, in this case the locator is
	 * no longer on the screen so indicate that.
	 */
	if (clipped_wd <= 0 || restore_ht <= 0) {
		/*
		 * For safty I zero restore_ht because it drives all the loops
		 * that save and restore the screen spot.
		 */
		restore_ht = 0;
		on_screen = FALSE;
		return;
	}

	/*
	 * Calculate the locator pointer which points to the upper left
	 * corner of the spot on the screen where the currsor will be placed.
	 */
	locator_p = (unsigned short *) ((long)APA8BASE + 
				        MUL_2(MUL_2((y * BITMAP_WORD_WD) +
  	  			        DIV_BPW(x))));

	/*
	 * Save the spot on the screen where the locator will be written.
	 * NOTE: We are saving on a short (not bit) boundary.
	 */
	dst = screen_data;
	data = locator_p;
	if (shorts_saved == SAVE_2) {
		for (i = restore_ht + 1; --i;) {
			*dst++ = *data;
			data += 2;
			*dst++ = *data;
			data += MUL_2(BITMAP_WORD_WD - 1);
		}
	}
	else {
		/*
		 * Keep track of the width of the area saved in bits.
		 */
		for (i = restore_ht + 1; --i;) {
			*dst++ = *data;
			data += MUL_2(BITMAP_WORD_WD);
		}
	}

	/*
	 * Put the locator on the screen.
	 */
	data = (unsigned short *) &(cur_locator.data[clipped_ht]);
	mask = (unsigned short *) &(cur_locator.mask[clipped_ht]);
	dst = locator_p; 

	/*
	 * Depending on the mode, write the locator to the screen.
	 */
	switch (mode) {
	   case LOCATOR_SKEW:
		leftmask = leftMasks[MOD_BPW(x)];
		rtmask = rightMasks[MOD_BPW(x+clipped_wd)];

		/*
		 * NOTE: To get the locator to look like the APA-16, the
		 *	 dst and data are inverted.
		 */
	    	for (i = restore_ht + 1; --i;) {
#ifdef APA8_REVERSE_VIDEO
	       		*dst = (*dst & ~leftmask) | (((~*dst & ~(*mask>>skew)) ^
			       (~*data >> skew)) & leftmask);
			dst += 2;
	        	*dst = (*dst & ~rtmask) |
			       (((~*dst & ~(*mask << (BPW-skew))) ^
			       (~*data << (BPW-skew))) & rtmask); 
#else
	       		*dst = (*dst & ~leftmask) | (((*dst & ~(*mask>>skew)) ^
			       (*data >> skew)) & leftmask);
			dst += 2;
	        	*dst = (*dst & ~rtmask) |
			       (((*dst & ~(*mask << (BPW-skew))) ^
			       (*data << (BPW-skew))) & rtmask); 
#endif
			dst += MUL_2(BITMAP_WORD_WD-1);
			data++;
			mask++;
		}
		break;

	   case LOCATOR_NO_SKEW:
	    	for (i = restore_ht + 1; --i;) {
#ifdef APA8_REVERSE_VIDEO
			*dst = ((~*dst & ~*mask) ^ ~*data); 
#else
			*dst = ((*dst & ~*mask) ^ *data); 
#endif
			dst += MUL_2(BITMAP_WORD_WD);
			data++;
			mask++;
	    	}
		break;

	   case LOCATOR_CLIPPED_LEFT:
		rtmask = rightMasks[MOD_BPW(x+clipped_wd)];
	    	for (i = restore_ht + 1; --i;) {
#ifdef APA8_REVERSE_VIDEO
	  		*dst = (*dst & ~rtmask) | (((~*dst & ~(*mask << skew)) ^
			       (~*data << skew)) & rtmask);
#else
	  		*dst = (*dst & ~rtmask) | (((*dst & ~(*mask << skew)) ^
			       (*data << skew)) & rtmask);
#endif
			dst += MUL_2(BITMAP_WORD_WD);
			data++;
			mask++;
	   	}
		break;

	   case LOCATOR_CLIPPED_RIGHT:
		leftmask = leftMasks[MOD_BPW(x)];
	    	for (i = restore_ht + 1; --i;) {
#ifdef APA8_REVERSE_VIDEO
	       		*dst = (*dst & ~leftmask) | 
			       (((~*dst & ~(*mask >> skew)) ^
			       (~*data >> skew)) & leftmask); 
#else
	       		*dst = (*dst & ~leftmask) | 
			       (((*dst & ~(*mask >> skew)) ^
			       (*data >> skew)) & leftmask); 
#endif
			dst += MUL_2(BITMAP_WORD_WD);
			data++;
			mask++;
	    	}
		break;
	}

	DEBUGF((loc8_debug > 2),
		printf("locator put at x,y(%d,%d) x,y hot:%d,%d\r\n",x,y,
			x_hotspot,y_hotspot));
	DEBUGF((loc8_debug > 2),
		printf("ptr:%x clipped:%d  w,h,s:%d,%d,%d\r\n",(long)locator_p,
			clipped_ht,restore_ht,shorts_saved));
}

/*
 * Change the system locator to look like the one passed in.
 */
apa8_load_loc(new_loc,ms_info)
register Locator *new_loc;
MSBox *ms_info;
{
	register unsigned short *src, *dst;
	register i;

	DEBUGF((loc8_debug > 3),printf("Enter apa8_load_loc\n"));

	/*
	 * Copy hotspot
	 */
	cur_locator.hotSpot.h = new_loc->hotSpot.h;
	cur_locator.hotSpot.v  = new_loc->hotSpot.v;

	/*
	 * Copy locator data
	 */
	src = (unsigned short *) new_loc->data;
	dst = (unsigned short *) cur_locator.data;

	for (i = BPL + 1; --i; )
		*dst++ = *src++;

	/*
	 * Copy locator mask
	 */
	src = (unsigned short *) new_loc->mask;
	dst = (unsigned short *) cur_locator.mask;

	for (i = BPL + 1; --i; )
		*dst++ = *src++;

	if (loc_hidden == FALSE) {
		/*
		 * If the old locator is on the screen take it off and
		 * put the new one on the screen if the locator is not
		 * to be hidden.
		 */
		apa8_remove_locator();

		/*
		 * Put the locator onto the screen.
		 */
		apa8_put_locator(ms_info);
	}
}

apa8_hide_loc()
{
	if (loc_hidden == FALSE) {
		loc_hidden = TRUE;
		apa8_remove_locator();
	}
}

apa8_show_loc(ms_info)
MSBox *ms_info;
{
	if (loc_hidden == TRUE) {
		loc_hidden = FALSE;
		apa8_put_locator(ms_info);
	}
}

apa8_pos_loc(x,y,ms_info)
register long x,y;
MSBox *ms_info;
{
	DEBUGF((loc8_debug > 3),printf("Enter apa8_pos_loc\n"));

	/*
	 * If the locator is not on the screen OR the locator has moved then
	 */
	if (loc_hidden == TRUE) {
		/*
		 * Fill in the new hotspots.
		 */
		x_hotspot = x;
		y_hotspot = y;
	}
	else if (x_hotspot != x || y_hotspot != y) {
		/*
		 * Fill in the new hotspots.
		 */
		x_hotspot = x;
		y_hotspot = y;

		/*
		 * Restore the screen where the locator is on it.
		 */
		apa8_remove_locator();
	
		/*
		 * Put the locator onto the screen.
		 */
		apa8_put_locator(ms_info);
	}
}

/*
 * If the area saved when the locator was put on the screen is within
 * these bounds return TRUE.  (NOTE: These bounds are open corners.)
 * If the locator is within the bounds given in 'b' then return TRUE (non-zero)
 * otherwise return FALSE (0).
 */
apa8_loc_in_bounds(b)
register Blt_Rectangle *b;  /* Rectangle representing the critical
			       area of the screen */
{
	register short x_origin,y_origin; /* saved area's top left corner */
	register short x_corner,y_corner; /* saved area's bottom right corner */

	DEBUGF((loc8_debug > 3),printf("Enter apa8_loc_in_bounds\n"));

	/*
	 * First calculate the "locator area" rectangle.  This rectangle
	 * is based on the current screen hotspot and the current locator
	 * hotspot.  The x_origin always resides on a word boundary.
	 * This locator area rectangle is the area that will be saved (or
	 * is already saved) by the apa8_put_locator routine.
	 *
	 * THIS MEANS THIS SECTION OF CODE ACTS LIKE THAT SECTION OF CODE
	 * IN APA8_SHOW_LOC.  IF APA8_SHOW_LOC IS CHANGED THEN THIS
	 * WILL NOT WORK!!!
	 *
	 * After calculating the x_origin and testing it, round it off to
	 * a word boundary and calculate the x_corner for the locator area.
	 */
	if ((x_origin = (x_hotspot - cur_locator.hotSpot.h)) < 0) {
		x_origin = 0;
		x_corner = BPW;
	}
	else {
		if (x_origin > (SCREEN_WD - BPW)){
			x_origin &= (~(BPW-1));
			x_corner = x_origin + BPW;
		}
		else if ((MOD_BPW(x_origin))) {
			/*
			 * Two words per scanline will be destroyed by the
			 * locator.
			 */
			x_origin &= (~(BPW-1));
			x_corner = x_origin + (2*BPW);
		}
		else {
			/*
			 * x_origin already falls on a word boundary.
			 */
			x_corner = x_origin + BPW;
		}
	}

	/*
	 * Calculate the y_origin and y_corner of the locator area.
	 */
	if ((y_origin = (y_hotspot - cur_locator.hotSpot.v)) < 0) {
		y_corner = BPW + y_origin; /* NOTE: y_origin is negative. */
		y_origin = 0;
	}
	else if (y_origin > (SCREEN_HT - LOC_HT)) {
		y_corner = SCREEN_HT;
	}
	else {
		y_corner = y_origin + LOC_HT;
	}

	if (y_corner <= 0) {
		/*
		 * The locator is not on the screen, so it can not be in
		 * the way.  This should never happen unless someone defines
		 * a hotSpot.v to be 16.
		 */
		return(FALSE);
	}

	/*
	 * If the given rectangle is smaller than the locator area, we
	 * will have a problem if we are checking to see if the locator
	 * area is within the given rectangle so we check the width of both
	 * and then check to see if the smaller area is in the larger
	 * area.
	 */
	if ((x_corner - x_origin) > (b->corner_x - b->origin_x)) {
		/*
		 * The locator area saved is larger than the blt area.
		 * Check to see if the given rectangle is within
		 * the saved area.
		 */
		if ((x_origin < b->corner_x && b->corner_x <= x_corner) ||
		   (x_origin <= b->origin_x && b->origin_x < x_corner)) {
			/*
			 * The part of the saved area resides in the blt
			 * rectangle.  Check the Y coordinates.
			 */
			if ((y_corner - y_origin)>(b->corner_y - b->origin_y)) {
				/*
				 * The locator area saved is larger than the
				 * blt area. Check to see if the given
				 * rectangle is within the saved area.
				 */
				if ((y_origin < b->corner_y &&
				    b->corner_y <= y_corner) ||
				    (y_origin <= b->origin_y &&
				    b->origin_y < y_corner)) {
					return(TRUE);
				}
			}
			else {
				/*
				 * The box being blt is larger than the locator
				 * area saved. Check to see if the saved
				 * area is within the given rectangle.
				 */
				if ((b->origin_y < y_corner &&
				     y_corner <= b->corner_y) ||
				    (b->origin_y <= y_origin &&
				     y_origin < b->corner_y)) {
					return(TRUE);
				}
			}
		}
	}
	else {
		/*
		 * The box being blt is larger (in X coordinates) than the
		 * locator saved area. Check to see if the saved area is within
		 * the given rectangle.
		 */
		if ((b->origin_x < x_corner && x_corner <= b->corner_x) ||
		    (b->origin_x <= x_origin && x_origin < b->corner_x)) {
			/*
			 * The saved area resides inside the X coord. blt
			 * rectangle check the Y coord.
			 */
			if ((y_corner - y_origin)>(b->corner_y - b->origin_y)) {
				/*
				 * The saved area has a larger y than the
				 * blt rectangle. Check to see if the given
				 * rectangle is within the saved area.
				 */
				if ((y_origin < b->corner_y &&
				    b->corner_y <= y_corner) ||
				     (y_origin<=b->origin_y &&
				    b->origin_y<y_corner)) {
					return(TRUE);
				}
			}
			else {
				/*
				 * Check to see if the saved area is within
				 * the given rectangle.
				 */
				if ((b->origin_y < y_corner &&
				     y_corner <= b->corner_y) ||
				    (b->origin_y <= y_origin &&
				     y_origin < b->corner_y)) {
					return(TRUE);
				}
			}
		}
	}

	return(FALSE);
}
#endif NAPAETTY
