$NOLIST

NAME  WinWindowRtns

$INCLUDE (ErrCnsts~Inc~)
$INCLUDE (WinCnsts~Inc~)

CGROUP GROUP CODE


PUBLIC WinSetActiveWindow, WinSetDrawWindow, WinSetDisplayOrientation
PUBLIC WinGetActiveWindow, WinGetDrawWindow, WinGetDisplayWindow
PUBLIC WinEnableWindow, WinDisableWindow, WinGetWindowsPointer
PUBLIC WinCreateWindow, WinCreateOffscreenWindow, WinDeleteWindow
PUBLIC WinEraseWindow, WinSaveBits, WinRestoreBits
PUBLIC WinGetWindowFrameRect, WinDrawWindowFrame

EXTRN  currOrientation: BYTE
EXTRN  activeWindow: WORD, drawWindow: WORD
EXTRN  displayWindow: WORD, firstWindow: WORD
EXTRN  enterWindowID: WORD, exitWindowID: WORD

EXTRN  DosAlloc: NEAR, DosFree: NEAR, RetDosAndIntelPtrs: NEAR
EXTRN  CsrTurnCursorOff: NEAR, CsrTurnCursorOn: NEAR, CsrSetCursor: NEAR
EXTRN  FntBaseLine: NEAR, FntCharHeight: NEAR, FntCharsWidth: NEAR
EXTRN  FntSetFont: NEAR, FntSetFontOrientation: NEAR
EXTRN  WinGetClip: NEAR, WinSetClip: NEAR, WinResetClip: NEAR
EXTRN  WinClipRectangle: NEAR, WinEraseRectangle: NEAR, WinCopyRectangle: NEAR
EXTRN  RctGetFramesRectangle: NEAR, WinDrawRectangleFrame: NEAR
EXTRN  WinDrawLine: NEAR, WinDrawChars: NEAR

EXTRN  GfxSetScreenOrientation: FAR, GfxWindowByteSize: FAR


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

; PROCEDURE WinSetActiveWindow (windowID: Word);

windowID EQU WORD PTR [BP+4]

WinSetActiveWindow PROC NEAR
	PUSH	BP
	MOV	BP, SP

	MOV	AX, windowID	; If new active window and
	MOV	BX, CS:activeWindow	; the current active window
	CMP	AX, BX	; are the same window
	JE	WinSetActiveWindowRet	; then no changes take place

	OR	BX, BX	; If the active window is not set
	JZ	WinSetActiveWindowHere	; then don't generate an exit event

	MOV	CS:exitWindowID, BX	; Send exit window event for last window

WinSetActiveWindowHere:
	MOV	CS:enterWindowID, AX	; Send enter window event for new window

WinSetActiveWindowRet:
	POP	BP
	RET	2
WinSetActiveWindow ENDP

PURGE windowID



; PROCEDURE WinSetDrawWindow (windowID: Word);

windowID EQU WORD PTR [BP+4]

WinSetDrawWindow PROC NEAR
	PUSH	BP
	MOV	BP, SP

	MOV	ES, windowID
	TEST	ES:wiWindowFlags, wiWinFormatFlag
	JNZ	WinSetDrawWindowRet	; Don't set if not screen format

	MOV	CS:drawWindow, ES

WinSetDrawWindowRet:
	POP	BP
	RET	2
WinSetDrawWindow ENDP

PURGE windowID
$EJECT

; PROCEDURE WinSetDisplayOrientation (newOrientation: Orientation);

; This routine will reorient the orientation of the...
;  - screen driver
;  - window code
;  - display window
;  - all onscreen windows in window list
;  - current font
;  - current cursor

orientation   EQU BYTE PTR [BP+6]

WinSetDisplayOrientation PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	CALL	CsrTurnCursorOff

	PUSH	WORD PTR orientation
	CALL	GfxSetScreenOrientation	; Tell screen driver about orientation

	MOV	AL, orientation
	MOV	BL, AL	; Save new orientation in BL
	XCHG	CS:currOrientation, AL	; Set window code variable to new orientation
	AND	AL, 1	; Look at N/S vs E/W orientation bit of old
	AND	BL, 1	; Look at N/S vs E/W orientation bit of new
	CMP	AL, BL	; If new orientation is 180 degrees from old
	JE	ReorientFont	; orientation then no need to change windows

