;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        PAGE     60,132
        TITLE    Cursor Functions
        SUBTITLE Header
;/*****************************************************************************
;*
;* SOURCE FILE NAME = CURSORS.ASM
;*
;* DESCRIPTIVE NAME = Cursor Mangement functions.
;*
;*
;* VERSION      V2.0
;*
;* DATE         10/17/91
;*
;* DESCRIPTION  This module contains the routines which are required to manage
;*              the pointer image.  The actual pointer drawing primitive reside
;*              elsewhere.  All display drivers must support a "pointer" for
;*              the pointing device.  The pointer is a small graphics image
;*              which is allowed to move around the screen independantly of
;*              all other operations to the screen, and is normally bound to
;*              the location of the pointing device.  The pointer is
;*              non-destructive in nature, i.e.; the bits underneath the
;*              pointer image are not destroyed by the presence of the pointer
;*              image.  Logically, the pointer image isn't part of the physical
;*              display surface.  When a drawing operation coincides with the
;*              pointer image, the result is the same as if the pointer image
;*              wasn't there.  In reality, if the pointer image is part of the
;*              display surface it must be removed from memory before the
;*              drawing operation may occur, and redrawn at a later time.  This
;*              exclusion of the pointer image is the responsibility of the
;*              display driver.  If the pointer image is part of physical
;*              display memory, then all output operations must perform a hit
;*              test to determine if the pointer must be removed from display
;*              memory, and set a protection rectangle wherein the pointer must
;*              not be displayed.  The actual pointer image drawing routine
;*              must honor this protection rectangle by never drawing the
;*              pointer image within its boundary.
;*
;*              Restrictions:
;*
;*              The Window Manager is responsible for mapping generic pointers
;*              into the correct size for the driver, as specified in the display
;*              driver's resources.  The segments containing these routines and
;*              their data must be fixed in memory since they are called at
;*              interrupt time.  The exclusion rectangle is a serially reusable
;*              resource.  Only one may be defined at any given moment.  There is
;*              no check for this.
;*
;*
;* FUNCTIONS    InnerCheckCursor
;*              MoveCursor
;*              ExcludeTest
;*              InnerExcludeTest
;*              CheckCursor
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*
;*****************************************************************************/

        .386P
        .MODEL FLAT,SYSCALL

IFDEF SLITE

        .ERR Starlite adapter not supported.

ENDIF ; SLITE

;/*
;** Included files
;*/

INCL_DDIMISC       EQU 1
INCL_GPIERRORS     EQU 1
INCL_GPIPRIMITIVES EQU 1
INCL_NOBASEAPI     EQU 1
INCL_SAADEFS       EQU 1
INCL_WINERRORS     EQU 1
INCL_WINPOINTERS   EQU 1
OS2_NOBASEAPI      EQU 1
OS2_NOPMAPI        EQU 1
        INCLUDE PMGRE.INC

DINCL_BITMAP     EQU 1
DINCL_ENABLE     EQU 1
        INCLUDE DRIVER.INC
        INCLUDE EXTERN.INC
        INCLUDE PROTOS.INC
        include cursors.inc
        include egafam.inc

        INCLUDE ASSERT.MAC
        INCLUDE pointer.inc
        INCLUDE curstrc.inc


;/*
;** Structures & types
;*/


;/*
;** Prototypes for private functions
;*/

ExcludeTest PROTO SYSCALL

;/*
;** Data global to this module
;*/

_DATA SEGMENT DWORD PUBLIC FLAT 'DATA'

PUBLIC CurData
CurData CURSORSTRUCT <>

PUBLIC MCDevice
MCDevice MCDESCRIPTION <OFFSET MoveCursor,\
                                     (OFFSET MoveCursor40) - (OFFSET MoveCursor) + 10,\
                                     OFFSET CurData,\
                                     SIZEOF CurData>
_DATA ENDS

