PAGE 58,132
;******************************************************************************
TITLE SAGE - SAGE VxD
;******************************************************************************
;
;   Title:      SAGE.ASM - SAGE VxD
;
;   Version:    0.060
;
;   Date:       04/18/95
;
;   Author:     Bob Eichenlaub    
;
;------------------------------------------------------------------------------
;
;   Change log:
;
;      DATE     REV                 DESCRIPTION
;   ----------- --- -----------------------------------------------------------
;     04/18/95      Initial version - be
;     07/06/95      serial IRQ detection - be; credit to rjc for the basic approach
;     05/23/97      [darrenmi] major clean-up and support multiple clients
;                   for IE4
;
;==============================================================================

        .386p

;******************************************************************************
;                             I N C L U D E S
;******************************************************************************

        .XLIST
        INCLUDE VMM.Inc
        INCLUDE VWIN32.Inc
        INCLUDE VPICD.Inc
 WIN41SERVICES equ 1		; need _SHELL_Update_User_Activity_Ex service
        INCLUDE SHELL.Inc
        INCLUDE VXDLDR.Inc
        INCLUDE regdef.Inc
        INCLUDE Debug.Inc
        INCLUDE Sage.Inc
        .LIST

;public SAGE_Update_User_Activity

;******************************************************************************
;              V I R T U A L   D E V I C E   D E C L A R A T I O N
;------------------------------------------------------------------------------
; The VxD declaration statement defines the VxD name, version number,
; control proc entry point, VxD ID, initialization order, and VM API 
; entry points.
;
; - Defined VxD ID: See VXDID.TXT for more information
; - Init order: If serial port detection is enabled then this Vxd MUST loaded
;               before VCOMM.VxD and after VPICD.VxD,
;               See VMM.INC for the complete
;               definition of the init order of the standard devices.
;               
;******************************************************************************

Declare_Virtual_Device sage, 1, 0, SAGE_Control, VSageID, UNDEFINED_INIT_ORDER

;******************************************************************************
;                                D A T A
;******************************************************************************

;
; Locked data
;
VxD_LOCKED_DATA_SEG

Window_List             dd  0, 0, 0, 0, 0, 0, 0, 0
cClients                dd  0       ; number of valid windows in window list
Time_Out_Idle           dd  10000   ; the interval between message posts
Time_Out_Handle         dd  0       ; Handle to the global time out we create
PtrSHELL_SUUAE_INFO	dd  0	    ; pointer to the SHELL_SUUAE_INFO structure
PrevActiveDisplay	dd  0       ; last screen or user input activity
PrevActiveSystem        dd  0       ; last system activity
Hooked_Proc             dd  0       ; shell's user_activity entry that we hooked

; Fake user activity info structure in case on Win95.
FakeSUUAE_INFO	_SHELL_SUUAE_INFO <0,0,0,0,0>

VxD_LOCKED_DATA_ENDS

;******************************************************************************
;                               C O D E
;------------------------------------------------------------------------------
; The 'body' of the VxD is in the standard code segment.
;******************************************************************************

VxD_CODE_SEG

BeginProc SAGE_Start_Idle_Timer

        push    esi

        ; check to see if we've already got a timer
        mov     esi, [Time_Out_Handle]
        test    esi, esi
        jnz     start1

        ; get a timer
        mov     eax, [Time_Out_Idle]
        mov     esi, OFFSET32 SAGE_User_Idle_Check
        VMMCall Set_Global_Time_Out
        mov     [Time_Out_Handle], esi

start1:
        pop     esi
        ret

EndProc SAGE_Start_Idle_Timer

BeginProc SAGE_Stop_Idle_Timer

        push    esi

        ; check to see if we have a timer
        mov     esi, [Time_Out_Handle]
        test    esi, esi
        jz      stop1

        ; kill it
        VMMCall Cancel_Time_Out
        xor     esi, esi
        mov     [Time_Out_Handle], esi

stop1:
        pop     esi
        ret

EndProc SAGE_Stop_Idle_Timer


;******************************************************************************
;
;   SAGE_Device_IO
;
;   DESCRIPTION:
;       This is the routine that is called when the CreateFile or
;       DeviceIoControl is made
;
;   ENTRY:
;       ESI = Pointer to args (see VWIN32.INC for struct definition)
;
;   EXIT:
;       EAX = return value
;
;   USES:
;       flags
;
;==============================================================================

