*******************************************************************************
**
**  $Id: nice.S,v 1.8 92/11/03 17:14:16 tf Exp $
**
**  NiceOpenWindow(), NiceCloseWindow() with (DICE) C interface code
**  written by Tobias Ferber 1992 with a little help and the idea from
**  Harald Kunze.
**
*******************************************************************************

  include "exec/exec_lib.i"
  include "intuition/intuition_lib.i"
  include "intuition/screens.i"
  include "graphics/graphics_lib.i"
  include "graphics/rastport.i"
  include "graphics/layers.i"
  include "graphics/layers_lib.i"

  LIBRARY_VERSION: equ 33

  dc.b "$Id: nice.S,v 1.8 92/11/03 17:14:16 tf Exp $",0 ; rcs ID
  even

  section NiceInterface,code

  debug:    set 1   ; produce debug information (see below)
; moderate: set 1
  wuslon:   set 1   ; draw two frames instead of one

  ifd debug
  xdef    NiceOpenWindow
  xdef    NiceCloseWindow
  xdef    OpenRectangle
  xdef    CloseRectangle
  xdef    Bresenham
  xdef    cc_open
  xdef    cc_close
  xdef    drawframe
  endc ;debug

  xdef _NiceOpenWindow
  xdef _NiceCloseWindow
  xdef _NiceOpenSpeed
  xdef _NiceCloseSpeed
  xdef _OpenRectangle
  xdef _CloseRectangle

_NiceOpenWindow:

  movem.l D1-D7/A0-A6,-(sp)
  link    A5,#-4                      ; A5 -> -(sp); sp -> A5; sp +(-4) -> sp
  clr.l   -4(A5)
  lea     IntuitionName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,_IntuitionBase
  beq     \exit
  lea     GraphicsName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,_GfxBase
  beq     \nogfx
  lea     LayersName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,_LayersBase
  beq     \nolayers

  move.w  _NiceOpenSpeed,D0
  move.l  68(sp),A0                   ; fish out the NewWindow struct ptr
  bsr     NiceOpenWindow
  move.l  D0,-4(A5)                   ; save our Window ptr

  move.l  _LayersBase,A1
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)
\nolayers:
  move.l  _GfxBase,A1
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)
\nogfx:
  move.l  _IntuitionBase,A1
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)
  move.l  -4(A5),D0                   ; return our Window ptr (in D0)
\exit:
  unlk    A5
  movem.l (sp)+,D1-D7/A0-A6
  rts
  

_NiceCloseWindow:

  movem.l D0-D7/A0-A6,-(sp)
  lea     IntuitionName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,_IntuitionBase
  beq     \exit
  lea     GraphicsName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,_GfxBase
  beq     \nogfx
  lea     LayersName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,_LayersBase
  beq     \nolayers

  move.w  _NiceCloseSpeed,D0
  move.l  64(sp),A0                   ; Window
  bsr     NiceCloseWindow

  move.l  _LayersBase,A1
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)
\nolayers:
  move.l  _GfxBase,A1
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)
\nogfx:
  move.l  _IntuitionBase,A1
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)
\exit:
  movem.l (sp)+,D0-D7/A0-A6
  rts


_OpenRectangle:

  movem.l D0-D7/A0-A6,-(sp)
  lea     GraphicsName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,_GfxBase
  beq     \nogfx

  move.l  64(sp),A1                   ; (8+7)*4 +4
  move.l  68(sp),D0
  move.l  72(sp),D1
  move.l  76(sp),D2
  move.l  80(sp),D3
  move.l  84(sp),D4

  bsr     OpenRectangle
  move.l  _GfxBase,A1
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)

\nogfx:
  movem.l (sp)+,D0-D7/A0-A6
  rts