ReorientDisplay:
	MOV	DS, CS:displayWindow
	MOV	AX, DS:wiDisplayWidth
	MOV	BX, DS:wiDisplayHeight
	XCHG	AX, BX
	MOV	DS:wiDisplayWidth, AX
	MOV	DS:wiDisplayHeight, BX	; Swap display width w/ display height
	MOV	AX, DS:wiBoundsExtentX
	MOV	BX, DS:wiBoundsExtentY
	XCHG	AX, BX
	MOV	DS:wiBoundsExtentX, AX
	MOV	DS:wiBoundsExtentY, BX	; Swap bounds extent x w/ bounds extent y
	PUSH	CS:drawWindow	; Save current draw window
	MOV	CS:drawWindow, DS	; Set display window as draw window
	CALL	WinResetClip	; Reset clipping rectangle to new orientation
	POP	CS:drawWindow	; Restore current draw window

ReorientOtherWindows:
	MOV	AX, CS:firstWindow
	MOV	DS, AX

ReorientOthersLoop:
	CMP	AX, CS:displayWindow	; If this window is the display window
	JE	ReorientOthersNext	; then skip it (just done above)

	TEST	DS:wiWindowFlags, wiOffscreenFlag
	JNZ	ReorientOthersNext	; If an offscreen window then skip it

	MOV	AX, DS:wiDisplayWidth
	MOV	BX, DS:wiDisplayHeight
	XCHG	AX, BX
	MOV	DS:wiDisplayWidth, AX
	MOV	DS:wiDisplayHeight, BX	; Swap display width w/ display height

ReorientOthersNext:
	MOV	AX, DS:wiNextWindow
	OR	AX, AX
	MOV	DS, AX
	JNZ	ReorientOthersLoop

ReorientFont:
	XOR	AX, AX
	PUSH	AX
	PUSH	AX
	CALL	FntSetFont	; Current font returned in ES:BX
	PUSH	ES
	PUSH	BX
	PUSH	WORD PTR orientation
	CALL	FntSetFontOrientation	; Orient current font to new orientation

ReorientCursor:
	XOR	AX, AX
	PUSH	AX
	PUSH	AX
	CALL	CsrSetCursor	; Current cursor info ptr returned in ES:BX
	PUSH	ES
	PUSH	BX
	PUSH	WORD PTR orientation
	CALL	FntSetFontOrientation	; Orient cursor to new orientation

	CALL	CsrTurnCursorOn

	POP	BP
	POP	DS
	RET	2
WinSetDisplayOrientation ENDP

PURGE orientation
$EJECT

WinGetActiveWindow PROC NEAR
	MOV	AX, CS:activeWindow
	RET
WinGetActiveWindow ENDP


WinGetDrawWindow PROC NEAR
	MOV	AX, CS:drawWindow
	RET
WinGetDrawWindow ENDP


WinGetDisplayWindow PROC NEAR
	MOV	AX, CS:displayWindow
	RET
WinGetDisplayWindow ENDP
$EJECT

; PROCEDURE WinEnableWindow (windowID: Word);

windowID EQU WORD PTR [BP+4]

WinEnableWindow PROC NEAR
	PUSH	BP
	MOV	BP, SP

	MOV	ES, windowID
	TEST	ES:wiWindowFlags, wiOffscreenFlag
	JNZ	WinEnableWindowRet	; Don't enable if not onscreen window

	OR	ES:wiWindowFlags, wiEnabledFlag

WinEnableWindowRet:
	POP	BP
	RET	2
WinEnableWindow ENDP

PURGE windowID


; PROCEDURE WinDisableWindow (windowID: Word);

windowID EQU WORD PTR [BP+4]

WinDisableWindow PROC NEAR
	PUSH	BP
	MOV	BP, SP

	MOV	AX, windowID
	MOV	ES, AX
	AND	ES:wiWindowFlags, NOT wiEnabledFlag

	POP	BP
	RET	2
WinDisableWindow ENDP

PURGE windowID


; FUNCTION WinGetWindowsPointer (windowID: Word): Pointer;

windowID EQU WORD PTR [BP+4]

WinGetWindowsPointer PROC NEAR
	PUSH	BP
	MOV	BP, SP

	MOV	ES, windowID
	XOR	BX, BX
	CALL	RetDosAndIntelPtrs	; Return ptr in ES:BX and DX:AX

	POP	BP
	RET	2
