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

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

/*
 * Written by Daniel Stone, November 11, 1985.
 *
 * This file contains all the routines written for the APA-16 hardware locator.
 * These routines initialize the locator device, position the locator device,
 * load a new locator device, hide and show the locator,obscure the locator 
 * and check to see if the locator is within a specified area.  This last
 * routine is only needed if the locator is done in software.
 */

#include "apasixteen.h"
#if NAPASIXTEEN > 0

/*
 * Define Bits Per Locator.  This is the width and height of the current
 * locator device.
 */
#define BPL	16

#define SCREEN_BYTE_WD	DIV_BPB(SCREEN_WD)

#define LOCATOR_INVISIBLE { *Y_LOCATOR_R = 1023; }

#define US_NIL ((unsigned short *)0)

/*
 * Constants for how to write the locator on the screen.
 */
#define LOCATOR_NEW 1
#define LOCATOR_CLIPPED_LEFT 2
#define LOCATOR_CLIPPED_RIGHT 3
#define LOCATOR_NORMAL 4

#define BIT_AND_LOCATOR	(unsigned short *)(AND_LOCATOR + 		\
				((HARD_LOCATOR_HT - BPL)*SCREEN_BYTE_WD))
#define BIT_XOR_LOCATOR	(unsigned short *)(XOR_LOCATOR + 		\
				((HARD_LOCATOR_HT - BPL)*SCREEN_BYTE_WD))

/*
 * Place on the APA-16's memory where we will save the current locator.
 */
#define SAVE_BIT_AND_LOCATOR (unsigned short *)(SAVE_AND_LOCATOR + \
                                   ((HARD_LOCATOR_HT - BPL) * SCREEN_BYTE_WD))
#define SAVE_BIT_XOR_LOCATOR (unsigned short *)(SAVE_XOR_LOCATOR + \
                                   ((HARD_LOCATOR_HT - BPL) * SCREEN_BYTE_WD))

#include "../machinecons/screen_conf.h"
#include "../machinecons/apa16tty.h"

#ifdef USR
typedef QIOLocator Locator;
#endif USR

#ifdef DEBUG
int loc16_debug = 0;
#endif

/*
 * Indicate whether or not the hardware locator is being used.
 */
short apa16locator_busy = FALSE;

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

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

static short on_screen; /* Indicates whether or not the locator is on the
			   screen or off of it. */
static short loc_hidden; /* Indicates that the user has requested that the
			    locator be removed from the screen. */

/*
 * NOTE: The difference between on_screen and loc_hidden is that on_screen
 *	 represents the physical locator and indicates whether or not it is
 *	 actually on the screen or not.  loc_hidden indicates the user's
 *	 view of the locator.
 */

static short prev_mode; /* Indicates the previous mode the locator was written
			   in. */
static long x_hotspot,y_hotspot; /* Previous hot spot given */
static Locator cur_locator;	 /* The current locator data, mask and
				    hotspots */

/*
 * Structure found in apaStty.c.  It contains information about how the
 * APA-16's queue has been set up.
 */
extern QUEinfo QUEinstr[];

/*
 * Initialize so that it is invisible.  This routine should be called by
 * the APA-16 initialize emulator routine.
 */
apa16_init_loc()
{
	register int i;

	/*
	 * Hopefully the queue on the APA16 has been initialized when the 
	 * system came came up.  If not, well thats life.
	 */ 

	/*
	 * Set the AND area to one's and the XOR area to zero's this provides
	 * a transparent locator.
	 */
	DO_QUE_CMD(Q_SETAND);
	DO_QUE_CMD(Q_CLEARXOR);

	/*
	 * Get the locator off the screen.
	 */
	on_screen = FALSE;
	LOCATOR_INVISIBLE;

	/*
	 * Initialize the loc_hidden flag to FALSE to show that the user has
	 * not requested that the locator be off the screen.
	 */
	loc_hidden = FALSE;

	/*
	 * Initialize the internal variables.
	 */
	x_hotspot = 0;
	y_hotspot = 0;

	/*
	 * 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.)
	 */
	prev_mode = LOCATOR_NORMAL;

	/*
	 * 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;

	/*
	 * Set locator mask.
	 */
	for (i = 0; i < BPL; i++)
		cur_locator.mask[i] = 0xffff;
}

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

	/*
	 * 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 = 0; i < BPL; i++)
		*dst++ = *src++;

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

	for (i = 0; i < BPL; i++)
		*dst++ = ~(*src++);

	/*
	 * Indicate a new locator has been put into the data and mask
	 * sections of the current locator.
	 */
	prev_mode = LOCATOR_NEW;

	/*
	 * Take the locator off the screen while its being changed by
	 * the hardware.
	 */
	if (on_screen) {
	    on_screen = FALSE;
	    LOCATOR_INVISIBLE;
	}

	/*
	 * Put the locator into the hardware area. (on the same spot
	 * on the screen)
	 */
	apa16_pos_loc(x_hotspot,y_hotspot);
}