_CloseRectangle:

  movem.l D0-D7/A0-A6,-(sp)
  lea     GraphicsName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,_GfxBase
  beq     \nogfx

  move.l  64(sp),A1                   ; (8+7)*4 +4
  move.l  68(sp),D0
  move.l  72(sp),D1
  move.l  76(sp),D2
  move.l  80(sp),D3
  move.l  84(sp),D4
  bsr     CloseRectangle

  move.l  _GfxBase,A1
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)

\nogfx:
  movem.l (sp)+,D0-D7/A0-A6
  rts


  section NiceInterface,data

_IntuitionBase:  ds.l 1
_GfxBase:        ds.l 1
_LayersBase:     ds.l 1


IntuitionName:   INTNAME
GraphicsName:    GRAFNAME
LayersName:      dc.b "layers.library",0

  even

_NiceOpenSpeed:  dc.w 8  ; default values
_NiceCloseSpeed: dc.w 8

NiceRastPort:    ds.b rp_SIZEOF       ; copy of pop-up screen's RP


*******************************************************************************
**
** Window= NiceOpenWindow ( NewWindow, OpenSpeed )       [NiceRastPort needed]
**   D0                        A0         D0               
**
*******************************************************************************

  section Nice,code

NiceOpenWindow:

  movem.l D1-D7/A0-A6,-(sp)

  tst.w   D0
  beq     \noframes                   ; ==> perform normal OpenWindow()

  move.w  D0,D4                       ; D4 is expected to be unchanged
  link    A5,#-8
  move.l  A0,-4(A5)                   ; save NewWindow

  move.l  nw_Screen(A0),A0            ; have we got a Screen? (CUSTOMSCREEN ?)
  cmpa.l  #0,A0
  bne     \copyrp

  ; Find the Screen on which we're going to pop up

  moveq   #0,D0                       ; Lock for IntuitionBase
  move.l  _IntuitionBase,A6
  jsr     _LVOLockIBase(A6)           ; ... now it's mine !

  move.l  _IntuitionBase,A0
  move.l  ib_FirstScreen(A0),-(sp)    ; start with IntuitionBase->FirstScreen

  move.l  D0,A0                       ; Lock from LockIBase()
  move.l  _IntuitionBase,A6
  jsr     _LVOUnlockIBase(A6)

  move.l  (sp)+,A0                    ; IntuitionBase->FirstScreen
  move.l  -4(A5),A1                   ; NewWindow ptr
  move.w  nw_Type(A1),D1              ; NewWindow->Type
  and.w   #SCREENTYPE,D1              ; ... just to be sure

\findscreen:
  cmpa.l  #0,A0
  beq     \noscreen
  move.w  sc_Flags(A0),D0
  and.w   #SCREENTYPE,D0              ; ... since we did it with D1
  and.w   D1,D0                       ; mask flags out from NewWindow->Type
  tst.w   D0
  bne     \copyrp                     ; yeah! this is a Screen we like ;)
  move.l  sc_NextScreen(A0),A0
  bra     \findscreen

  ; Get a copy.  Don't mess with somebody else's RastPort !

\copyrp:
  move.l  A0,-8(A5)                   ; save Screen pointer
  lea     sc_RastPort(A0),A0          ; source= &Screen->RastPort
  lea     NiceRastPort,A1             ; dest= &NiceRastPort
  move.l  #rp_SIZEOF,D0               ; numbytes= sizeof(struct RastPort)
  move.l  (_SysBase).W,A6
  jsr     _LVOCopyMem(A6)

  moveq   #RP_COMPLEMENT,D0
  lea     NiceRastPort,A1
  move.l  _GfxBase,A6
  jsr     _LVOSetDrMd(A6)             ; SetDrawMode(COMPLEMENT)

  move.l  -4(A5),A0                   ; NewWindow
  move.w  nw_DetailPen(A0),D0
  lea     NiceRastPort,A1
  move.l  _GfxBase,A6
  jsr     _LVOSetAPen(A6)             ; SetAPen(NewWindow->DetailPen)

  move.l  -8(A5),A0                   ; Screen
  lea     sc_LayerInfo(A0),A0         ; &Screen->LayerInfo
  move.l  _LayersBase,A6
  jsr     _LVOLockLayerInfo(A6)

  move.l  -4(A5),A0                   ; NewWindow
  move.w  nw_LeftEdge(A0),D0
  move.w  nw_TopEdge(A0),D1
  move.w  nw_Width(A0),D2
  move.w  nw_Height(A0),D3
  lea     NiceRastPort,A1
  bsr     OpenRectangle

  move.l  -8(A5),A0                   ; Screen (with locked LayerInfo)
  lea     sc_LayerInfo(A0),A0         ; &Screen->LayerInfo
  move.l  _LayersBase,A6
  jsr     _LVOUnlockLayerInfo(A6)

