*******************************************************************************
*
* Jiffy Windows - Pixel Transfer Demonstration
*
* (C)  Copyright Apple Computer, Inc. 1988-1990
* All rights reserved.
*
* Developer Technical Support Apple II Sample Code
*
* by Keith Rollin
*
*******************************************************************************

*******************************************************************************
*
doClose             start
*
* Description:      Closes the top window by hiding it. If it closes the last
*                   window on the screen, the Close menu item is disabled.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

; While closing the window, we have to get a little tricky. If we are closing
; the Odyssey window, then we have to enable the "Read a Book" menu item. If
; we are closing any of the Test window, we have to enable the "Open Test
; Windows" menu item. We perform this check by getting the pointer to the top
; window, and comparing it to the stored pointer to the Odyssey window. If they
; match, then we know we are closing the Odyssey window. If not, then we assume
; that we are closing a Test window. We don't have to worry about NDA windows
; getting in the way as those are taken care of TaskMaster before we ever get
; here.
;

                    pha                 ; get the front window
                    pha
                    _FrontWindow

                    stz WindType        ; Assume we're closing a test window.

                    lda 1,s             ; Check to see if we are closing
                    cmp OdyWindow       ; the Odyssey window
                    bne HideIt
                    lda 3,s
                    cmp OdyWindow+2
                    bne HideIt

                    inc WindType        ; set flag to say we closed Odyssey
HideIt              ANOP
                    _HideWindow         ; hide the top window

                    lda WindType        ; did we close a test window?
                    bne EnableOdy       ; no, enable "Read a Book"

                    PushWord #258       ; yes, so re-enable that menu item
                    _EnableMItem        ; to let us re-open it.
                    bra CheckForMoreWindows

EnableOdy           ANOP
                    PushWord #259       ; re-enable Read A Book menu item
                    _EnableMItem        ; to let us re-open it.

CheckForMoreWindows ANOP
                    pha                 ; any more front windows?
                    pha
                    _FrontWindow

                    plx
                    pla
                    bne done            ; yes, so leave
                    txa
                    bne done            ; yes, so leave

                    PushWord #255       ; no, so disable close item
                    _DisableMItem

done                ANOP
                    rts
WindType            ds 2
                    end

*******************************************************************************
*
CacheContent        start
*
* Description:      This routine draws the contents of the window. Since it
*                   is called from TaskMaster, we need to set the Direct Page
*                   and Data Bank registers. The we prepare to draw the window.
*                   First we check to see if there is a handle in the window's
*                   RefCon, and make sure that it is not purged. If it is
*                   still valid, it is used to update the window. If not, then
*                   the window is drawn by hand.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:
*                   Import GetOffScreenBuffer
*                   Import DrawByHand
*
* Entry Points:
*                   Export oldPort      ; used by DrawByHand
*
*******************************************************************************
                    using Globals

;
; CacheContents is called from the Window Manager to update the contents
; of the window. Therefore, the Direct Page and Data Bank Register may
; not be set to what our application had them set to. Save the settings
; as the Window Manager had them, and then set them up the way we like
; them. That way we can use local (2-byte absolute) addressing, and can
; access our Direct Page variables.

                    phd                 ; Save caller's direct page
                    phb                 ; Save caller's data bank register

                    phk                 ; switch data bank to Program Bank
                    plb
                    lda MyDP            ; switch Direct Page to application's
                    tcd

SetUp               ANOP

; Get the port of the window that we have to update

                    pha                 ; space for result of GetWRefCon
                    pha
                    pha                 ; space for result of GetPort
                    pha
                    _GetPort

                    lda 1,s             ; save the current port for later
                    sta oldPort         ; when we leave this routine. the port
                    lda 3,s             ; gets changed if we have to draw into
                    sta oldPort+2       ; the offscreen buffer.

                    _GetWRefCon         ; Get refcon, which holds a handle to
                    PullLong Deref      ;   the cache record.

                    ldy #4              ; Lock the handle to the cache record.
                    lda [Deref],y       ; We do this using the method documented
                    ora #attrLocked     ; in the description of HLock in the
                    sta [Deref],y       ; memory manager chapter.
                    dey
                    dey
                    lda [Deref],y       ; Dereference it. Get a pointer to the
                    sta CPPtr+2         ; cache record.
                    lda [Deref]
                    sta CPPtr

                    ldy #4              ; See if we have cached the contents.
                    lda [CPPtr],y       ; We determine this by seeing if the
                    iny                 ; pointer is a NULL pointer.
                    iny
                    ora [CPPtr],y       ; see if the pointer is $0000 0000
                    bne haveBuffer      ; no it's not, so update from offscreen

