$NOLIST

NAME  WindowCursorRoutines

$INCLUDE (WinCnsts~Inc~)

CGROUP GROUP CODE


PUBLIC CsrTurnCursorOn, CsrTurnCursorOff, CsrMaybeTurnCursorOff
PUBLIC CsrMoveCursor, CsrSetCursor

EXTRN  currOrientation: BYTE, displayWindow: WORD
EXTRN  cursorOriginX: WORD, cursorOriginY: WORD
EXTRN  cursorOffCount: WORD, cursorLocX: WORD, cursorLocY: WORD
EXTRN  cursorLeft: WORD, cursorTop: WORD, cursorRight: WORD, cursorBottom: WORD
EXTRN  lastCursorWindow: WORD, workCursorWindow: WORD, crsrFontInfoAddr: DWORD

EXTRN  RetDosAndIntelPtrs: NEAR
EXTRN  WinCopyCursorRectangle: NEAR, FntSetFontOrientation: NEAR


CODE SEGMENT  PUBLIC 'CODE'
	ASSUME CS:CGROUP
$EJECT

; PROCEDURE CsrTurnCursorOn;

rExtentY    EQU WORD PTR [BP-2]
rExtentX    EQU WORD PTR [BP-4]
rTopLftY    EQU WORD PTR [BP-6]
rTopLftX    EQU WORD PTR [BP-8]

CsrTurnCursorOn PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, 8	; Local variables

	MOV	AX, CS:cursorOffCount
	OR	AX, AX	; If cursor is already on then
	JZ	JumpToCsrTurnCursorOnRet	; don't turn it on again

	DEC	AX	; Decrement cursor off count
	MOV	CS:cursorOffCount, AX	; If cursor state changed from off to on
	JZ	CsrTurnCursorOnHere	; then redisplay the cursor.

JumpToCsrTurnCursorOnRet:
	JMP	CsrTurnCursorOnRet	; Don't do anything

CsrTurnCursorOnHere:
	MOV	AX, CS:cursorLocX
	SUB	AX, CS:cursorOriginX
	MOV	CS:cursorLeft, AX	; cursorLeft = cursorLocX - cursorOriginX

	MOV	BX, CS:cursorLocY
	SUB	BX, CS:cursorOriginY
	MOV	CS:cursorTop, BX	; cursorTop = cursorLocY - cursorOriginY

	MOV	rTopLftX, AX	; r.topLeftX = cursorLeft
	MOV	rTopLftY, BX	; r.topLeftY = cursorTop
	MOV	CX, cursorWidth	; cursorWidth, cursorHeight are the same
	MOV	rExtentX, CX	; r.extentX  = maxCursorWidth
	MOV	rExtentY, CX	; r.extentY  = maxCursorHeight

	ADD	AX, CX
	DEC	AX
	MOV	CS:cursorRight, AX
	ADD	BX, CX
	DEC	BX
	MOV	CS:cursorBottom, BX
    
	PUSH	CS:displayWindow	; source window of copy
	PUSH	CS:lastCursorWindow	; destination window of copy
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; @r
	XOR	AX, AX
	PUSH	AX
	PUSH	AX	; destination (x,y) = (0,0)

    CALL WinCopyCursorRectangle

	LDS	SI, CS:crsrFontInfoAddr
	LEA	BX, DS:[SI].fiChInfBase
	INC	BX
	INC	BX	; Skip past the width (to the index)
	ADD	SI, DS:[BX]
	MOV	DX, DS	; Store cursor info base in DX
	MOV	DS, CS:lastCursorWindow
	MOV	DI, DS:wiDisplayAddr	; Store source window display base in DI
	MOV	ES, CS:workCursorWindow
	MOV	ES, ES:wiDisplayAddr	; Store dest window display base in ES
	XOR	BX, BX	; index to the windows