\noscreen:
  move.l  -4(A5),A0                   ; NewWindow
  unlk    A5

\noframes:
  move.l  _OldOpenWindow,A1
  cmpa.l  #0,A1
  beq     \notpatched
  move.l  _IntuitionBase,A6
  jsr     (A1)
  bra     \exit

\notpatched:
  move.l  _IntuitionBase,A6
  jsr     _LVOOpenWindow(A6)          ; NewWindow in A0

\exit:
  movem.l (sp)+,D1-D7/A0-A6
  rts

*******************************************************************************
**
** NiceCloseWindow ( Window, CloseSpeed )                [NiceRastPort needed]
**                     A0       D0
**
*******************************************************************************

NiceCloseWindow:

  movem.l D0-D7/A0-A6,-(sp)
  link    A5,#-14
  move.w  wd_LeftEdge(A0),-2(A5)
  move.w  wd_TopEdge(A0),-4(A5)
  move.w  wd_Width(A0),-6(A5)
  move.w  wd_Height(A0),-8(A5)
  move.w  D0,-10(A5)                  ; closing speed
  move.l  wd_WScreen(A0),A1
  move.l  A1,-14(A5)                  ; save Window->WScreen

  move.l  _OldCloseWindow,A1
  cmpa.l  #0,A1
  beq     \notpatched
  move.l  _IntuitionBase,A6
  jsr     (A1)
  bra     \nice

\notpatched:
  move.l  _IntuitionBase,A6
  jsr     _LVOCloseWindow(A6)         ; close the window

\nice:
  tst.w   -10(A5)                     ; closing speed
  beq     \noframes

  ; Get a copy.  Don't mess with somebody else's RastPort !

  move.l  -14(A5),A0                  ; Window->WScreen
  lea     sc_RastPort(A0),A0          ; source= &Window->WScreen->RastPort
  lea     NiceRastPort,A1             ; dest= &NiceRastPort
  move.l  #rp_SIZEOF,D0               ; numbytes= sizeof(struct RastPort)
  move.l  (_SysBase).W,A6
  jsr     _LVOCopyMem(A6)

  move.w  #RP_COMPLEMENT,D0
  lea     NiceRastPort,A1             ; copy of Window->WScreen->RastPort
  move.l  _GfxBase,A6
  jsr     _LVOSetDrMd(A6)             ; SetDrawMode(COMPLEMENT)

  move.l  -14(A5),A0                  ; Window->WScreen
  lea     sc_LayerInfo(A0),A0         ; &Window->WScreen->LayerInfo
  move.l  _LayersBase,A6
  jsr     _LVOLockLayerInfo(A6)

  move.w  -2(A5),D0                   ; LeftEdge
  move.w  -4(A5),D1                   ; TopEdge
  move.w  -6(A5),D2                   ; Width
  move.w  -8(A5),D3                   ; Height
  move.w  -10(A5),D4                  ; CloseSpeed
  lea     NiceRastPort,A1             ; copy of Window->WScreen->RastPort
  bsr     CloseRectangle

  move.l  -14(A5),A0                  ; Window->WScreen (with locked LayerInfo)
  lea     sc_LayerInfo(A0),A0         ; &Window->WScreen->LayerInfo
  move.l  _LayersBase,A6
  jsr     _LVOUnlockLayerInfo(A6)