;/*
;** Functions
;*/

        SUBTITLE MoveCursor Function
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = MoveCursor
;*
;* DESCRIPTION   =
;*
;*       This is a private entry point within the display driver for the Window
;*       Manager.  It also serves as an entry point to CheckCursor if abs_x and
;*       abs_y are both 0x8000.
;*
;*       The current pointer location is set to the given coordinates.  If the
;*       pointer is visible, it will be moved to the new location.  If the
;*       pointer is off (a NULL pointer), or if the pointer has been excluded,
;*       then just the pointer location will be updated.
;*
;*       If the pointer is on and the new pointer position will cause the pointer
;*       to be excluded, it will be removed from the screen.
;*
;*     Warnings:
;*       The word immediately preceeding MoveCursors is the selector for the
;*       segment which contains all of the data required for pointer drawing.
;*       This segment will be locked in place so it can be called at interrupt
;*       time.
;*
;*       This routine must not be a NODATA routine.  The ring 0 interrupt handler
;*       will examine the first instruction looking for a MOV AX,xxxxH and skip
;*       it if it's the first instruction, passing the ring 0 selector in AX.
;*
;*       Registers Preserved:
;*             SI,DI,DS,BP
;*       Registers Destroyed:
;*             AX,BX,CX,DX,ES,FLAGS
;*
;* INPUT         = C Prototype:
;*                   void MoveCursor (SWORD abs_x, SWORD abs_y);
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

_PtrCode32 SEGMENT DWORD PUBLIC FLAT 'CODE'

ALIGN 4
InnerCheckCursor PROC SYSCALL
        LEA     EAX,CurData
        INVOKE  MoveCursor,8000h,8000h          ; Actually a CheckCursor call
        ret
InnerCheckCursor ENDP

;/*
;** ds=es=cs !=SS
;**
;*/

ALIGN 4
MoveCursor PROC SYSCALL USES EBX ESI EDI,
                abs_x:DWORD,
                abs_y:DWORD

mov     edi,eax                                  ;edi is now ptr to our data
ASSUME  EDI:PCURSORSTRUCT

        MOV     EAX,abs_x                        ;EAX = abs_x
        MOV     EBX,abs_y                        ;EBX = abs_y
        CMP     EAX,EBX                          ;CheckCursor iff abs_x == abs_y == 8000h
        JNZ     @F
        CMP     EAX,8000h
        JNZ     @F
        JMP     CheckCursor
@@:

;/*
;** Some day we'll have to call the semaphore code to enter in here!!!
;** It would be nice if we had a seperate entry point for a user calling us.
;*/

        CLI                                      ; Moving is a critical section
        SUB     EAX,[EDI].cxHot                  ; Get the new pointer position,
        MOV     [EDI].cxReal,EAX                 ; relative to its hot spot
        SUB     EBX,[EDI].cyHot
        MOV     [EDI].cyReal,EBX

        XOR     ECX,ECX                          ; Show that the screen is busy
        XCHG    [EDI].screen_busy,CL             ; and get the old status

        .ERRNZ  IS_BUSY

MoveCursor10:
        STI                                      ;Can allow interrupts now

;/*
;** The real pointer (x,y) has been updated and the screen_busy flag has been
;** set, and the old value of screen_busy is ready for testing.
;**
;** If the screen previously wasn't busy, then if the pointer is hidden or
;** excluded, no action will take place.  If the pointer isn't hidden or
;** excluded, then the pointer will be drawn at it's new location if the hit
;** test of the exclusion area succeeds.  If the hit test of the exclusion
;** area fails, then the pointer will be taken down and left down.
;**
;** If the screen was busy, the real pointer (x,y) will have been updated so
;** that when the pointer is drawn next, it will have the current (x,y) to
;** draw at.  The actions which could cause the screen to be busy are the
;** following:
;**
;**    Pointer is being taken down by the SetCursor routine:
;**
;**       If this is the case, then the pointer is to remain off until a new
;**       pointer is selected, at which time the timer code will try to bring
;**       the pointer back.
;**
;**    Pointer is being taken down by exclusion from an output function:
;**
;**       If this is the case, then the pointer will remain excluded until
;**       either one of two events occur:
;**
;**          1) A new pointer is selected, at which time the timer code will
;**             try to bring the pointer back.
;**
;**          2) The timer interrupt occured and determined that the pointer
;**             needed to be far_unexcluded ([screen_busy] must be false for the
;**             timer to redraw the pointer).
;**
;**    Pointer is being drawn by MoveCursor:
;**
;**       Hey, thats us!  If this is the case, after the pointer has been
;**       drawn, the real pointer location will be checked to see if the
;**       pointer has moved, and if it has, it will be drawn again at the
;**       correct spot (unless it has become excluded).
;*/

        JECXZ   MoveCursor40    ; Screen is busy, just update x,y
        .ERRNZ  IS_BUSY
        MOV     [EDI].cxCell,EAX      ; Set x,y where drawing will occur
        MOV     [EDI].cyCell,EBX