BeginProc SAGE_Device_IO

        mov     ecx, [esi.dwIOControlCode]
        test    ecx, ecx
        jnz     next1

        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        ;
        ; DIOC_GETVERSION
        ;

        ; obtain a pointer to the shell vxd's activity timer info
        cmp	[PtrSHELL_SUUAE_INFO], 0
        jne	short getver1
        VxDCall	SHELL_Get_Version
        cmp	eax, 040Ah
        jae	short haveSUUAEX

	; Running on Win95 so we don't have SHELL_Update_User_Activty_Ex service,
	; hook the shell vxd's activity service to watch activity.
        mov     esi, offset32 SAGE_Update_User_Activity
        GetVxDServiceOrdinal eax, SHELL_Update_User_Activity
        VMMCall Hook_Device_Service
	mov	eax, offset32 FakeSUUAE_INFO
	jmp	short setSUUAE_INFO

haveSUUAEX:
	VxDCall _SHELL_Update_User_Activity_Ex, <SUUAE_CONTINUOUS OR SUUAE_CONTINUOUS_CHECK>
setSUUAE_INFO:
	mov     [PtrSHELL_SUUAE_INFO], eax	; (eax) = ptr to SHELL_SUAAE_INFO structure
        
getver1:
        xor     eax, eax                    ; success
        ret

next1:
        cmp     ecx,-1
        jne     next2

        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        ;
        ; DIOC_CLOSE
        ;

        ; see if we have clients to close
        cmp     [cClients], 0
        jz      close1

        ; we do... see if it's the last one...
        dec     [cClients]
        cmp     [cClients], 0
        jnz     close1

        ; last client going away - clean up
        call    SAGE_Stop_Idle_Timer

        cmp     [PtrSHELL_SUUAE_INFO], offset32 FakeSUUAE_INFO
        jne     short close1

        ; unhook activity service
        GetVxDServiceOrdinal    eax,SHELL_Update_User_Activity
        mov     esi, offset32 SAGE_Update_User_Activity
        VMMCall Unhook_Device_Service
        xor	eax, eax
	mov     [PtrSHELL_SUUAE_INFO], eax	; clear pointer

close1:
        xor     eax,eax     ; success
        ret

next2:
        cmp     ecx,1
        jne     next3

        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        ;
        ; Set handle and timeout
        ;
        mov     ebx, [esi.lpvInBuffer]

        ; Try to find window handle
        mov     eax, [ebx]
        mov     ecx, [cClients]
        test    ecx, ecx
        jz      addtolist

ioloop:
        cmp     eax, [Window_List + 4 * ecx - 4]
        je      timeout

        dec     ecx
        jnz     ioloop

        ; Can't find it - add it to window list
        mov     ecx, [cClients]
        cmp     ecx, 8
        jnl     timeout

addtolist:
        inc     [cClients]
        mov     [Window_List + 4 * ecx], eax

timeout:
        ; update timeout if specified
        mov     eax, [ebx+8]
        test    eax, eax
        jz      config1

        mov     [Time_Out_Idle], eax

config1:
        call    SAGE_Start_Idle_Timer

        xor     eax, eax    ; success
        ret
next3:
        cmp     ecx, 2
        jne     next4

        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        ;
        ; Query last activity
        ;
        mov     ebx, [esi.lpvInBuffer]
        mov	edx, [PtrSHELL_SUUAE_INFO]
	.errnz ssiHoldSystem-ssiHoldDisplay-1
	cmp     word ptr [edx].ssiHoldDisplay, 0
	jne     short lact1		; system or display in "hold" state
	mov	eax, [edx].ssiTimeLastActiveDisplay
	cmp	eax, [edx].ssiTimeLastActiveSystem
	jae	short lact2
	mov	eax, [edx].ssiTimeLastActiveSystem
	jmp	short lact2

lact1:	VMMCall	Get_Last_Updated_System_Time
lact2:  mov     [ebx], eax
        xor     eax, eax
        ret

next4:

        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        ;
        ; some unsupported value...
        ;
        mov     eax, ERROR_NOT_SUPPORTED
        ret

EndProc SAGE_Device_IO


VxD_CODE_ENDS



;******************************************************************************
;                      P A G E   L O C K E D   C O D E
;------------------------------------------------------------------------------
;       Memory is a scarce resource. Use this only where necessary.
;******************************************************************************
VxD_LOCKED_CODE_SEG

;******************************************************************************
;
;   SAGE_Control
;
;   DESCRIPTION:
;
;       This is a call-back routine to handle the messages that are sent
;       to VxD's to control system operation. Every VxD needs this function
;       regardless if messages are processed or not. The control proc must
;       be in the LOCKED code segment.
;
;       The Control_Dispatch macro used in this procedure simplifies
;       the handling of messages. To handle a particular message, add
;       a Control_Dispatch statement with the message name, followed
;       by the procedure that should handle the message. 
;
;   ENTRY:
;       EAX = Message number
;       EBX = VM Handle
;
;==============================================================================

BeginProc SAGE_Control

        Control_Dispatch W32_DEVICEIOCONTROL, SAGE_Device_IO
        clc
        ret

EndProc SAGE_Control


