ifndef                  _DINPUT_ASM
_DINPUT_ASM             equ     1

; ------------------------------------------------------
; Constants
DINPUT_OK               equ     0
DINPUT_ERR_DINPUT       equ     1
DINPUT_ERR_KEYBOARD     equ     2
DINPUT_ERR_MOUSE        equ     4

; ------------------------------------------------------
; Variables
                        .data

_IDirectInputDevice     LPDIRECTINPUTA 0
_KeyboardDevice         LPDIRECTINPUTDEVICE8 0
_MouseDevice            LPDIRECTINPUTDEVICE8 0
_IID_IDirectInput8      GUID    IID_IDirectInput8
_GUID_SysKeyboard       GUID    GUID_SysKeyboard
_GUID_SysMouse          GUID    GUID_SysMouse
g_Keys                  dd      0
g_Keys_No_Repeat        dd      0
_Kb_Ack                 dd      0
_Ms_Ack                 dd      0
g_Mouse                 DIMOUSESTATE <>
Mouse_X                 dd      0
Mouse_Y                 dd      0
Mouse_Z                 dd      0
Mouse_Left_Button       dd      0
Mouse_Middle_Button     dd      0
Mouse_Right_Button      dd      0

                        .code

; ------------------------------------------------------
; Name: Get_DirectInput
; Desc: Open directinput & obtain the keyboard & the mouse
; Out: DINPUT_OK (notification)
;      DINPUT_ERR_DINPUT (critical error)
;      DINPUT_ERR_KEYBOARD (notification)
;      DINPUT_ERR_MOUSE (notification)
Get_DirectInput         proc    hWnd:HWND
                        local   Error_Value:dword

                        mov     Error_Value, DINPUT_OK

                        invoke  DirectInput8Create, g_window.init.application.hInstance, DIRECTINPUT_VERSION, addr _IID_IDirectInput8, addr _IDirectInputDevice, NULL
                        .if     eax != DI_OK
                                mov     eax, DINPUT_ERR_DINPUT
                                ret                     
                        .endif
                        
                        COINVOKE _IDirectInputDevice, IDirectInputA.CreateDevice, addr _GUID_SysKeyboard, addr _KeyboardDevice, NULL
                        .if     eax != DI_OK
                                or      Error_Value, DINPUT_ERR_KEYBOARD
                        .else
                                COINVOKE _KeyboardDevice, IDirectInputDevice8.SetDataFormat, addr c_dfDIKeyboard
                                .if     eax != DI_OK
                                        or      Error_Value, DINPUT_ERR_KEYBOARD
                                .else
                                        COINVOKE _KeyboardDevice, IDirectInputDevice8.SetCooperativeLevel, hWnd, DISCL_FOREGROUND or DISCL_NONEXCLUSIVE
                                        .if     eax != DI_OK
                                                or      Error_Value, DINPUT_ERR_KEYBOARD
                                        .else
                                                mov     g_Keys, ALLOCMEM(256)
                                                mov     g_Keys_No_Repeat, ALLOCMEM(256)
                                        .endif
                                .endif
                        .endif


                        COINVOKE _IDirectInputDevice, IDirectInputA.CreateDevice, addr _GUID_SysMouse, addr _MouseDevice, NULL
                        .if     eax != DI_OK
                                or      Error_Value, DINPUT_ERR_MOUSE
                        .else
                                COINVOKE _MouseDevice, IDirectInputDevice8.SetDataFormat, addr c_dfDIMouse
                                .if     eax != DI_OK
                                        or      Error_Value, DINPUT_ERR_MOUSE
                                .else
                                        COINVOKE _MouseDevice, IDirectInputDevice8.SetCooperativeLevel, hWnd, DISCL_FOREGROUND or DISCL_NONEXCLUSIVE
                                        .if     eax != DI_OK
                                                or      Error_Value, DINPUT_ERR_MOUSE
                                        .endif
                                .endif
                        .endif
                        mov     eax, Error_Value
                        ret
Get_DirectInput         endp