;/*
;** The following call to ExcludeTest need not be made from here with
;** interrupts disabled since screen_busy has been set, and no code will
;** modify the screen while screen_busy is set (except the normal drawing
;** routines which cannot be called at interrupt time, who just happen to set
;** up the exclusion rectangle, which should never be able to be called while
;** screen_busy is set, ...)
;*/

        MOV     CL,PTR_OFF+PTR_EXCLUDED+PTR_REDRAW      ; Set flag mask for
                                                        ; exclude test
        INVOKE  ExcludeTest     ; Go see if excluded
        JNC     MoveCursor20    ; Hidden or already excluded
        JZ      MoveCursor30    ; Can draw the pointer

;/*
;** The hit test was positive.  The pointer must be excluded.  Since this is a
;** pointer drawing operation which will take some time, do it with interrupts
;** enabled.
;*/

        OR      [EDI].fbPtrFlags,PTR_EXCLUDED    ; Show pointer excluded
        INVOKE  pointer_off,0,0                  ; Remove pointer

MoveCursor20:
        MOV     [EDI].screen_busy,NOT_BUSY       ; Show screen no longer busy
        JMP     MoveCursor40                     ; Exit

;/*
;** The hit test was negative and the pointer was on and not excluded.  Draw
;** the pointer at the passed location.  Since this is a pointer drawing
;** operation which will take some time, do it with interrupts enabled.
;*/

MoveCursor30:
        INVOKE  draw_pointer,[EDI].cxCell,[EDI].cyCell      ; Hooray! We get to draw it.

;/*
;** The pointer has been drawn.  Check to see if the pointer has moved again
;** while being drawn.  If it has, then draw the pointer at the new location.
;** Otherwise make the screen available.
;*/

        CLI                                 ; This is a critical section!
        MOV     CL,NOT_BUSY                 ; Show screen available to MoveCursor10
        MOV     EAX,[EDI].cxReal            ; Get the real X location
        MOV     EBX,[EDI].cyReal            ; Get the real y location
        CMP     EAX,[EDI].cxCell            ; If real X is different then pointer's
        JNE     MoveCursor10                ; X, then go draw the pointer
        CMP     EBX,[EDI].cyCell            ; If real Y is different then pointer's
        JNE     MoveCursor10                ; Y, then go draw the pointer
        MOV     [EDI].screen_busy,CL        ; Show screen available

public MoveCursor40
MoveCursor40::
        STI                                 ; Done with this critical section

        RET
MoveCursor ENDP

        SUBTITLE ExcludeTest
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = ExcludeTest
;*
;* DESCRIPTION   = The pointer is checked for its current status (excluded,
;*                 hidden), and a hit test possibly made against the exclusion
;*                 rectangle.  The fbPtrFlags are tested against the passed
;*                 test mask, and if any of the test mask bits are set, then
;*                 the hit test is skipped.  This allows drawing code to check
;*                 for either exclusion or the user turning off the pointer
;*                 before a hit test is performed.  It also allows the timer
;*                 code to just check for the user turning off the pointer,
;*                 and if it hasn't been turned off, to go ahead and perform
;*                 the hit test.
;*
;*                 Registers Preserved:
;*                       DX,SI,DI,BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,FLAGS
;*
;* INPUT         = EAX = cxCell to hit against
;*                 EBX = cyCell to hit against
;*                 CL = fbPtrFlags test mask
;*                 EDX = return address (for InnerExcludeTest only)
;*
;* OUTPUT        = 'C' clear if any bits in test mask are set in [fbPtrFlags]
;*                 'C' set if no bits in test mask are set in [fbPtrFlags]
;*                 'Z' clear if pointer is excluded
;*                 'Z' set if pointer isn't excluded
;*
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** This proc defines the entry point for this function when it is CALLed, in
;** which case the return address is found at the top of the stack.
;*/

ExcludeTest PROC SYSCALL PRIVATE

        POP     EDX     ; Pop the return address into DX

;/*
;** FALL THRU!!! on purpose
;*/

ExcludeTest ENDP

;/*
;** This proc defines the entry point for this function when it is JMPed, in
;** which case the return address has been pre-loaded into EDX.  This technique
;** is used to reduce the overhead on the stack and the execution time.
;** Should be and is NOGEN
;*/