CsrTurnCursorOnLoop:
	MOV	DS, DX	; @Cursor info
	MOV	CH, DS:[SI+0]
	MOV	CL, DS:[SI+16]	; CX = data image
	MOV	AH, DS:[SI+32]
	MOV	AL, DS:[SI+48]	; AX = mask image

	MOV	DS, DI	; @Source window (last cursor)
	NOT	AX	; NOT maskImage
	AND	AX, DS:[BX]
	XOR	AX, CX	; XOR dataImage
	MOV	ES:[BX], AX

	INC	BX
	INC	BX	; Dest stored as words
	INC	SI	; Source stored as bytes
	CMP	BX, cursorHeight + cursorHeight
	JB	CsrTurnCursorOnLoop

	XOR	AX, AX	; source of copy is (0,0)
	MOV	rTopLftX, AX	; r.topLeftX = 0
	MOV	rTopLftY, AX	; r.topLeftY = 0
	MOV	CX, cursorWidth	; cursorWidth, cursorHeight are the same
	MOV	rExtentX, CX	; r.extentX  = maxCursorWidth
	MOV	rExtentY, CX	; r.extentY  = maxCursorHeight
    
	PUSH	CS:workCursorWindow	; source window of copy
	PUSH	CS:displayWindow	; destination window of copy
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; @r
	PUSH	CS:cursorLeft	; destination x = cursorLeft
	PUSH	CS:cursorTop	; destination y = cursorTop

    CALL WinCopyCursorRectangle

CsrTurnCursorOnRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET
CsrTurnCursorOn ENDP

PURGE rTopLftX, rTopLftY, rExtentX, rExtentY
$EJECT

; PROCEDURE CsrTurnCursorOff;

rExtentY    EQU WORD PTR [BP-2]
rExtentX    EQU WORD PTR [BP-4]
rTopLftY    EQU WORD PTR [BP-6]
rTopLftX    EQU WORD PTR [BP-8]

CsrTurnCursorOff PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, 8	; Local variables

	CMP	CS:cursorOffCount, 0	; If cursor is already off then
	JNE	CsrTurnCursorOffRet	; leave off, but keep track of off count

	XOR	AX, AX	; source of copy is (0,0)
	MOV	rTopLftX, AX	; r.topLeftX = 0
	MOV	rTopLftY, AX	; r.topLeftY = 0
	MOV	CX, cursorWidth	; cursorWidth, cursorHeight are the same
	MOV	rExtentX, CX	; r.extentX  = maxCursorWidth
	MOV	rExtentY, CX	; r.extentY  = maxCursorHeight
    
	PUSH	CS:lastCursorWindow	; source window of copy
	PUSH	CS:displayWindow	; destination window of copy
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; @r
	PUSH	CS:cursorLeft	; cursorLeft
	PUSH	CS:cursorTop	; cursorTop

    CALL WinCopyCursorRectangle

CsrTurnCursorOffRet:
	INC	CS:cursorOffCount	; Increment cursor off count

	MOV	SP, BP
	POP	BP
	POP	DS
	RET
CsrTurnCursorOff ENDP

PURGE rTopLftX, rTopLftY, rExtentX, rExtentY
$EJECT

; PROCEDURE CsrMoveCursor (newXLoc, newYLoc);

newLocX       EQU WORD PTR [BP+8]
newLocY	     EQU WORD PTR [BP+6]

oldCursorLeft EQU WORD PTR [BP-2]
oldCursorTop  EQU WORD PTR [BP-4]

rExtentY      EQU WORD PTR [BP-6]
rExtentX      EQU WORD PTR [BP-8]
rTopLftY      EQU WORD PTR [BP-10]
rTopLftX      EQU WORD PTR [BP-12]

rectAExtentY  EQU WORD PTR [BP-14]
rectAExtentX  EQU WORD PTR [BP-16]
rectATopLftY  EQU WORD PTR [BP-18]
rectATopLftX  EQU WORD PTR [BP-20]

rectBExtentY  EQU WORD PTR [BP-22]
rectBExtentX  EQU WORD PTR [BP-24]
rectBTopLftY  EQU WORD PTR [BP-26]
rectBTopLftX  EQU WORD PTR [BP-28]

rectEExtentY  EQU WORD PTR [BP-30]
rectEExtentX  EQU WORD PTR [BP-32]
rectETopLftY  EQU WORD PTR [BP-34]
rectETopLftX  EQU WORD PTR [BP-36]