; ------------------------------------------------------
; Name: Release_DirectInput
; Desc: Free the allocated resources
Release_DirectInput     proc
                        SAFE_RELEASE _MouseDevice
                        SAFE_RELEASE _KeyboardDevice
                        FREEMEM g_Keys_No_Repeat
                        FREEMEM g_Keys
                        SAFE_RELEASE _IDirectInputDevice
                        ret
Release_DirectInput     endp

; ------------------------------------------------------
; Name: Clear_Keys_Buffer
; Desc: Flush the keyboard buffer
Clear_Keys_Buffer       proc    uses edi
                        mov     edi, g_Keys
                        xor     eax, eax
                        mov     ecx, 256
                        push    ecx
                        shr     ecx, 2
                        rep     stosd
                        pop     ecx
                        and     ecx, 3
                        rep     stosb
                        ret
Clear_Keys_Buffer       endp

; ------------------------------------------------------
; Name: Clear_Mouse_Buffer
; Desc: Flush the mouse buffer
Clear_Mouse_Buffer      proc    uses edi
                        lea     edi, [g_Mouse]
                        xor     eax, eax
                        mov     ecx, sizeof DIMOUSESTATE
                        push    ecx
                        shr     ecx, 2
                        rep     stosd
                        pop     ecx
                        and     ecx, 3
                        rep     stosb
                        ret
Clear_Mouse_Buffer      endp

; ------------------------------------------------------
; Name: Keyboard_Read
; Desc: Read the datas from keyboard
Keyboard_Read           proc
                        .if     _KeyboardDevice != NULL
                                COINVOKE _KeyboardDevice, IDirectInputDevice8.Poll
                                COINVOKE _KeyboardDevice, IDirectInputDevice8.GetDeviceState, 256, g_Keys
                                .if     eax != DI_OK
                                        COINVOKE _KeyboardDevice, IDirectInputDevice8.Acquire
                                        .if     eax != DI_OK
                                                call    Clear_Keys_Buffer
                                                xor     eax, eax
                                                ret
                                        .endif
                                        COINVOKE _KeyboardDevice, IDirectInputDevice8.Poll
                                        COINVOKE _KeyboardDevice, IDirectInputDevice8.GetDeviceState, 256, g_Keys
                                        .if     eax != DI_OK
                                                call    Clear_Keys_Buffer
                                                xor     eax, eax
                                                ret
                                        .endif
                                        mov     eax, TRUE
                                        ret
                                .endif
                        .endif
                        xor     eax, eax
                        ret
Keyboard_Read           endp

; ------------------------------------------------------
; Name: Mouse_Read
; Desc: Read the datas from mouse
Mouse_Read              proc    uses ebx
                        .if     _MouseDevice != NULL
                                COINVOKE _MouseDevice, IDirectInputDevice8.Poll
                                COINVOKE _MouseDevice, IDirectInputDevice8.GetDeviceState, sizeof DIMOUSESTATE, addr g_Mouse
                                .if     eax != DI_OK
                                        COINVOKE _MouseDevice, IDirectInputDevice8.Acquire
                                        .if     eax != DI_OK
                                                call    Clear_Mouse_Buffer
                                                xor     eax,eax
                                                ret
                                        .endif
                                        COINVOKE _MouseDevice, IDirectInputDevice8.Poll
                                        COINVOKE _MouseDevice, IDirectInputDevice8.GetDeviceState, sizeof DIMOUSESTATE, addr g_Mouse
                                        .if     eax != DI_OK
                                                call    Clear_Mouse_Buffer
                                                xor     eax,eax
                                                ret
                                        .endif
                                .endif
                                mov     eax,g_Mouse.lX
                                mov     Mouse_X,eax
                                mov     eax,g_Mouse.lY
                                mov     Mouse_Y,eax
                                mov     eax,g_Mouse.lZ
                                mov     Mouse_Z,eax
                                movzx   eax,byte ptr g_Mouse.rgbButtons[0]
                                test    eax,eax
                                setne   al
                                mov     Mouse_Left_Button,eax
                                movzx   eax,byte ptr g_Mouse.rgbButtons[1]
                                test    eax,eax
                                setne   al
                                mov     Mouse_Right_Button,eax
                                movzx   eax,byte ptr g_Mouse.rgbButtons[2]
                                test    eax,eax
                                setne   al
                                mov     Mouse_Middle_Button,eax
                                mov     eax,TRUE
                                ret
                        .endif
                        xor     eax,eax
                        ret