; We don't have an offscreen buffer, so this is what we are going to do. First
; save the old port (the window that we are updating). Then, try to get an
; offscreen GrafPort for buffering. If that is successful, then the port will
; be set to it. Regardless of the outcome, however, we will draw the entire
; contents to whatever is active. Then we check the status of the
; GetOffScreenBuffer call. If it succeeded, then copy the buffer's contents
; to the window. If it didn't then what we drew appeared on the screen anyway,
; and we're done.


                    lda oldPort         ; pass the window pointer in X & Y
                    ldx oldPort+2
                    jsr GetOffScreenBuffer ; changes the port if successful
                    php                 ; save the status of this call
                    jsl DrawByHand      ; draw the contents to cache or screen

                    PushLong oldPort    ; reset the port to window port
                    _SetPort

                    plp                 ; Did offscreen caching occur?
                    bcc SetUp           ; yes - now try again
                    bcs done            ; no - we drew to screen - so exit.

haveBuffer          ANOP
;
; We have an offscreen buffer. Prepare the stack for a PPToPort call.
;
                    ldy #6              ; push on source LocInfo pointer
                    PushWord [CPPtr],y  ; This pointer points to the LocInfo
                    dey                 ; of the OffScreen buffer, and is
                    dey                 ; stored in the Cached Record.
                    PushWord [CPPtr],y

;
; We push on a pointer to VisRect here, but haven't yet initialized it! But
; don't worry. We will fill in VisRect down below.
;
                    PushLong #VisRect   ; pass the pointer to the visrect.

                    lda oldPort         ; get the pointer to the dest port.
                    sta Deref
                    lda oldPort+2
                    sta Deref+2

; Now we need to tell PPToPort what part of the screen to update. This is
; done by looking at the VisRgn (which contains the intersection of the
; update region and the VisRgn). The format of a region is:
;
;  WORD: rgnSize  Size of the region
;  RECT: rgnBBox  rectangle the just surrounds the region
;  XXXX: rgnData  region data the would get me fired if I told you what it was.
;
; By using rgnBBox as our source/destination rectangle, we make PPToPort do
; as little work as possible; There won't be anything to update outside of
; that rectangle.
;
                    ldy #ovisRgn+2      ; Find the visible rectangle.
                    lda [Deref],y       ; First get the handle to the visRgn.
                    sta temp2+2
                    dey
                    dey
                    lda [Deref],y
                    sta temp2

                    ldy #2              ; Deref the VisRgn handle
                    lda [temp2],y
                    sta temp1+2
                    lda [temp2]
                    sta temp1

                    ldy #8              ; finally copy the rect out of the
loop                lda [temp1],y       ; VisRgn into a local variable.
                    sta VisRect-2,y
                    dey
                    dey
                    bne loop

;
; VisRect now holds the rectangle that surrounds the entire area to udpate.
; This makes that "PushLong #VisRect" above point to valid data!
;
; Now we push on the destination for the PPToPort command. Since PPToPort
; doesn't do any scaling, all we need is the upper-left hand point of the
; destination; the lower left corner is calculated by QuickDraw by using the
; height and width of the source.

                    PushLong VisRect    ; use the upper left hand corner of the
*                                       ; source rectangle as the destination.

                    PushWord #modeCopy  ; Set the transfer mode.
                    _PPToPort           ; Blit those bits!!!

                    pha                 ; push on room for GetWRefcon result
                    pha
                    PushLong oldPort
                    _GetWRefCon
                    _HUnlock            ; unlock the Cache record handle

done                ANOP
                    plb                 ; restore caller's data bank register
                    pld                 ; restore caller's direct page reg.

                    rtl                 ; return to TaskMaster

oldPort             entry
                    ds 4
VisRect             ds 8

                    end

                    EJECT