CsrMoveCursor PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, 36

; Need to save new cursor location info, even if cursor is off
; so the values will be correct when it is turned back on

	MOV	AX, newLocX
	MOV	CS:cursorLocX, AX	; cursorLocX = newLocX
	SUB	AX, CS:cursorOriginX	; new cursorLeft
	MOV	CX, AX
	ADD	CX, cursorWidth
	DEC	CX
	MOV	CS:cursorRight, CX	; cursorRight = cursorLeft + cursorWidth - 1
	XCHG	CS:cursorLeft, AX	; cursorLeft = newLocX - cursorOriginX
	MOV	oldCursorLeft, AX	; oldCursorLeft saved

	MOV	BX, newLocY
	MOV	CS:cursorLocY, BX	; cursorLocY = newLocY
	SUB	BX, CS:cursorOriginY	; new cursorTop
	MOV	DX, BX
	ADD	DX, cursorWidth
	DEC	DX
	MOV	CS:cursorBottom, DX	; cursorBottom = cursorTop + cursorWidth - 1
	XCHG	CS:cursorTop, BX	; cursorTop = newLocY - cursorOriginY
	MOV	oldCursorTop, BX	; oldCursorTop saved

	CMP	CS:cursorOffCount, 0	; If cursor is not off then
	JE	CsrMoveCursorBegin	; move the cursor loc and redraw it
	JMP	CsrMoveCursorRet	; else don't do anything

CsrMoveCursorBegin:
	MOV	AX, CS:cursorLeft
	MOV	rTopLftX, AX	; r.topLeftX = cursorLeft
	MOV	AX, CS:cursorTop
	MOV	rTopLftY, AX	; r.topLeftY = cursorTop
	MOV	CX, cursorWidth	; cursorWidth, cursorHeight are the same
	MOV	rExtentX, CX	; r.extentX  = maxCursorWidth
	MOV	rExtentY, CX	; r.extentY  = maxCursorHeight

	PUSH	CS:displayWindow	; source window of copy
	PUSH	CS:workCursorWindow	; destination window of copy
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; @r
	XOR	AX, AX	; destination point is (0,0)
	PUSH	AX
	PUSH	AX

    CALL WinCopyCursorRectangle

; Figure the rectangles involved

; Use AX=temp; BX=width/height; CX=old x; DX=old y; SI=new x; DI= new y
; in the following rectangle calculation sections

	MOV	BX, cursorWidth	; cursorWidth or cursorHeight (same)
	MOV	CX, oldCursorLeft	; old cursor left (x)
	MOV	DX, oldCursorTop	; old cursor top  (y)
	MOV	SI, CS:cursorLeft	; new cursor left (x)
	MOV	DI, CS:cursorTop	; new cursor top  (y)

; Update region (rectangle) section A - Top or Bottom rectangle region

	MOV	rectATopLftX, CX	; oldCsrRectA.topLeftX = oldTopLftX
	MOV	AX, DX	; use old topLeft y if cursor moved up
	CMP	DI, DX	; If newTopLftY >= oldTopLftY then
	JGE	SetRectATopLeftY	; use old topLeftY to set rectA.topLftY

	MOV	AX, DI
	ADD	AX, BX	; If newTopLftY + maxCursorHeight
	CMP	AX, DX	; is greater than oldTopLftY then
	JGE	SetRectATopLeftY	; use it to set rectA.topLftY

	MOV	AX, DX	; else use oldTopLftY to set rectA.topLftY

SetRectATopLeftY:
	MOV	rectATopLftY, AX
	MOV	rectAExtentX, BX

	MOV	AX, DI
	SUB	AX, DX
	JNS	NotNegA

	NEG	AX

NotNegA:
	CMP	AX, BX	; If ABS (newTopLftY - oldTopLftY)
	JLE	SetRectAExtentY	; is less than cursor height then use it

	MOV	AX, BX	; else use cursorHeight

SetRectAExtentY:
	MOV	rectAExtentY, AX	; to set rectA.extentY