\noframes:
  unlk    A5
  movem.l (sp)+,D0-D7/A0-A6
  rts

********************************************************************************
**
**  FUNCTIONs
**
**    OpenRectangle,CloseRectangle -- Open/Close a rectangle
**  
**  SYNOPSIS 
**
**     OpenRectangle( RastPort, LeftEdge, TopEdge, Width, Height, OpenSpeed )
**
**    CloseRectangle( RastPort, LeftEdge, TopEdge, Width, Height, CloseSpeed )
**                      A1        D0       D1       D2      D3       D4
**
**
**    struct RastPort *RastPort;
**    UWORD  LeftEdge, TopEdge;
**    UWORD  Width, Height;
**    UWORD  OpenSpeed,CloseSpeed;
**
**
**  DESCRIPTION
**
**    OpenRectangle draws the edges of a rectangle.  This rectangle will grow
**    out of its center in that way, that all 4 edges reach the borders at the
**    same time. (i.e. proportionally)
**
**    CloseRectangle proportionally shrinks it back to it's center again.
**
********************************************************************************

OpenRectangle:

  movem.l D0-D7/A0-A6,-(sp)
  lea     cc_open,A0
  bsr     Bresenham
  movem.l (sp)+,D0-D7/A0-A6
  rts

CloseRectangle:

  movem.l D0-D7/A0-A6,-(sp)
  lea     cc_close,A0
  bsr     Bresenham
  movem.l (sp)+,D0-D7/A0-A6
  rts

  ; Here we do the open/close stuff via Bresenham's algorithm for drawing
  ; a line.  Note that opening and closing works nearly the same way since
  ; we compute the distance to move as a vector (dx|dy).

Bresenham:

  tst.w   d4
  beq     \noframes                   ; no frames --> we're done

  ; compute the distances from the center to the border

  lsr.w   #1,d2                       ; D2= dx
  lsr.w   #1,d3                       ; D3= dy

  ; save the distances for cc_open

  move.w  d2,d6
  move.w  d3,d7

  ; comute the center of the rectangle in (D0|D1)

  add.w   d2,d0                       ; LeftEdge + Width/2
  add.w   d3,d1                       ; TopEdge + Height/2

  ; init our modulo

  move.w  d2,d5
  lsr.w   #1,d5                       ; D5= modulo (initially dx/2)

  jsr     (A0)
  bsr     drawframe

  ifd wuslon

  ; make a copy of our initial coords

  movem.l D0/A4,-(sp)
  lea     coords,A4
  move.l  0(A4),8(A4)
  move.l  4(A4),12(A4)
  movem.l (sp)+,D0/A4
  bsr     drawframe
  endc ;wuslon


\xloop:

  ifd  moderate
  cmp.w   D4,d2      ; if we are one x-step before the last then
  bge.s   \goonx     ; we set the speed to 1 in order to reach
  moveq   #1,D4      ; exactly the borders of our rectangle.
\goonx:              ; (compiles to nothing if moderate is undefined)
  endc ;moderate

  cmp.w   D4,d2                       ; while dx>speed     (better than dx > 0)
  ble     \alldone
  add.w   d3,d5                       ; modulo += dy

\yloop:

  ifd moderate
  cmp.w   D4,d3      ; if we are one y-step before the last then
  bge.s   \goony     ; we set the speed to 1 in order to reach
  moveq   #1,D4      ; exactly the borders of our rectangle.