*******************************************************************************
*
SlowDraw            start
*
* Description:      This routine calls the routine that draws the test window
*                   contents. It takes the place of the 'CacheContents'
*                   routine for Windows 1 and 2. It does NOT cache the window
*                   contents, and is used to show the difference between
*                   cached and non-cached updating.
*
*
* Inputs:
*
* Outputs:
*
* External Refs:
*                   Import DrawContent
*
* Entry Points:
*
*******************************************************************************
                    using Globals

;
; SlowDraw is called from the Window Manager to update the contents
; of the window. Therefore, the Direct Page and Data Bank Register may
; not be set to what our application had them set to. Save the settings
; as the Window Manager had them, and then set them up the way we like
; them. That way we can use local (2-byte absolute) addressing, and can
; access our Direct Page variables.
;
                    phd
                    phb

                    phk
                    plb
                    lda MyDP
                    tcd

                    pha
                    pha
                    _GetPort
                    pla
                    plx

                    jsl DrawContent

                    plb
                    pld

                    rtl
                    end

                    EJECT
*******************************************************************************
*
DrawByHand          start
*
* Description:      The contents need to be created for the first time. The
*                   address of theroutine that doesthis is in a parameter
*                   block pointed to by the RefCon. Get this address, push it
*                   on the stack, and load up A and X with the pointer to the
*                   window so that the drawing routine can have information
*                   about it (the actual port it is drawing to is an offscreen
*                   port, and does not have window information in it, like the
*                   window title).
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:
*                   Import oldPort
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    ldy #1              ; Get bytes 1 and 2 of drawing routine.
                    PushWord [CPPtr],y  ; push bytes 1 and 2 on.
                    phb                 ; push one byte onto the stack.
                    lda [CPPtr]         ; Get bytes 0 and 1 into Acc.
                    sta 1,s             ; now store bytes 0 and 1 on the stack
                    lda oldPort         ; set up A & X for the drawing routine
                    ldx oldPort+2
                    rtl                 ; RTL to the drawing routine

                    end

                    EJECT
*******************************************************************************
*
DrawOdyRtn          start
*
* Description:      This routine posts the first few pages of Sculley's book
*                   into the Cache buffer or onto the screen. It uses TextBox2
*                   and takes a while, so tell the user by showing a watch.
*
*                   None of what I do here requires the use of my Direct Page
*                   or 2-byte absolute addressing, so I don't need to change
*                   the Direct Page or Data Bank Registers. However, other
*                   routines that perform updating will normally need to change
*                   these. The CacheContents routine demonstrates the
*                   technique.
*
*
* Inputs:           A = low order word of window pointer
*                   X = high order word
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

;
; --- We'll be sitting here for a while, so tell the user.
;
                    _WaitCursor

                    jsl xorPoint
                    PushLong #Odyssey   ; push pointer to text
                    PushWord #OdysseyEnd-Odyssey            ; length of text
                    PushLong #OdyRect   ; bounds to fit the text into
                    PushWord #0         ; Fill justify
                    _LETextBox2         ; draw it all!

                    _InitCursor         ; set cursor back to arrow.

* This routine is necessary to work around a bug in system disk 5.0.  The fastFont
* routine has a problem with pixelmaps that are larger than 64k.  The fastFont
* routine is used only when the regions are rectangular and no region clipping
* is involved.  If either the clipRgn or visRgn is not rectangular, then fastFont
* is not used.  This method of "turning it off" is what we will use to work around
* the bug.  Note that the visRgn is made non-rectangular.  This would normally
* be a bad thing.  However, setting the clipRgn non-rectangular does not work in
* this case because LETextBox2 sets the clipRgn to a rectangular region the size
* of the rectangle for the text.  So, setting the clipRgn to be non-rectangular
* is defeated by the LETextBox2 routine.  This is why the visRgn is used.
* Also note that for off-screen grafPorts, the visRgn IS your property, so it
* is okay to modify it.  Also note that the modification to the visRgn is undone
* after the LETextBox2 call.

xorPoint            pha
                    pha
                    _NewRgn             ; Temp region handle now on stack.

                    lda 3,s             ; Duplicate region handle on stack.
                    pha
                    lda 3,s
                    pha

                    ldy #-1             ; Push rect of -1,-1,0,0
                    phy
                    phy
                    iny
                    phy
                    phy
                    _SetRectRgn         ; Temp region handle still on stack.

                    lda 3,s             ; Duplicate region handle on stack.
                    pha
                    lda 3,s
                    pha

                    pha
                    pha
                    _GetVisHandle       ; visRgn handle on stack.

                    lda 3,s             ; Duplicate visRgn handle on stack.
                    pha
                    lda 3,s
                    pha

                    _XorRgn             ; Xor point in visRgn.
                    _DisposeRgn         ; Dispose of visRgn.

                    rtl                 ; Back to caller.