WinGetWindowsPointer ENDP

PURGE windowID
$EJECT

; PROCEDURE WinCreateWindow
;  (VAR bounds: Rectangle; frameType: Word; modal, titleArea: Boolean;
;   VAR titlePtr: Bytes; titleLen: Word; VAR error: Word): Word;

params STRUC
  newWindow  DW ?
;--------------
  oldBP      DW ?
  oldDS      DW ?
  nearReturn DW ?
;--------------
  pError	    DD ?
  titleLen   DW ?
  titlePtr   DD ?
  titleArea  DW ?
  modal      DW ?
  frameType  DW ?
  pBounds    DD ?
params ENDS

localBytes EQU 2
loc        EQU [BP-localBytes]
paramBytes EQU 20

WinCreateWindow PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, localBytes

	PUSH	CS:drawWindow	; Save current draw window

	MOV	BX, SIZE WindowInfoType
	ADD	BX, 15
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	CALL	DosAlloc	; Interface call to Int21, fnc 48h
	JNC	InitializeWindow
	JMP	WinCreateWindowRet

InitializeWindow:
	MOV	loc.newWindow, AX

	MOV	ES, AX	; sWindowInfo
	MOV	DS, CS:displayWindow
	LEA	SI, DS:wiDisplayWidth
	MOV	DI, SI
	CLD
	MOVSW	; Copy display width from display window
	MOVSW	; Copy display height from display window
	MOVSW	; Copy display addr from display window

	XOR	AX, AX
	STOSW	; wiWindowFlags := 0;

	LEA	DI, ES:wiBoundsTopLeftX
	LDS	SI, loc.pBounds
	MOVSW	; Set bounds topLeft x
	MOVSW	; Set bounds topLeft y
	MOVSW	; Set bounds extent x
	MOVSW	; Set bounds extent y

	MOV	CX, SIZE WindowInfoType
	SUB	CX, 16	; 16 bytes have already been initialized
	REP	STOSB	; Zero out remainder of window record

	MOV	AX, CS:displayWindow
	MOV	CS:drawWindow, AX

	LEA	DI, ES:wiBoundsTopLeftX
	PUSH	ES
	PUSH	DI
	CALL	WinClipRectangle	; Clip rectangle to bounds of display window

	MOV	AX, loc.newWindow
	MOV	CS:drawWindow, AX
	CALL	WinResetClip

	MOV	DS, loc.newWindow
	MOV	AX, loc.frameType
	MOV	DS:wiFrameType, AX	; window.wiFrameType := frameType

	MOV	AX, loc.modal
	SHL	AX, 1
	SHL	AX, 1	; Bit two is used to store modal flag
	AND	AX, wiModalFlag
	MOV	BX, loc.titleArea
	SHL	BX, 1
	SHL	BX, 1
	SHL	BX, 1	; Bit three is used to store title flag
	AND	BX, wiTitleFlag
	OR	AX, BX	; Add in the bit that defines title flag
	OR	AX, wiEnabledFlag	; Window is initially enabled
	MOV	DS:wiWindowFlags, AX	; Bit zero (= 0) is format (= screen format)

	LES	AX, loc.titlePtr
	MOV	WORD PTR DS:wiTitlePtr+0, AX
	MOV	WORD PTR DS:wiTitlePtr+2, ES

	MOV	AX, loc.titleLen
	MOV	DS:wiTitleLen, AX

	MOV	AX, CS:firstWindow
	MOV	DS:wiNextWindow, AX	; newWindow.next = "old" firstWindow
	MOV	CS:firstWindow, DS	; firstWindow = new window

	XOR	AX, AX	; No error

WinCreateWindowRet:
	LES	DI, loc.pError
	STOSW	; Set error code

	POP	CS:drawWindow	; Restore current draw window
	MOV	AX, loc.newWindow	; Return new windows windowID

	MOV	SP, BP
	POP	BP
	POP	DS
	RET	paramBytes
WinCreateWindow ENDP

PURGE params, loc, localBytes, paramBytes
PURGE oldBP, oldDS, nearReturn
PURGE pBounds, frameType, modal, pError
PURGE titleArea, titlePtr, titleLen
PURGE newWindow
$EJECT

; PROCEDURE WinCreateOffscreenWindow
;  (width, height: Integer; format: Byte; VAR error: Word): Word;