; Update region (rectangle) section B - Left or Right rectangle region

	MOV	AX, CX	; use old topLeft x if cursor moved right
	CMP	SI, CX	; If newTopLftX >= oldTopLftX then
	JGE	SetRectBTopLeftX	; use old topLeftX to set rectB.topLftX

	MOV	AX, SI
	ADD	AX, BX	; If newTopLftX + maxCursorWidth
	CMP	AX, CX	; is greater than oldTopLftX then
	JGE	SetRectBTopLeftX	; use it to set rectB.topLftX

	MOV	AX, CX	; else use oldTopLftX to set rectB.topLftX

SetRectBTopLeftX:
	MOV	rectBTopLftX, AX	; set rectB.topLftX

	MOV	AX, DX
	CMP	AX, DI	; If oldTopLftY > newTopLftY
	JGE	SetRectBTopLeftY	; the use oldTopLftY to set rectB.topLftY

	MOV	AX, DI	; else use newTopLftY to set rectB.topLftY

SetRectBTopLeftY:
	MOV	rectBTopLftY, AX	; set rectB.topLftY

	MOV	AX, SI
	SUB	AX, CX
	JNS	NotNegB

	NEG	AX

NotNegB:
	CMP	AX, BX	; If ABS (newTopLftX - oldTopLftX)
	JLE	SetRectBExtentX	; is less than cursor width then use it

	MOV	AX, BX	; else use cursorWidth

SetRectBExtentX:
	MOV	rectBExtentX, AX	; to set rectB.extentX

	MOV	AX, BX
	SUB	AX, rectAExtentY	; rectB.extentY =
	MOV	rectBExtentY, AX	;  maxCursorHeight - rectA.extentY

; Update region (rectangle) section E - The overlapping rectangle region

	MOV	AX, CX
	CMP	AX, SI	; Use the larger of oldTopLftX
	JGE	SetRectETopLeftX

	MOV	AX, SI	; and newTopLftX

SetRectETopLeftX:
	MOV	rectETopLftX, AX	; to set rectE.topLftX

	MOV	AX, rectBTopLftY
	MOV	rectETopLftY, AX	; rectE.topLftY = rectB.topLftY

	MOV	AX, BX
	SUB	AX, rectBExtentX
	MOV	rectEExtentX, AX	; rectE.extentX = maxCrsrWidth - rectB.extentX

	MOV	AX, BX
	SUB	AX, rectAExtentY
	MOV	rectEExtentY, AX	; rectE.extentY = maxCrsrWidth - rectA.extentY

; Move overlapping region (rectangle) E from save area
; to its new position in the work area

	MOV	AX, rectETopLftX	; movTopLftX =
	SUB	AX, SI	;  rectE.topLftX - newTopLftX
	MOV	BX, rectETopLftY	; movTopLftY =
	SUB	BX, DI	;  rectE.topLftY - newTopLftY

	SUB	rectETopLftX, CX	; rectE.topLftX = rectE.topLftX - oldTopLftX
	SUB	rectETopLftY, DX	; rectE.topLftY = rectE.topLftY - oldTopLftY

	PUSH	CS:lastCursorWindow	; source window of copy
	PUSH	CS:workCursorWindow	; destination window of copy
	LEA	CX, rectETopLftX
	PUSH	SS
	PUSH	CX	; @r
	PUSH	AX	; movTopLftX
	PUSH	BX	; movTopLftY

    CALL WinCopyCursorRectangle

; Restore region (rectangle) section A

	MOV	AX, rectATopLftX	; movTopLftX = rectA.topLftX
	MOV	BX, rectATopLftY	; movTopLftY = rectA.topLftY

	MOV	CX, oldCursorLeft	; old cursor left (x)
	MOV	DX, oldCursorTop	; old cursor top  (y)
	SUB	rectATopLftX, CX	; rectA.topLftX = rectA.topLftX - oldTopLftX
	SUB	rectATopLftY, DX	; rectA.topLftY = rectA.topLftY - oldTopLftY

	PUSH	CS:lastCursorWindow	; source window of copy
	PUSH	CS:displayWindow	; destination window of copy
	LEA	CX, rectATopLftX
	PUSH	SS
	PUSH	CX	; @r
	PUSH	AX	; movTopLftX
	PUSH	BX	; movTopLftY

    CALL WinCopyCursorRectangle