/*
 * If the locator is on the screen hide it.
 */
apa16_hide_loc()
{
	if (on_screen) {
		on_screen = FALSE;
		loc_hidden = TRUE;
		LOCATOR_INVISIBLE;
	}
}

/*
 * If the locator is off the screen, show it.
 */ 
apa16_show_loc()
{
	if (loc_hidden) {
		loc_hidden = FALSE;
		apa16_pos_loc(x_hotspot,y_hotspot);
	}
}

/*
 * This routine does some interesting things to get around hardware problems
 * (or "features"). First, some background information: I always load my 
 * 16x16 bit locator at the bottom right corner of the hardware locator area.
 * The hot spot of the locator is always on top of the logical coordinates
 * given, so if 2,2 is given the hot spot of the locator should be on that
 * spot.  Let's say 2,2 is the logical coordinate given and the locator's 
 * hot spot is 1,1 and for the time being lets say the locator is only 3 bits
 * by 3 bits.  The hardware is different because it always places the 
 * BOTTOM RIGHT corner of the locator on the spot in its x,y registers.
 * I must convert the logical coordinates into hardware coordinates. Note
 * also that the logical coordinates are in the "Mac" type coordinate system
 * where pixels are BETWEEN infinitely thin lines and the hardware coordinates
 * are in the system where 1,1 IS the pixel.  To do
 * this I set:
 *
 * 	x register = logical x - locator hot spot x
 *	y register = logical y + (Bits Per Locator - locator hot spot y) - 1
 *
 * Using the above example I would load 1,3 into the x,y registers.
 * This works until a logical coordinate like 0,0 is given.  I don't have to
 * worry about the y coordinate, the hardware takes care of it.  However 
 * the x coordinate in this example would be negative 1.  This is no good
 * because the range of the registers is 0 to 1024.  Instead I must clip
 * the locator by moving it in the two hardware areas.  In this case I would
 * shift it over by one.  Another problem occurs when a value over (1024 - 48)
 * is put in the x register, the hardware goes nuts and puts crap on the
 * screen. To get by this problem, I just shift the locator over to the right.
 *
 * NOTE on X,Y registers of the APA-16's hardware locator:
 * The point put in the X,Y registers (say it's 9,11) is based on a ZERO
 * based system.  The hardware takes the bottom right corner of the
 * combination of two hardware locator areas, one at 847,0 inclusive 
 * (the AND_AREA) and the other at 847,48 inclusive (the XOR_AREA), and puts 
 * it at the 9th bit (the 10th bit in a system where the 1st bit is one),
 * 11th scan line (the 12th scan line if the 1st scan line is one).
 * The 2 areas are combined like this, the destination is and'ed with the
 * bits in the AND_AREA and this result is exclusively or'ed with the
 * coorisponding bits in the XOR_AREA.
 *
 * 	dst = ((dst & AND_AREA) ^ XOR_AREA)
 * 
 * White in the mask means the coorisponding spots in the XOR_AREA will show
 * through.
 * NOTE: This routine depends on the following globals:
 *	 x_hotspot,y_hotspot - Logical x,y coordinates on the screen.
 *	 cur_locator - Contains the data,mask and hotspot for the current
 *		       locator.
 *	 prev_mode - The mode of the previous locator written out.
 */