\goony:              ; (compiles to nothing if moderate is undefined)
  endc ;moderate

  cmp.w   d2,d5                       ; while modulo>dx
  ble     \ydone
  cmp.w   D4,d3                       ; ... and dy>step    (better than dy > 0)
  ble     \ydone
  sub.w   d2,d5                       ; modulo -= dx
  sub.w   D4,d3                       ; dy -= D4 (speed)

  ifd wuslon
  bsr     swapframes
  endc ;wuslon
  bsr     drawframe
  jsr     (A0)                        ; compute coords
  bsr     drawframe
  bra     \yloop                      ; wend (y)

\ydone:
  sub.w   D4,d2                       ; dx -= D4 (speed)

  ifd wuslon
  bsr     swapframes
  endc ;wuslon
  bsr     drawframe
  jsr     (A0)                        ; compute coords
  bsr     drawframe
  bra     \xloop                      ; wend (x)

\alldone:
  bsr     drawframe                   ; delete the last box
  ifd wuslon
  bsr     swapframes
  bsr     drawframe
  endc ;wuslon

\noframes:
  rts

  ; Here we [c]ompute the [c]oords of our rectangle for later use by drawframe.
  ; In case of cc_open we turn the vector (dx|dy) that way that it points from
  ; the corners of our rectangle to it's center.  In the other case (cc_close)
  ; we make it point from the center to the corners.

cc_open:

  ; D0.W  x-pos of the box center    ( unmodified )
  ; D1.W  y-pos of the box center    ( unmodified )
  ; D2.W  dx                         ( unmodified )
  ; D3.W  dy                         ( unmodified )
  ; D6.W  rectangle width / 2        ( unmodified )
  ; D7.W  rectangle height / 2       ( unmodified )

  movem.l D2-D3,-(sp)
  neg.w   D2
  add.w   D6,D2
  neg.w   D3
  add.w   D7,D3
  bsr     cc_close
  movem.l (sp)+,D2-D3
  rts

cc_close:

  ; D0.W  x-pos of the box center    ( unmodified )
  ; D1.W  y-pos of the box center    ( unmodified )
  ; D2.W  dx                         ( unmodified )
  ; D3.W  dy                         ( unmodified )

  movem.l D4/A0,-(sp)

  lea     coords,A0                   ; base pointer of our array

  move.w  d0,d4
  sub.w   d2,d4
  move.w  d4,(A0)+                    ; x-dx (left edge)

  move.w  d0,d4
  add.w   d2,d4
  move.w  d4,(A0)+                    ; x+dx (right edge)

  move.w  d1,d4
  sub.w   d3,d4
  move.w  d4,(A0)+                    ; y-dy (top edge)

  move.w  d1,d4
  add.w   d3,d4
  move.w  d4,(A0)                     ; y+dy (bottom edge)

  movem.l (sp)+,D4/A0
  rts

  ; here we perform the wuslon stuff

  ifd wuslon
swapframes:
  movem.l D0/A4,-(sp)
  lea     coords,A4
  move.l  0(A4),D0
  move.l  8(A4),0(A4)
  move.l  D0,8(A4)
  move.l  4(A4),D0
  move.l  12(A4),4(A4)
  move.l  D0,12(A4)
  movem.l (sp)+,D0/A4
  rts
  endc ; wuslon

  ; here we draw the box with the coords in our array.