params STRUC
  newWindow  DW ?
;--------------
  oldBP      DW ?
  oldDS      DW ?
  nearReturn DW ?
;--------------
  pError	    DD ?
  winFormat  DW ?
  winHeight  DW ?
  winWidth   DW ?
params ENDS

localBytes EQU 2
loc        EQU [BP-localBytes]
paramBytes EQU 10

WinCreateOffscreenWindow PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, localBytes

	PUSH	CS:drawWindow	; Save current draw window

	MOV	BX, SIZE WindowInfoType
	ADD	BX, 15
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	CALL	DosAlloc	; Interface call to Int21, fnc 48h
	JC	WinCreateOffscreenWinExitHop

	MOV	loc.newWindow, AX

	PUSH	loc.winWidth
	PUSH	loc.winHeight
	PUSH	loc.winFormat
	CALL	GfxWindowByteSize

	MOV	BX, AX
	ADD	BX, 15
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	CALL	DosAlloc	; Interface call to Int21, fnc 48h
	JNC	OffscreenWindowAllocated

	PUSH	AX	; Save error
	MOV	ES, loc.newWindow	; sWindowInfo
	CALL	DosFree	; Interface call to Int21, fnc 49h
	POP	AX	; Restore error

WinCreateOffscreenWinExitHop:
	JMP	SHORT WinCreateOffscreenWindowExit

OffscreenWindowAllocated:
	PUSH	AX	; Save address of offscreen window buffer
	MOV	ES, loc.newWindow
	LEA	DI, ES:wiDisplayWidth
	MOV	CX, SIZE WindowInfoType
	XOR	AX, AX
	SHR	CX, 1
	CLD
	REP	STOSW	; Zero out contents of window record
	JNC	SetOffscreenWindowFields

	STOSB	; Zero out last "odd" byte

SetOffscreenWindowFields:
	POP	AX	; Address of offscreen window buffer
	MOV	BX, ES
	MOV	DS, BX
	MOV	CX, loc.winWidth
	MOV	DX, loc.winHeight
	MOV	DS:wiDisplayWidth, CX
	MOV	DS:wiDisplayHeight, DX
	MOV	DS:wiDisplayAddr, AX	; AX was returned by the allocate above

;	MOV	DS:wiBoundsTopLeftX, 0	; Set by the STOSW above
;	MOV	DS:wiBoundsTopLeftY, 0	; Set by the STOSW above
	MOV	DS:wiBoundsExtentX, CX
	MOV	DS:wiBoundsExtentY, DX

	MOV	CS:drawWindow, DS
	CALL	WinResetClip

	MOV	AX, loc.winFormat
	AND	AX, 1	; Bit zero is used to store the format
	OR	AX, wiOffscreenFlag	; Set offscreen flag to TRUE
	MOV	DS:wiWindowFlags, AX

	MOV	AX, CS:firstWindow
	MOV	DS:wiNextWindow, AX	; newWindow.next = "old" firstWindow
	MOV	CS:firstWindow, DS	; firstWindow = new window

	XOR	AX, AX	; No error