OdyRect             dc i2'0,10,790,590'

                    end

                    EJECT
*******************************************************************************
*
DrawContent         start
*
* Description:      This is the real procedure that draws the contents of the
*                   Test windows. It merely draws the name of the window many
*                   times.
*
*
* Inputs:           A = low order word of window pointer
*                   X = high order word
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    sta myPort          ; save window pointer
                    stx myPort+2

                    pha                 ; room for window title pointer
                    pha
                    phx                 ; push on window pointer
                    pha
                    _GetWTitle          ; get the window's title
                    PullLong wTitle

                    lda #10             ; now initialize the X and Y variables
                    sta yLoc            ; that we use to draw the strings.
                    lda #0
                    sta xLoc
                    lda #10             ; loop 10 times down the screen
                    sta bigCounter
loop                ANOP
                    PushWord xLoc       ; set up for the Moveto
                    PushWord yLoc
                    _MoveTo

                    lda #8              ; loop 8 times across the screen
                    sta littleCounter
loop2               ANOP
                    PushLong wTitle     ; put window title pointer onto stack
                    _DrawString         ; and draw the string
                    dec littleCounter   ; update our horizontal counter
                    bpl loop2

                    lda yLoc            ; update the X/Y coordinates
                    clc
                    adc #10
                    sta yLoc
                    lda xLoc
                    sec
                    sbc #20
                    sta xLoc
                    dec bigCounter      ; update our vertical counter
                    bpl loop            ; ...and check it.

                    rtl

xLoc                ds 2                ; where to start drawing string next
yLoc                ds 2                ; where to start drawing string next
littleCounter       ds 2                ; counter across the screen
bigCounter          ds 2                ; counter down the screen

myPort              ds 4
wTitle              ds 4

                    end

                    EJECT
*******************************************************************************
*
GetOffScreenBuffer  start
*
* Description:      Get a handle to an offscreen GrafPort. If _NewHandle call
*                   fails, set the carry and return. If it succeeds, create a
*                   new bitmap for it, install the bitmap, and initialize the
*                   appropriate fields. Finally, install the pointer to the
*                   GrafPort in the cache record for the window, clear the
*                   carry, and return with the port set to the new port.
*
*                   This routine is entered with the pointer to the window
*                   manager port of the window we are updating. In case we
*                   cannot get an offscreen buffer, we can reset the port to
*                   this.
*
*
* Inputs:           A = low word of window pointer of window we are updating
*                   X = high word
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    sta origPort        ; save the window manager port
                    stx origPort+2

;
; --- Get a memory for a new grafport
;
                    stz newPort         ; assume that we don't get it.
                    stz newPort+2
                    pha                 ; space for the new handle
                    pha
                    PushLong #portSize
                    PushWord MyID
                    PushWord #attrFixed ; plus 'attrNoCross' if on old ROMs
                    PushLong #$0000
                    _NewHandle
                    PullLong Deref
                    bcs error

                    ldy #2              ; Dereference and lose the handle
                    lda [Deref],y       ; (we only need a pointer)
                    tax
                    lda [Deref]
                    sta newPort
                    stx newPort+2

                    phx                 ; Now initialize it as a GrafPort
                    pha
                    _OpenPort
                    bcs error
;
; --- We now have a port to play with, but its PixImage is the
; --- screen. Let's change that.
;
                    jsr AddPixMap
                    bcs error

; Everything is all set up. The current port should still be the one
; we just created with the _OpenPort above. All we have to do is install
; the pointer to it into the cacheParms block for the Window.

                    ldy #4
                    lda newPort
                    sta [CPPtr],y
                    iny
                    iny
                    lda newPort+2
                    sta [CPPtr],y

                    clc
                    rts
error               ANOP
                    pha                 ; Error occured. Get the handle to the
                    pha                 ; port that we created; dispose of it.
                    PushLong newPort
                    _FindHandle
                    bcs skip
                    _DisposeHandle