static
apa16_put_loc(X_reg,Y_reg)
unsigned short *X_reg,*Y_reg; /* Pointers to shorts which are filled with 
				 the values that should be put into the x 
				 and y locator registers to make the locator
				 move. */
{
	register unsigned short *data,*dst; /* pointers to data in memory
					       for the locator and the spot
					       in the hardware locator area */
	register mode,skew;
	register int i;
	register short word_off;
	short x,y;
	int width, Xorigin;

	/*
	 * Indicates whether or not the locator must be clipped.
	 */
	mode = LOCATOR_NORMAL;

	/*
	 * Width of the locator is Bits Per Locator.
	 */
	width = BPL;

	/*
	 * Hardware locator is usually put on the left side of the hardware
	 * locator area.
	 */
	Xorigin = 0;

	/*
	 * Clip X, APA-16 expects x to be the left side of the locator.
	 */
	if ((x = (x_hotspot - cur_locator.hotSpot.h)) < 0) {
		/*
		 * Locator must be shifted to left.
		 */
		width -= (-x);
		x = 0;
		skew = BPW - width;
		mode = LOCATOR_CLIPPED_LEFT;
	}
	else if (x > (SCREEN_WD - HARD_LOCATOR_WD)) {
		/*
		 * Must move the locator and possibly shorten right side.
		 */
		Xorigin = (x - (SCREEN_WD - HARD_LOCATOR_WD));
		if (x > (SCREEN_WD - BPL)) {
			width -= (x - (SCREEN_WD - BPL));
		}
		x = SCREEN_WD - HARD_LOCATOR_WD;
		skew = MOD_BPW(Xorigin);
		word_off = DIV_BPW(Xorigin);
		mode = LOCATOR_CLIPPED_RIGHT;
	}

	/*
	 * Clip y, APA-16 expects y to be the BOTTOM INCLUSIVE. Both systems
	 * (the APA-16 hardware where the BIT is one and the mac model where
	 * there are infinitely small lines and a BIT is one plus 1/2) are zero
	 * based but because the APA-16 is inclusive and the Mac model is
	 * exclusive, we must subtract 1.
	 */
	if ((y = (y_hotspot+(BPL - cur_locator.hotSpot.v) - 1)) < 0) {
		y = 0;
	}
	else if (y > (SCREEN_HT + HARD_LOCATOR_HT)) {
		y = SCREEN_HT + HARD_LOCATOR_HT;
	}

	/*
	 * If we have a new locator then clear the areas first.
	 */
	if (prev_mode == LOCATOR_NEW) {
		DO_QUE_CMD(Q_SETAND);
	   	DO_QUE_CMD(Q_CLEARXOR);

		/*
		 * Wait for the queue command to be finished because 
		 * we are then going to write on the areas just cleared.
		 */
		WAIT_QUE(Q_CLEARXOR);
	}
	
	switch (mode) {
	   case LOCATOR_NORMAL:
		/*
		 * If the locator has been changed in some way put
		 * it back to normal.
		 */
		if (prev_mode != LOCATOR_NORMAL) {
			/*
		         * If the origin of the locator in the hardware locator
		         * area WAS moved then clear both areas so as to start
		         * with a fresh area.
		         */
			if (prev_mode == LOCATOR_CLIPPED_RIGHT) {
	   	        	DO_QUE_CMD(Q_SETAND);
	   	        	DO_QUE_CMD(Q_CLEARXOR);

				/*
				 * Wait for the queue command to be finished
				 * because we are then going to write on the
				 * areas just cleared.
				 */
			        WAIT_QUE(Q_CLEARXOR);
		        }

		        /*
		         * Copy locator data
		         */
		        data = (unsigned short *) cur_locator.data;
		        dst = BIT_XOR_LOCATOR;
	
			for (i = BPL + 1; --i; dst += SCREEN_WORD_WD)
				*dst = *data++;

		        /*
		         * Copy locator mask
		         */
		        data = (unsigned short *) cur_locator.mask;
		        dst = BIT_AND_LOCATOR;

			for (i = BPL + 1; --i; dst += SCREEN_WORD_WD)
		        	*dst = *data++;
		}
		break;

	   case LOCATOR_CLIPPED_LEFT:
		/*
		 * If the origin of the locator in the hardware locator
		 * area WAS moved then clear both areas so as to start
		 * with a fresh area.
		 */
		if (prev_mode == LOCATOR_CLIPPED_RIGHT) {
	   		DO_QUE_CMD(Q_SETAND);
	   	        DO_QUE_CMD(Q_CLEARXOR);

			/*
		         * Wait for the queue command to be finished because
			 * we are then going to write on the areas just
			 * cleared.
		         */
		        WAIT_QUE(Q_CLEARXOR);
		}

		/*
		 * Take care of the And area. Set those areas left blank
		 * by the shift.
		 */
		dst = BIT_AND_LOCATOR;
		data = (unsigned short *)cur_locator.mask;
		for (i = BPL + 1; --i; dst += SCREEN_WORD_WD)
			*dst = (*data++ << skew) ^ locatorRtMasks[skew];

		/*
		 * Take care of the Xor area.
		 */
		dst = BIT_XOR_LOCATOR;
		data = (unsigned short *)cur_locator.data;
		for (i = BPL + 1; --i; dst += SCREEN_WORD_WD)
			*dst = *data++ << skew;

		break;

	   case LOCATOR_CLIPPED_RIGHT:
		/*
		 * Clear the hardware locator area.
		 */
	        DO_QUE_CMD(Q_SETAND);
	        DO_QUE_CMD(Q_CLEARXOR);

		/*
		 * Wait for the queue command to be finished because we are
		 * then going to write on the areas just cleared.
		 */
		WAIT_QUE(Q_CLEARXOR);

		/*
		 * If the locator has disappeared then don't do anything
		 * otherwise move it in the hardware locator area.  This
		 * is done because the hardware does some goof ball things
		 * if the locator gets within 48 pels of the edge.
		 */
#ifdef DEBUG
		if (loc16_debug) {
			printf("word_off:%d skew:%d Xorigin:%d\n",word_off,skew,
				Xorigin);
		}
#endif
		if (word_off < DIV_BPW(HARD_LOCATOR_WD)) {
			/*
		         * Take care of the And area. Set those areas left
			 * blank by the shift.
		         */
		        dst = (unsigned short *)((int)BIT_AND_LOCATOR +
						 MUL_2(word_off));
		        data = (unsigned short *)cur_locator.mask;

			/*
			 * The destination may have one or TWO words to be
			 * changed, the skew indicates this.
			 */
			if (skew != 0) {
		        	for (i = BPL+1; --i; dst+=(SCREEN_WORD_WD-1)) {
					*dst++ = (*data >> skew ) ^
						 locatorLfMasks[skew];
					/*
					 * If the width of the locator is
					 * less then the width of a normal
					 * locator then the locator has been
					 * clipped and we do not have to write
					 * the second half of the locator to
					 * the screen.
					 */
					if (width == BPL) {
						*dst = (*data << (BPL-skew)) ^
						       locatorRtMasks[BPL-skew];
					}
					data++;
				}
			}
			else {
		        	for (i = BPL + 1; --i; dst += SCREEN_WORD_WD)
					*dst = *data++ ^ locatorRtMasks[skew];
			}

		        /*
		         * Take care of the Xor area.
		         */
		        dst = (unsigned short *)((int)BIT_XOR_LOCATOR +
						 MUL_2(word_off));
		        data = (unsigned short *)cur_locator.data;

			/*
			 * The destination may have one or TWO words to be
			 * changed, the skew indicates this.
			 */
			if (skew != 0) {
		        	for (i = BPL+1; --i; dst+=(SCREEN_WORD_WD-1)) {
					*dst++ = (*data >> skew );
					/*
					 * If the width of the locator is
					 * less then the width of a normal
					 * locator then the locator has been
					 * clipped and we do not have to write
					 * the second half of the locator to
					 * the screen.
					 */
					if (width == BPL) {
						*dst = (*data << (BPL-skew));
					}
					data++;
				}
			}
			else {
				for (i = BPL + 1; --i; dst += SCREEN_WORD_WD)
					*dst = *data++;
			}
		}
		break;
	}

	/*
	 * Save the mode by which the present locator was put up.
	 */
	prev_mode = mode;

	*X_reg = x;
	*Y_reg = y;

#ifdef DEBUG
	if (loc16_debug) {
		printf("hardware has: %d,%d\n",x,y);
	}
#endif
}