WinCreateOffscreenWindowExit:
	LES	DI, loc.pError
	STOSW	; Set error code

	POP	CS:drawWindow	; Restore current draw window

	OR	AX, AX	; If error <> eOK
	MOV	AX, 0	; (MOV doesn't change flags)
	JNZ	WinCreateOffscreenWindowRet	; then return (invalid windowID)

	MOV	AX, loc.newWindow	; Else return (new windows windowID)

WinCreateOffscreenWindowRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	paramBytes
WinCreateOffscreenWindow ENDP

PURGE params, loc, localBytes, paramBytes
PURGE oldBP, oldDS, nearReturn
PURGE winWidth, winHeight, winFormat, pError
PURGE newWindow
$EJECT

; PROCEDURE WinDeleteWindow (windowID: Word; eraseIt: Boolean; VAR error: Word);

windowID	   EQU  WORD PTR [BP+12]
eraseWindow EQU  BYTE PTR [BP+10]
pError	   EQU DWORD PTR [BP+6]

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

WinDeleteWindow PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, 8	; Room for local variables

	MOV	AX, windowID
	MOV	BX, CS:displayWindow

WinDeletingDisplayWindow:
	CMP	AX, BX	; If the window is not the display window
	JNE	WinDeletingDrawWindow	; check to see if it is the draw window
	JMP	WinDeleteWindowOK	; If it is the display window - Do nothing

WinDeletingDrawWindow:
	CMP	AX, CS:drawWindow	; If it is not the draw window then
	JNE	WinDeletingActiveWindow	; check to see if it is the active window

	MOV	CS:drawWindow, BX	; Reset draw window to the display window

WinDeletingActiveWindow:
	CMP	AX, CS:activeWindow	; If it is not the active window then
	JNE	WinDeleteWindowHere	; go ahead and delete it

	MOV	CS:exitWindowID, AX	; This window is exiting (so to speak)
	MOV	CS:activeWindow, 0	; No active window if deleting active window

WinDeleteWindowHere:
	MOV	DS, AX
	TEST	DS:wiWindowFlags, wiOffscreenFlag
	JNZ	WinDeleteWindowUnlink	; Don't need to erase offscreen windows

	TEST	eraseWindow, 1	; If the user does not want to erase the
	JZ	WinDeleteWindowUnlink	; window then don't erase it

	PUSH	DS	; windowID
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; @rectangle
	CALL	WinGetWindowFrameRect
	PUSH	CS:drawWindow
	MOV	AX, CS:displayWindow
	MOV	CS:drawWindow, AX
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; @rectangle
	XOR	AX, AX
	PUSH	AX	; corner diameter = 0 (no rounded corners)
	CALL	WinEraseRectangle
	POP	CS:drawWindow

WinDeleteWindowUnlink:
	MOV	AX, windowID	; AX = window being deleted
	MOV	ES, AX
	MOV	CX, ES:wiNextWindow	; CX = window that follows deleted window
	MOV	BX, CS:firstWindow
	CMP	AX, BX	; If the window being deleted <> first window
	JNE	WinDeleteWinUnlinkSearch	; then find the windows position in the list

	MOV	CS:firstWindow, CX	; firstWindow = deletedWindow.next
	JMP	SHORT WinDeleteWindowFree

WinDeleteWinUnlinkSearch:
	MOV	ES, BX
	MOV	BX, ES:wiNextWindow	; BX = next window
	OR	BX, BX	; If this is the end of the list
	JZ	WinDeleteWindowFree	; then it must not be in the list (?)

	CMP	AX, BX	; If the window that follows this one
	JE	WinDeleteWinUnlinkHere	; is being deleted then remove it from list
	JMP	WinDeleteWinUnlinkSearch

WinDeleteWinUnlinkHere:
	MOV	ES:wiNextWindow, CX

WinDeleteWindowFree:
	MOV	DS, windowID
	TEST	DS:wiWindowFlags, wiOffscreenFlag
	JZ	WinDontDeleteBuffer	; Don't delete the buffer of onscreen window

	MOV	ES, DS:wiDisplayAddr
	CALL	DosFree	; Interface call to Int21, fnc 49h
	JC	WinDeleteWindowRet

WinDontDeleteBuffer:
	PUSH	DS
	POP	ES
	CALL	DosFree	; Interface call to Int21, fnc 49h
	JC	WinDeleteWindowRet
	
WinDeleteWindowOK:
	XOR	AX, AX	; No error

WinDeleteWindowRet:
	LES	DI, pError
	STOSW	; Set error code

	MOV	SP, BP
	POP	BP
	POP	DS
	RET	8
WinDeleteWindow ENDP

PURGE windowID, eraseWindow, pError
PURGE rTopLftX, rTopLftY, rExtentX, rExtentY
$EJECT

; WinGetWindowFrameRect: PROCEDURE (windowID, pRect);

windowID EQU  WORD PTR [BP+10]
pRect	EQU DWORD PTR [BP+6]

WinGetWindowFrameRect PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	MOV	DS, windowID
	LEA	SI, DS:wiBoundsTopLeftX
	LES	DI, pRect
	CLD
	MOVSW
	MOVSW
	MOVSW
	MOVSW

	TEST	DS:wiWindowFlags, wiTitleFlag
	JZ	AddInSpaceNeededForFrame

	CALL	FntCharHeight
	PUSH	AX
	CALL	FntBaseLine
	POP	CX
	ADD	CX, CX	; CharHeight + CharHeight - BaseLine
	SUB	CX, AX	; Descender distance = charHeight-baseLine
	INC	CX	; Gap above window title
	INC	CX	; Gap below window title
	INC	CX	; Line between title and window content area
	LES	DI, pRect
	SUB	ES:[DI].rectTopLeftY, CX	; Move the window "interior" up
	ADD	ES:[DI].rectExtentY, CX	; Extent length of window area

AddInSpaceNeededForFrame:
	PUSH	DS:wiFrameType	; frame type
	LES	AX, pRect
	PUSH	ES
	PUSH	AX	; pRect
	PUSH	ES
	PUSH	AX	; pObscuredRect
	CALL	RctGetFramesRectangle

	POP	BP
	POP	DS
	RET	6
WinGetWindowFrameRect ENDP

PURGE windowID, pRect
$EJECT

; WinDrawWindowFrame: PROCEDURE;

descender   EQU WORD PTR [BP-2]
rExtentY    EQU WORD PTR [BP-4]
rExtentX    EQU WORD PTR [BP-6]
rTopLftY    EQU WORD PTR [BP-8]
rTopLftX    EQU WORD PTR [BP-10]
clpExtentY  EQU WORD PTR [BP-12]	; old clip rectangle
clpExtentX  EQU WORD PTR [BP-14]
clpTopLftY  EQU WORD PTR [BP-16]
clpTopLftX  EQU WORD PTR [BP-18]

WinDrawWindowFrame PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, 18	; Make room for local variable

	MOV	DS, CS:drawWindow
	LEA	SI, DS:wiBoundsTopLeftX
	PUSH	SS
	POP	ES
	LEA	DI, rTopLftX
	CLD
	MOVSW
	MOVSW
	MOVSW
	MOVSW	; Copy window's rect to local rectangle

	MOV	AX, CS:displayWindow
	MOV	CS:drawWindow, AX	; Make entire display the drawWindow

	TEST	DS:wiWindowFlags, wiTitleFlag
	JZ	DrawFrameAroundWindow

	LEA	AX, clpTopLftX
	PUSH	SS
	PUSH	AX
	CALL	WinGetClip	; Get old clipping rectangle

	MOV	AX, rTopLftX
	PUSH	AX
	MOV	BX, rTopLftY
	DEC	BX
	PUSH	BX
	ADD	AX, rExtentX
	DEC	AX
	PUSH	AX
	PUSH	BX
	CALL	WinDrawLine	; Draw line between title and window area

	CALL	FntCharHeight
	PUSH	AX
	CALL	FntBaseLine
	POP	CX	; Char Height
	MOV	DX, CX	; Save char height in DX
	SUB	CX, AX	; Descender distance = charHeight-baseLine
	MOV	descender, CX
	ADD	DX, CX	; Add descender distance to char height
	ADD	DX, 3	; Add space for upper/lower gaps and line

	SUB	rTopLftY, DX
	ADD	rExtentY, DX	; Update rectangle to include title area

	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX
	CALL	WinSetClip	; Set clipping rect to the windows width

	LES	AX, DS:wiTitlePtr
	PUSH	ES
	PUSH	AX
	PUSH	DS:wiTitleLen
	CALL	FntCharsWidth
	NEG	AX
	ADD	AX, rExtentX
	SAR	AX, 1
	ADD	AX, rTopLftX	; Draw x = topLftX + (extentX - textWidth)/2

	MOV	BX, rTopLftY
	ADD	BX, descender
	INC	BX	; Draw y = topLftY + descender + 1 (for gap)

	LES	DX, DS:wiTitlePtr
	PUSH	ES
	PUSH	DX	; @chars
	PUSH	DS:wiTitleLen	; len chars
	PUSH	AX	; x location
	PUSH	BX	; y location
	CALL	WinDrawChars

	LEA	AX, clpTopLftX
	PUSH	SS
	PUSH	AX
	CALL	WinSetClip	; Reset old clipping rectangle

DrawFrameAroundWindow:
	PUSH	DS:wiFrameType	; frameType
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; @ local rect
	XOR	AX, AX
	PUSH	AX	; diameter of corner is 0 (square rectangle)
	CALL	WinDrawRectangleFrame	; Draw the frame around the window

	MOV	CS:drawWindow, DS	; Restore draw window relative coordinates
	MOV	SP, BP
	POP	BP
	POP	DS
	RET
WinDrawWindowFrame ENDP

PURGE descender, rTopLftX, rTopLftY, rExtentX, rExtentY
PURGE clpTopLftX, clpTopLftY, clpExtentX, clpExtentY
$EJECT

; WinEraseWindow: PROCEDURE CLEAN;

WinEraseWindow PROC NEAR
	MOV	ES, CS:drawWindow

	PUSH	ES:wiBoundsExtentY
	PUSH	ES:wiBoundsExtentX
	XOR	AX, AX
	PUSH	AX
	PUSH	AX	; Rectangle to erase is at (0,0, extX, extY)
	MOV	SI, SP	; offset of the local rectangle being erased
	PUSH	SS
	PUSH	SI	; @rectangle
	PUSH	AX	; corner diameter = 0 (no rounded corners)
	CALL	WinEraseRectangle

	ADD	SP, 8	; Restore stack pointer
	RET
WinEraseWindow ENDP
$EJECT

; WinSaveBits: PROCEDURE (pSrcRect, pError) WORD;

pSrcRect EQU DWORD PTR [BP+10]
pError   EQU DWORD PTR [BP+6]

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

WinSaveBits PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, pSrcRect
	PUSH	DS:[SI].rectExtentY
	PUSH	DS:[SI].rectExtentX
	PUSH	DS:[SI].rectTopLeftY
	PUSH	DS:[SI].rectTopLeftX

	PUSH	rExtentX
	PUSH	rExtentY
	MOV	AL, screenFormat
	PUSH	AX
	LDS	AX, pError
	PUSH	DS
	PUSH	AX
	CALL	WinCreateOffscreenWindow
	LDS	SI, pError
	CMP	WORD PTR DS:[SI], 0	; If error <> eOK then
	JNE	WinSaveBitsRet	; return a window id of zero

	PUSH	AX	; Save window ID of saved area/window

	PUSH	CS:drawWindow	; Source is draw window by definition
	PUSH	AX	; Make destination be offscreen window
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; Source rectangle = copy of rect passed in
	XOR	AX, AX
	PUSH	AX
	PUSH	AX	; Destination point = (0,0)
	CALL	WinCopyRectangle

	POP	AX	; Restore window ID of saved area/window

WinSaveBitsRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	8
WinSaveBits ENDP

PURGE pSrcRect, pError
PURGE rTopLftX, rTopLftY, rExtentX, rExtentY
$EJECT

; WinRestoreBits: PROCEDURE (windowID, dstTopLeftX, dstTopLeftY, pError);

windowID    EQU  WORD PTR [BP+14]
dstTopLeftX EQU  WORD PTR [BP+12]
dstTopLeftY EQU  WORD PTR [BP+10]
pError      EQU DWORD PTR [BP+6]

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

WinRestoreBits PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, 8	; Make room for rectangle on the stack

	LES	DI, pError
	MOV	AX, eInvalidWindowID
	STOSW	; error = eInvalidWindowID

	MOV	BX, windowID
	OR	BX, BX	; If windowID = invalid window ID
	JZ	WinRestoreBitsRet	; then return with error already set

	MOV	DS, BX
	LEA	SI, DS:wiBoundsTopLeftX
	PUSH	SS
	POP	ES
	LEA	DI, rTopLftX
	CLD
	MOVSW	; src.topLeft.x = altWindow.topLeftX
	MOVSW	; src.topLeft.y = altWindow.topLeftY
	MOVSW	; src.extent.x  = altWindow.extentX
	MOVSW	; src.extent.y  = altWindow.extentY

	PUSH	BX	; Make source be offscreen window
	PUSH	CS:drawWindow	; Destination is draw window by definition
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX	; Source rectangle
	PUSH	dstTopLeftX	; Destination x = dstTopLeftX
	PUSH	dstTopLeftY	; Destination y = dstTopLeftY
	CALL	WinCopyRectangle

	PUSH	windowID	; window to free / remove from window list
	XOR	AX, AX
	PUSH	AX	; eraseWindow = FALSE
	LES	AX, pError
	PUSH	ES
	PUSH	AX	; @error
	CALL	WinDeleteWindow

WinRestoreBitsRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	10
WinRestoreBits ENDP

PURGE windowID, dstTopLeftX, dstTopLeftY, pError
PURGE rTopLftX, rTopLftY, rExtentX, rExtentY


CODE ENDS

     END