BeginDoc
;******************************************************************************
;
; SAGE_Update_User_Activity
;
; DESCRIPTION:
;
;   This service is called by VMD, VKD to tell us that user input occured
; ENTRY:    None
; EXIT:     None
; USES:     NONE
;==============================================================================
EndDoc
BeginProc SAGE_Update_User_Activity, HOOK_PROC, Hooked_Proc

    push    eax

    ; save off the time
    VMMCall Get_Last_Updated_System_Time
    mov     [FakeSUUAE_INFO.ssiTimeLastActiveDisplay], eax

    pop     eax
    jmp     Hooked_Proc

EndProc SAGE_Update_User_Activity


VxD_LOCKED_CODE_ENDS

VxD_PAGEABLE_CODE_SEG

;******************************************************************************
;
; SAGE_User_Idle_Check
;
; DESCRIPTION:
;
;   This checks if key/mouse or serial port (comm) event has occurred.
;
; Entry:
;   None
; Exit:
;   None
; Uses:
;   ALL
;******************************************************************************

BeginProc SAGE_User_Idle_Check,High_Freq,PUBLIC                                 
                                                                                
    ;                                                                           
    ; clear handle                                                              
    ;                                                                           
    xor     ecx, ecx                                                            
    mov     [Time_Out_Handle], ecx                                              
                                                                                
    ;                                                                           
    ; check for idleness                                                        
    ;                                                                           
    mov     eax, [PtrSHELL_SUUAE_INFO]	; (eax) = ptr to SHELL_SUAAE_INFO structure
    .errnz ssiHoldSystem-ssiHoldDisplay-1
    cmp     word ptr [eax].ssiHoldDisplay, 0
    jne     short holdActive		; system or display in "hold" state
    mov	    ecx, [eax].ssiTimeLastActiveDisplay
    mov	    edx, [eax].ssiTimeLastActiveSystem
    cmp	    ecx, [PrevActiveDisplay]
    jne	    notIdle			; display activity changed
    cmp     edx, [PrevActiveSystem]
    je      ResetTimer			; no display or system activity since last time

    ;                                                                           
    ; Not idle so post a message to all clients who want to know
    ;                     
notIdle:
    mov     [PrevActiveDisplay], ecx	; update idle times for next time
    mov     [PrevActiveSystem], edx
holdActive:
    
    xor     eax, eax                                                            
    mov     ecx, [cClients]
    test    ecx, ecx                                                            
    jz      ResetTimer                                                          

loop0:

    ; get next window
    mov     ebx, [4 * ecx + Window_List - 4]

    ; skip if it's -1
    cmp     ebx, -1
    je      loop1
                                                                                
    ; post message
    push    ecx
    VxDCall _SHELL_PostMessage, <ebx, WM_SAGE_MSG, eax, eax, eax, eax>          
    pop     ecx

loop1:
    dec     ecx
    jnz     loop0

    ;                                                                           
    ; reset the timer so we check again later                                   
    ;                                                                           
ResetTimer:                                                                     
    call    SAGE_Start_Idle_Timer
    ret                                                                         
                                                                                
EndProc SAGE_User_Idle_Check                                                    

VxD_PAGEABLE_CODE_ENDS


;******************************************************************************
;                       R E A L   M O D E   C O D E
;******************************************************************************

;******************************************************************************
;
;       Real mode initialization code
;
;   DESCRIPTION:
;       This code is called when the system is still in real mode, and
;       the VxDs are being loaded.
;
;       This routine as coded shows how a VxD (with a defined VxD ID)
;       could check to see if it was being loaded twice, and abort the 
;       second without an error message. Note that this would require
;       that the VxD have an ID other than Undefined_Device_ID. See
;       the file VXDID.TXT more details.
;
;   ENTRY:
;       AX = VMM Version
;       BX = Flags
;               Bit 0: duplicate device ID already loaded 
;               Bit 1: duplicate ID was from the INT 2F device list
;               Bit 2: this device is from the INT 2F device list
;       EDX = Reference data from INT 2F response, or 0
;       SI = Environment segment, passed from MS-DOS
;
;   EXIT:
;       BX = ptr to list of pages to exclude (0, if none)
;       SI = ptr to list of instance data items (0, if none)
;       EDX = DWORD of reference data to be passed to protect mode init
;
;==============================================================================

VxD_REAL_INIT_SEG

BeginProc SAGE_Real_Init_Proc

        test    bx, Duplicate_Device_ID ; check for already loaded
        jnz     short duplicate         ; jump if so

        xor     bx, bx                  ; no exclusion table
        xor     si, si                  ; no instance data table
        xor     edx, edx                ; no reference data

        mov     ax, Device_Load_Ok
        ret

duplicate:
        mov     ax, Abort_Device_Load + No_Fail_Message
        ret

EndProc SAGE_Real_Init_Proc


VxD_REAL_INIT_ENDS


        END