drawframe:                            ; RastPort pointer needed in A1

  movem.l D0-D7/A0-A6,-(sp)           ; ALL registers remain unchanged

  link    A5,#-4                      ; A5 is expected to be unchanged !
  move.l  A1,-4(A5)                   ; save the RastPort
  lea     coords,A4                   ; A4 is expected to be unchanged !

  move.w  0(A4),D0                    ; LeftEdge
  move.w  4(A4),D1                    ; TopEdge
  move.l  -4(A5),A1                   ; RastPort
  move.l  _GfxBase,A6
  jsr     _LVOMove(A6)

  move.w  2(A4),D0                    ; RightEdge
  move.w  4(A4),D1                    ; TopEdge
  move.l  -4(A5),A1                   ; RastPort
  move.l  _GfxBase,A6
  jsr     _LVODraw(A6)

  move.w  2(A4),D0                    ; RightEdge
  move.w  6(A4),D1                    ; BottomEdge
  move.l  -4(A5),A1                   ; RastPort
  move.l  _GfxBase,A6
  jsr     _LVODraw(A6)

  move.w  0(A4),D0                    ; LeftEdge
  move.w  6(A4),D1                    ; BottomEdge
  move.l  -4(A5),A1                   ; RastPort
  move.l  _GfxBase,A6
  jsr     _LVODraw(A6)

  lea     coords,A4
  move.w  0(A4),D0                    ; LeftEdge
  move.w  4(A4),D1                    ; TopEdge
  move.l  -4(A5),A1                   ; RastPort
  move.l  _GfxBase,A6
  jsr     _LVODraw(A6)

\exit:
  unlk    A5
  movem.l (sp)+,D0-D7/A0-A6
  rts

  section nice,data

coords:   dc.w  0,0,0,0               ; x-dx, x+dx, y-dy, y+dy
          dc.w  0,0,0,0               ; (wuslon)
          even

  section patch,code

  xdef _PatchOpenWindow
  xdef _PatchCloseWindow
  xdef _OldOpenWindow
  xdef _OldCloseWindow

NewOpenWindow:
  move.l  A0,-(sp)
  bsr     _NiceOpenWindow
  adda.l  #4,sp
  rts

NewCloseWindow:
  move.l  A0,-(sp)
  bsr     _NiceCloseWindow
  adda.l  #4,sp
  rts

_PatchOpenWindow:

  movem.l D0-D7/A0-A6,-(sp)
  link    A5,#-4
  lea     IntuitionName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,-4(A5)
  beq     \exit
  move.l  (_SysBase).W,A6
  jsr     _LVOForbid(A6)
  move.l  -4(A5),A1                   ; library= IntuitionBase
  suba.l  A0,A0
  move.w  #_LVOOpenWindow,A0          ; funcOffset= #_LVOOpenWindow
  move.l  #NewOpenWindow,D0           ; funcEntry= NewOpenWindow()
  move.l  (_SysBase).W,A6
  jsr     _LVOSetFunction(A6)
  move.l  D0,_OldOpenWindow           ; save oldFunc ptr
  move.l  (_SysBase).W,A6
  jsr     _LVOPermit(A6)
  move.l  -4(A5),A1                   ; IntuitionBase
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)  
\exit:
  unlk    A5
  movem.l (sp)+,D0-D7/A0-A6
  rts

_PatchCloseWindow:

  movem.l D0-D7/A0-A6,-(sp)
  link    A5,#-4
  lea     IntuitionName,A1
  moveq   #LIBRARY_VERSION,D0
  move.l  (_SysBase).W,A6
  jsr     _LVOOpenLibrary(A6)
  move.l  D0,-4(A5)
  beq     \exit
  move.l  (_SysBase).W,A6
  jsr     _LVOForbid(A6)
  move.l  -4(A5),A1                   ; library= IntuitionBase
  suba.l  A0,A0
  move.w  #_LVOCloseWindow,A0         ; funcOffset= #_LVOCloseWindow
  move.l  #NewCloseWindow,D0          ; funcEntry= NewCloseWindow()
  move.l  (_SysBase).W,A6
  jsr     _LVOSetFunction(A6)
  move.l  D0,_OldCloseWindow          ; save oldFunc ptr
  move.l  (_SysBase).W,A6
  jsr     _LVOPermit(A6)
  move.l  -4(A5),A1                   ; IntuitionBase
  move.l  (_SysBase).W,A6
  jsr     _LVOCloseLibrary(A6)
\exit:
  unlk    A5
  movem.l (sp)+,D0-D7/A0-A6
  rts

  section patch,data

_OldOpenWindow:   dc.l 0
_OldCloseWindow:  dc.l 0

  END