InnerExcludeTest PROC SYSCALL

        TEST    [EDI].fbPtrFlags,cl              ; Do the hit test? (Clears 'C')
        JNZ     ExcludeTest20                    ; Skip hit test
        XOR     ECX,ECX                          ; Show pointer not excluded (need a 0)
        CMP     [EDI].ExcludeCt,CL               ; Is there an exclusion area?
        JE      ExcludeTest10                    ;  N - Show not excluded
        .ERRNZ  RECT_NOT_PRES
        CMP     EAX,[EDI].ExcludeRect.rcl_xRight ;  Y - Is left of pointer >
                                                 ;      right of exclusion
                                                 ;      area?
        JG      ExcludeTest10                    ;  Y - Not excluded
        ADD     EAX,[EDI].cxPointer              ;  N - Add in width of
                                                 ;      pointer or icon
        CMP     EAX,[EDI].ExcludeRect.rcl_xLeft  ; Is right of pointer < left
                                                 ; of exclusion area?
        JL      ExcludeTest10                    ;  Y - Not excluded
        CMP     EBX,[EDI].ExcludeRect.rcl_yBottom ;  N - Is top of pointer >
                                                 ;      bottom of exclusion
                                                 ;      area?
        JG      ExcludeTest10                    ;  Y - Not excluded
        ADD     EBX,[EDI].cyPointer              ;  N - Add in height of
                                                 ;      pointer/icon
        CMP     EBX,[EDI].ExcludeRect.rcl_yTop   ; Is bottom of pointer < top
                                                 ; of exclusion area?
        JLE     ExcludeTest10                    ;  Y - Not excluded
        INC     ECX                              ;  N - Show pointer is
                                                 ;      excluded

ExcludeTest10:
        OR      ECX,ECX                          ; Clear 'Z' if pointer is excluded
        STC                                      ; Show some bits in mask were set

ExcludeTest20:
        JMP     EDX                              ; Return to caller

;/*
;** Should be and is NOGEN
;*/

InnerExcludeTest ENDP

        SUBTITLE CheckCursor
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = CheckCursor
;*
;* DESCRIPTION   =
;*
;*  This function checks on the pointer.
;*
;*  This is not an entry point.  This function is entered by a JMP from
;*  MoveCursor when it is called for the purpose of updating the cursor.
;*
;*  The pointer is checked to see if it can be far_unexcluded, and if so it is
;*  far_unexcluded.  This is the only routine which can cause the pointer to
;*  become visible once it has become invisible (excluded by a drawing
;*  operation or turned off because of a new shape being set).
;*
;*  It is expected that this routine be called at a rate close to once every
;*  quarter of a second.  This allows for a lazy redraw of the pointer
;*  whenever it has become excluded.
;*
;*  If the screen is busy (due to a current pointer operation), the pointer
;*  is turned off, or it isn't excluded, then there is nothing for this
;*  routine to do.  If the pointer is excluded, then a hit test will be
;*  performed against the current exclusion rectangle (if there is one), and
;*  if the pointer is now visible, it will be drawn.
;*
;*  This routine is intended to be interrupt code.  The state of the
;*  interrupts will be maintained over the entire call, but interrupts will
;*  be enabled whenever the pointer is drawn.
;*
;*  For devices with hardware support for pointers, this routine could be a
;*  NOP.
;*
;*  Registers Preserved:
;*        SI,DI,DS,BP
;*  Registers Destroyed:
;*        AX,BX,CX,DX,ES,FLAGS
;*
;* INPUT         = C Prototype:
;*                   BOOL CheckCursor (SWORD abs_x, SWORD abs_y);                           *
;*
;*                   Note: This function is entered only by a JMP from MoveCursor
;*                         when it determines that the cursor needs to be checked;
;*                         therefore, no call frame is established on entry.
;*                         MoveCursor's call frame is used instead.
;*
;* OUTPUT        =   Note: This function does not return to MoveCursor, but, rather,
;*                         to the function that called MoveCursor.  Therefore,
;*                         MoveCursor's call frame is cleared upon exit.
;*
;* RETURN-NORMAL =  Returns TRUE if successful (either there was nothing to do,
;*                  or the cursor was successfully drawn.)
;* RETURN-ERROR  =  Returns FALSE if the cursor should have been drawn but could
;*                  not be, such as when it is excluded by an output operation in
;*                  progress.
;********************************************************************************/

;/*
;** This PROC statement must be identical to MoveCursor's, with the exception
;** of being PRIVATE rather than PUBLIC.
;*/

OPTION PROLOGUE:NONE
ALIGN 4
CheckCursor PROC SYSCALL PRIVATE USES EBX ESI EDI,
                abs_x:DWORD,
                abs_y:DWORD


        XOR     EAX,EAX                          ; Set screen busy