Mouse_Read              endp

; ------------------------------------------------------
; Name: Input_LostFocus
; Desc: Release input
Input_LostFocus         proc
                        .if     _KeyboardDevice != NULL
                                .if     _Kb_Ack == TRUE
                                        call    Clear_Keys_Buffer
                                        COINVOKE _KeyboardDevice, IDirectInputDevice8.Unacquire
                                        mov     _Kb_Ack, FALSE
                                .endif
                        .endif
                        .if     _MouseDevice != NULL
                                .if     _Ms_Ack == TRUE
                                        call    Clear_Mouse_Buffer
                                        COINVOKE _MouseDevice, IDirectInputDevice8.Unacquire
                                        mov     _Ms_Ack, FALSE
                                .endif
                        .endif
                        ret
Input_LostFocus         endp

; ------------------------------------------------------
; Name: Input_GotFocus
; Desc: Restore input
Input_GotFocus          proc
                        .if     _KeyboardDevice != NULL
                                .if     _Kb_Ack == FALSE
                                        COINVOKE _KeyboardDevice, IDirectInputDevice8.Acquire
                                        call    Clear_Keys_Buffer
                                        mov     _Kb_Ack, TRUE
                                .endif
                        .endif
                        .if     _MouseDevice != NULL
                                .if     _Ms_Ack == FALSE
                                        call    Clear_Mouse_Buffer
                                        COINVOKE _MouseDevice, IDirectInputDevice8.Acquire
                                        mov     _Ms_Ack, TRUE
                                .endif
                        .endif
                        ret
Input_GotFocus          endp

; ------------------------------------------------------
; Name: DInput_Clamp_Mouse
; Desc: Make the mouse coordinates fit inside a given range
DInput_Clamp_Mouse      proc    Speed:dword, Coord:dword, Min:dword, Max:dword
                        mov     eax, Speed
                        mov     ecx, Coord
                        mov     edx, Min
                        .if     sdword ptr [ecx] <= edx
                                mov     [ecx], edx
                                .if sdword ptr eax < 0
                                        xor     eax, eax
                                .endif
                        .endif
                        mov     edx, Max
                        .if     sdword ptr [ecx] >= edx
                                mov     [ecx], edx
                                .if sdword ptr eax > 0
                                        xor     eax, eax
                                .endif
                        .endif
                        add     [ecx], eax
                        mov     edx, Min
                        .if     sdword ptr [ecx] <= edx
                                mov     [ecx], edx
                        .endif
                        mov     edx, Max
                        .if     sdword ptr [ecx] >= edx
                                mov     [ecx], edx
                        .endif
                        ret
DInput_Clamp_Mouse      endp

; ------------------------------------------------------
; Name: DInput_Get_Key_NoRepeat
; Desc: Return the state of a key only one time
DInput_Get_Key_NoRepeat proc    uses ebx edi Key:dword
                        mov     eax, g_Keys
                        mov     ebx, Key
                        mov     edi, g_Keys_No_Repeat
                        mov     al, [eax + ebx]
                        test    al, al
                        jz      No_Key
                        xor     eax, eax
                        cmp     byte ptr [edi + ebx], 1
                        je      Already_Pressed
                        inc     byte ptr [edi + ebx]
                        mov     eax, TRUE                       ; Only report it when it's first pressed
Already_Pressed:        ret
No_Key:                 and     byte ptr [edi + ebx], 0
                        xor     eax, eax
                        ret
DInput_Get_Key_NoRepeat endp

; ------------------------------------------------------
; Name: DInput_Get_Key
; Desc: Return the state of a key
DInput_Get_Key          proc    uses ebx edi Key:dword
                        mov     eax, g_Keys
                        mov     ebx, Key
                        movzx   eax, byte ptr [eax + ebx]
                        ret
DInput_Get_Key          endp

endif