; Restore region (rectangle) section B

	MOV	AX, rectBTopLftX	; movTopLftX = rectB.topLftX
	MOV	BX, rectBTopLftY	; movTopLftY = rectB.topLftY

	MOV	CX, oldCursorLeft	; old cursor left (x)
	MOV	DX, oldCursorTop	; old cursor top  (y)
	SUB	rectBTopLftX, CX	; rectB.topLftX = rectB.topLftX - oldTopLftX
	SUB	rectBTopLftY, DX	; rectB.topLftY = rectB.topLftY - oldTopLftY

	PUSH	CS:lastCursorWindow	; source window of copy
	PUSH	CS:displayWindow	; destination window of copy
	LEA	CX, rectBTopLftX
	PUSH	SS
	PUSH	CX	; @r
	PUSH	AX	; movTopLftX
	PUSH	BX	; movTopLftY

    CALL WinCopyCursorRectangle

; Copy work area to save area and merge in cursor at the same time

	LDS	SI, CS:crsrFontInfoAddr
	LEA	BX, DS:[SI].fiChInfBase
	INC	BX
	INC	BX	; Skip past the width (to the index)
	ADD	SI, DS:[BX]
	MOV	DX, DS	; Store cursor info base in DX
	MOV	DS, CS:lastCursorWindow
	MOV	DI, DS:wiDisplayAddr	; Store source window display base in DI
	MOV	ES, CS:workCursorWindow
	MOV	ES, ES:wiDisplayAddr	; Store dest window display base in ES
	XOR	BX, BX	; index to the windows

	PUSH	BP	; Need to use BP as a general purpose register

CsrMoveCursorLoop:
	MOV	DS, DX	; @Cursor info
	MOV	CH, DS:[SI+0]
	MOV	CL, DS:[SI+16]	; CX = data image
	MOV	AH, DS:[SI+32]
	MOV	AL, DS:[SI+48]	; AX = mask image

	MOV	DS, DI	; @Dest window (last cursor)
	MOV	BP, ES:[BX]	; Copy part of the cursor from
	MOV	DS:[BX], BP	; work window to last window
	NOT	AX	; NOT maskImage
	AND	AX, BP
	XOR	AX, CX	; XOR dataImage
	MOV	ES:[BX], AX

	INC	BX
	INC	BX	; Dest stored as words
	INC	SI	; Source stored as bytes
	CMP	BX, cursorHeight + cursorHeight
	JB	CsrMoveCursorLoop

	POP	BP	; Restore BP

; Redisplay the updated cursor

	XOR	AX, AX	; source of copy is (0,0)
	MOV	rTopLftX, AX	; r.topLeftX = 0
	MOV	rTopLftY, AX	; r.topLeftY = 0
	MOV	CX, cursorWidth	; cursorWidth, cursorHeight are the same
	MOV	rExtentX, CX	; r.extentX  = maxCursorWidth
	MOV	rExtentY, CX	; r.extentY  = maxCursorHeight
    
	PUSH	CS:workCursorWindow	; source window of copy
	PUSH	CS:displayWindow	; destination window of copy
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; @r
	PUSH	CS:cursorLeft	; cursorLeft
	PUSH	CS:cursorTop	; cursorTop

    CALL WinCopyCursorRectangle

CsrMoveCursorRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	4
CsrMoveCursor ENDP

PURGE newLocX, newLocY, oldCursorLeft, oldCursorTop
PURGE rTopLftX, rTopLftY, rExtentX, rExtentY
PURGE rectATopLftX, rectATopLftY, rectAExtentX, rectAExtentY
PURGE rectBTopLftX, rectBTopLftY, rectBExtentX, rectBExtentY
PURGE rectETopLftX, rectETopLftY, rectEExtentX, rectEExtentY
$EJECT

; PROCEDURE CsrMaybeTurnCursorOff;