skip                ANOP
                    PushLong origPort
                    _SetPort
                    sec
                    rts

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This routine adds an offscreen PixMap to the Grafport pointed to by
; 'newPort'.
;
AddPixMap           ANOP
                    jsr CalcScrnDims    ; get values for LocInfo

                    pha                 ; get a PixImagePtr of right size
                    pha
                    PushLong scrnSize
                    PushWord MyID
                    PushWord #attrFixed ; plus 'attrNoCross' if on old ROMs
                    PushLong #$0000
                    _NewHandle
                    PullLong Deref
                    bcs error2

;
; Get the Window Pointer into a Direct Page location (temp1) for
; easy access later.
;
                    lda newPort
                    sta temp1
                    lda newPort+2
                    sta temp1+2

                    ldy #2              ; Dereference and lose the handle
                    lda [Deref],y       ; to the PixImage (it's locked)
                    tax
                    lda [Deref]         ; (pointer in A and X)

                    ldy #optrToPixImage ; install the new PixImage
                    sta [temp1],y
                    iny
                    iny
                    txa
                    sta [temp1],y

                    ldy #owidth         ; set the 'rowBytes' value
                    lda rowBytes
                    sta [temp1],y

                    ldy #oboundsRect+4  ; update the size of the boundsRect
                    lda scrnHeight
                    sta [temp1],y
                    iny
                    iny
                    lda scrnWidth
                    sta [temp1],y

                    ldy #oportRect+4    ; and the size of the portrect
                    lda scrnHeight
                    sta [temp1],y
                    iny
                    iny
                    lda scrnWidth
                    sta [temp1],y

;
; Set the VisRgn and ClipRgn to the size of the port we just
; created. This is done by calling ClipRect with the portRect,
; and then copying the resulting ClipRgn to the VisRgn.
;
                    lda temp1           ; Create a pointer to the portRect
                    clc                 ; in the Window Record (remember,
                    adc #oportRect      ; temp1 points to the WindRec).
                    tax
                    lda temp1+2
                    adc #0
                    pha                 ; push on one set for _EraseRect
                    phx
                    pha                 ; push on this set for _ClipRect
                    phx
                    _ClipRect

                    ldy #oclipRgn+2     ; now copy the ClipRgn to the VisRgn
                    PushWord [temp1],y
                    dey
                    dey
                    PushWord [temp1],y
                    _SetVisRgn

                    _EraseRect          ; rect ptr left on from way before.

                    clc
                    rts

error2              ANOP
                    sec
                    rts

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Calculate the screen size based on the general formula:
;
;    rowBytes := (((((right - left) * theDepth) + 7) DIV 8) + 7) AND ~%111
;
; in English, or what passes for it when I type, this says: take the width
; of the GrafPort, and multiply it by the depth (number of bits per pixel)
; of the screen. Add 7 to this and divide by 8 to get the number of bytes
; that this takes up (add 7 effectively rounds up, to counter the DIV 8's
; rounding down). After we have the number of bytes, we have to make sure that
; this is an "even multiple of 8", as it states in the QuickDraw Chapter. This
; is done by adding 7 (again, to round up) and stripping off the low 3 bits,
; which performs the rounding down to the nearest multiple of 8.
;
; This formula can be slightly reduced to:
;
;    rowBytes := ((((right - left) * theDepth) + 63) DIV 8) AND ~%111
;
; which is used in specific:
;
;    rowBytes := (((scrnWidth * 2) + 63) DIV 8) AND ~%111
;
CalcScrnDims        ANOP
                    ldy #10             ; get the screen width from cacheParms
                    lda [CPPtr],y
                    sta scrnWidth
                    asl a               ; times 2
;                   asl a               ; use this also if in 320 mode
                    clc                 ; add 63
                    adc #63
                    lsr a               ; divide by 8
                    lsr a
                    lsr a
                    and #%11111000      ; and with ~%111
                    sta rowBytes
                    pha                 ; Now prepare to multiply to get
                    pha                 ; the whole screen size.
                    pha                 ; Multiplier
                    dey
                    dey
                    lda [CPPtr],y
                    sta scrnHeight
                    pha                 ; Multiplicand
                    _Multiply
                    PullLong scrnSize
                    rts

origPort            ds 4
newPort             ds 4

scrnSize            ds 4
scrnHeight          ds 2
scrnWidth           ds 2
rowBytes            ds 2

                    end

                    copy Jiffy.stds.asm

                    END