OPTION PROLOGUE:PROLOGUEDEF
        XCHG    [EDI].screen_busy,AL             ; and get current status
        OR      EAX,EAX
        JZ      CheckCursorExit            ; Screen is busy, skip check, return AX=0
        .ERRNZ  IS_BUSY
        MOV     AL,[EDI].fbPtrFlags        ; See if the pointer is on and excluded
        ADD     AL,AL
        JC      CheckCursor40              ; Pointer is off (NULL); no work to do
        ADD     AL,AL
        JS      CheckCursor05              ; Pointer has changed and needs refreshing
        JNC     CheckCursor40              ; Pointer is on, but not excluded

        .ERRNZ  PTR_OFF-10000000b          ; Must be this bit
        .ERRNZ  PTR_EXCLUDED-01000000b     ; Must be this bit
        .ERRNZ  PTR_REDRAW-00100000b       ; Must be this bit

;/*
;** The pointer is on and is currently excluded.  Perform a hit test against
;** the current (cxReal, cyReal) pointer location, and if the pointer is no
;** longer excluded, show the pointer.  (Can also reach here if doing animated
;** pointers and we need to draw the new pointer, we don't need to work about
;** the old pointer because the new one will be full sized and draw pointer
;** will remove it if they were in different positions.)
;**
;** After drawing the pointer, the exclusion test must be repeated.  If the
;** pointer is excluded, the mouse moved while drawing it.  If this is the
;** case, take it down, then wait for the next timer interrupt to try and put
;** it back up.
;*/

CheckCursor05:
        CLI                                      ; Dont want an old X and a new Y!
        MOV     EAX,[EDI].cxReal                 ; Get the X,Y for the test
        MOV     EBX,[EDI].cyReal

CheckCursor10:
        STI
        MOV     ESI,EAX                          ; Save these over the exclude test
        XOR     ECX,ECX                          ; Don't test any flags
        PUSH    EBX
        INVOKE  ExcludeTest                      ; Is pointer excluded?
        POP     EBX
        JNZ     CheckCursor30                    ;  Y - (might have to remove it now)

;/*
;** The pointer is no longer excluded, so draw the new pointer.  Since this is
;** a pointer drawing operation which will take some time, do it with
;** interrupts enabled.
;*/

        MOV     [EDI].cxCell,ESI           ; Save (x,y) where pointer will be drawn
        MOV     [EDI].cyCell,EBX
        INVOKE  draw_pointer,[EDI].cxCell,[EDI].cyCell

        MOV     [EDI].fbPtrFlags,0         ; Pointer is on and not excluded

;/*
;** The pointer has been drawn.  Check to see if the pointer has moved again
;** while being drawn.  If it has, then draw the pointer at the new location.
;** Otherwise make the screen available.
;*/

        CLI                                      ; This is a critical section!
        MOV     EAX,[EDI].cxReal                 ; Get the real pointer (X,Y) location
        MOV     EBX,[EDI].cyReal
        CMP     EAX,[EDI].cxCell                 ; If real X is different than
        JNE     CheckCursor10                    ; pointer's X, then draw the pointer
        CMP     EBX,[EDI].cyCell                 ; If real Y is different than
        JNE     CheckCursor10                    ; pointer's Y, then draw the pointer
        MOV     [EDI].screen_busy,NOT_BUSY       ; Show available before enabling
        STI                                      ; ints so MoveCursor can update
        JMP     CheckCursor50

;/*
;** The pointer is excluded.  If it became excluded after the pointer was
;** brought up (while interrupts were enabled), it must be taken down again.
;** If it is taken down, then no attempt will be made to draw it again until
;** the next timer interrupt.
;*/

CheckCursor30:
        TEST    [EDI].fbPtrFlags,PTR_EXCLUDED    ; Is pointer excluded?
        JNZ     CheckCursor40                    ;  Y - Don't need to take it down
        INVOKE  pointer_off,0,0                  ; Remove the pointer
        MOV     [EDI].fbPtrFlags,PTR_EXCLUDED    ; Pointer is now excluded
;/*
;** Whatever was needed has been done.  Free the screen and exit.
;*/

CheckCursor40:
        MOV     [EDI].screen_busy,NOT_BUSY

CheckCursor50:

        MOV     EAX,1                   ; Return success

CheckCursorExit:
        ret
CheckCursor ENDP
_PtrCode32 ENDS

        END