; This routine will turn the cursor off if:
;   1. The cursor is on
;   2. The draw window is the display
;   3. The cursor rectangle overlaps the rectangle passed in

; Entry:
;   CX = Left pt of rectangle
;   DX = Top
;   SI = Right
;   DI = Bottom
;   DS = Draw Window

; Exit:
;   AL = "Was cursor turned off" (0=No, 1=Yes)
;   All other register potentially changed except DS

CsrMaybeTurnCursorOff PROC NEAR
	CMP	CS:cursorOffCount, 0	; If cursor is already off then
	JNE	NotTurnedOff	; don't have to turn off cursor

	MOV	ES, CS:displayWindow	; If the window in question
	MOV	AX, ES:wiDisplayAddr	; does not have the same display
	CMP	AX, DS:wiDisplayAddr	; address as the actual display
	JNE	NotTurnedOff	; then don't have to turn off cursor

	ADD	DX, DS:wiBoundsTopLeftY	; If the top of the rectangle in question
	CMP	DX, CS:cursorBottom	; is below the cursor's rectangle
	JG	NotTurnedOff	; then don't have to turn off cursor

	ADD	CX, DS:wiBoundsTopLeftX	; If the left of the rectangle in question
	CMP	CX, CS:cursorRight	; is to the right the cursor's rectangle
	JG	NotTurnedOff	; then don't have to turn off cursor

	ADD	DI, DS:wiBoundsTopLeftY	; If the bottom of the rectangle in question
	CMP	DI, CS:cursorTop	; is above the cursor's rectangle
	JL	NotTurnedOff	; then don't have to turn off cursor

	ADD	SI, DS:wiBoundsTopLeftX	; If the right of the rectangle in question
	CMP	SI, CS:cursorLeft	; is to the left the cursor's rectangle
	JL	NotTurnedOff	; then don't have to turn off cursor

TurnCursorOff:
	CALL	CsrTurnCursorOff
	MOV	AL, 1
	JMP	SHORT CsrMaybeTurnCursorOffRet

NotTurnedOff:
	XOR	AL, AL

CsrMaybeTurnCursorOffRet:
	RET
CsrMaybeTurnCursorOff ENDP
$EJECT

; CsrSetCursor: PROCEDURE (newCursor: FontInfoPtr): FontInfoPtr;

newCursor EQU DWORD PTR [BP+6]

CsrSetCursor PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LES	BX, CS:crsrFontInfoAddr
	LDS	SI, newCursor
	MOV	AX, DS	; If the newCursor fontInfoPtr
	OR	AX, SI	; is zero then dont change current cursor
	JZ	CsrSetCursorRet	; but just return it

	CALL	CsrTurnCursorOff	; Leave cursor off while setting new cursor

	MOV	AL, CS:currOrientation	 ; If the current window orientation
	CMP	AL, DS:[SI].fiOrientation	; matches that of the cursor font
	JE	CsrSetCrsrOkayOrientation	; then no need to rotate it into place

	LDS	SI, newCursor
	PUSH	DS
	PUSH	SI	; pFontInfo of new cursor font
	PUSH	AX	; new orientation
	CALL	FntSetFontOrientation
	LDS	SI, newCursor	; Reload this value after rotating font

CsrSetCrsrOkayOrientation:
	MOV	AX, DS:[SI].fiIntLead
	MOV	CS:cursorOriginX, AX
	MOV	AX, DS:[SI].fiExtLead
	MOV	CS:cursorOriginY, AX

	LES	BX, CS:crsrFontInfoAddr	; Return previous cursor fontInfoPtr
	MOV	WORD PTR CS:crsrFontInfoAddr+0, SI
	MOV	WORD PTR CS:crsrFontInfoAddr+2, DS

	PUSH	ES
	PUSH	BX
	CALL	CsrTurnCursorOn	; Turn cursor on after setting new cursor
	POP	BX
	POP	ES

CsrSetCursorRet:
	CALL	RetDosAndIntelPtrs	; Return ptr in ES:BX and DX:AX

	POP	BP
	POP	DS
	RET	4
CsrSetCursor ENDP

PURGE newCursor


CODE ENDS

     END