/* 
 * If the locator is not on the screen or the x,y position given is different
 * from the current position, then move the locator on the screen.
 */
apa16_pos_loc(x,y)
register long x,y;
{
	/*
	 * If the locator is to be hidden then keep these points and return.
	 * Otherwise, If the locator is not on the screen OR the locator
	 * has moved then keep these points and put the locator on the screen.
	 */
	if (loc_hidden) {
		x_hotspot = x;
		y_hotspot = y;
	}
	else if (x != x_hotspot || y != y_hotspot || (on_screen == FALSE)) {
		/*
		 * Yes the locator is on the screen.
		 */
		on_screen = TRUE;

		/*
		 * Fill in the new hotspots.
		 */
		x_hotspot = x;
		y_hotspot = y;

		/*
		 * Apa16_put_loc puts the locator in the hardware locator area
		 * (if necessary) does some clipping on the x,y hotspots 
		 * and fills in the APA-16's locator registers.
		 */
		apa16_put_loc(X_LOCATOR_R,Y_LOCATOR_R);
	}
}

/*
 * If the locator is within these bounds remove it and return TRUE (non-zero).
 * Thats what this routine would normally do except that this is now a
 * hardware locator and it does not need to be removed from the screen
 * because it is not ON the actual bitmap of the screen so we always return
 * FALSE(0).
 */
apa16_check_loc(xleft,ytop,xright,ybottom)
register int xleft,ytop,xright,ybottom;
{
	return(FALSE);
}
#endif NAPASTTY
