pyiforce/pyvjoy_ffb.py

337 lines
11 KiB
Python
Raw Normal View History

2020-10-30 03:39:16 +01:00
import pyvjoy
from ctypes import CFUNCTYPE, c_int, c_void_p, c_char_p, c_ulong, Structure, Union, POINTER, byref, wintypes
2020-10-30 02:00:14 +01:00
2020-10-30 03:39:16 +01:00
_ffb_cb = None
_vj = None
2020-10-30 02:00:14 +01:00
2020-10-30 03:39:16 +01:00
class FfbPacket(Structure):
_fields = [
"size", c_ulong,
"cmd", c_ulong,
"data", c_char_p
]
2020-10-30 02:00:14 +01:00
2020-10-30 03:39:16 +01:00
class FFBEType:
Type = c_int
ET_NONE = 0, # No Force
ET_CONST = 1, # Constant Force
ET_RAMP = 2, # Ramp
ET_SQR = 3, # Square
ET_SINE = 4, # Sine
ET_TRNGL = 5, # Triangle
ET_STUP = 6, # Sawtooth Up
ET_STDN = 7, # Sawtooth Down
ET_SPRNG = 8, # Spring
ET_DMPR = 9, # Damper
ET_INRT = 10, # Inertia
ET_FRCTN = 11, # Friction
ET_CSTM = 12, # Custom Force Data
class FFBPType:
Type = c_int
PT_EFFREP = 0x01 # Usage Set Effect Report
PT_ENVREP = 0x02 # Usage Set Envelope Report
PT_CONDREP = 0x03 # Usage Set Condition Report
PT_PRIDREP = 0x04 # Usage Set Periodic Report
PT_CONSTREP = 0x05 # Usage Set Constant Force Report
PT_RAMPREP = 0x06 # Usage Set Ramp Force Report
PT_CSTMREP = 0x07 # Usage Custom Force Data Report
PT_SMPLREP = 0x08 # Usage Download Force Sample
PT_EFOPREP = 0x0A # Usage Effect Operation Report
PT_BLKFRREP = 0x0B # Usage PID Block Free Report
PT_CTRLREP = 0x0C # Usage PID Device Control
PT_GAINREP = 0x0D # Usage Device Gain Report
PT_SETCREP = 0x0E # Usage Set Custom Force Report
PT_NEWEFREP = 0x01 # Usage Create New Effect Report
PT_BLKLDREP = 0x02 # Usage Block Load Report
PT_POOLREP = 0x03 # Usage PID Pool Report
# enum FFBOP
# {
# EFF_START = 1, # EFFECT START
# EFF_SOLO = 2, # EFFECT SOLO START
# EFF_STOP = 3, # EFFECT STOP
# };
# enum FFB_CTRL
# {
# CTRL_ENACT = 1, # Enable all device actuators.
# CTRL_DISACT = 2, # Disable all the device actuators.
# CTRL_STOPALL = 3, # Stop All Effects<74> Issues a stop on every running effect.
# CTRL_DEVRST = 4, # Device Reset<65> Clears any device paused condition, enables all actuators and clears all effects from memory.
# CTRL_DEVPAUSE = 5, # Device Pause<73> The all effects on the device are paused at the current time step.
# CTRL_DEVCONT = 6, # Device Continue<75> The all effects that running when the device was paused are restarted from their last time step.
# };
# enum FFB_EFFECTS {
# Constant = 0x0001,
# Ramp = 0x0002,
# Square = 0x0004,
# Sine = 0x0008,
# Triangle = 0x0010,
# Sawtooth_Up = 0x0020,
# Sawtooth_Dn = 0x0040,
# Spring = 0x0080,
# Damper = 0x0100,
# Inertia = 0x0200,
# Friction = 0x0400,
# Custom = 0x0800,
# };
2020-10-30 02:00:14 +01:00
# typedef struct _FFB_DATA {
2020-10-30 03:39:16 +01:00
# ULONG size;
# ULONG cmd;
# UCHAR *data;
# } FFB_DATA, * PFFB_DATA;
# typedef struct _FFB_EFF_CONSTANT {
# BYTE EffectBlockIndex;
# LONG Magnitude; # Constant force magnitude: -10000 - 10000
# } FFB_EFF_CONSTANT, *PFFB_EFF_CONSTANT;
# typedef struct _FFB_EFF_RAMP {
# BYTE EffectBlockIndex;
# LONG Start; # The Normalized magnitude at the start of the effect (-10000 - 10000)
# LONG End; # The Normalized magnitude at the end of the effect (-10000 - 10000)
# } FFB_EFF_RAMP, *PFFB_EFF_RAMP;
class FFB_EFF_REPORT_Dir(Union):
_fields_ = [
# Polar direction: (0x00-0xFF correspond to 0-360<36>)
("Direction", wintypes.BYTE),
# X direction: Positive values are To the right of the center (X); Negative are Two's complement
("DirX", wintypes.BYTE)
]
class FFB_EFF_REPORT(Structure):
_fields_ = [
("EffectBlockIndex", wintypes.BYTE),
("EffectType", FFBEType.Type),
("Duration", wintypes.WORD), # Value in milliseconds. 0xFFFF means infinite
("TrigerRpt", wintypes.WORD),
("SamplePrd", wintypes.WORD),
("Gain", wintypes.BYTE),
("TrigerBtn", wintypes.BYTE),
# How to interpret force direction Polar (0-360<36>) or Cartesian (X,Y)
("Polar", wintypes.BOOL),
("Direction", FFB_EFF_REPORT_Dir),
# Y direction: Positive values are below the center (Y); Negative are Two's complement
("DirY", wintypes.BYTE)
]
# typedef struct _FFB_EFF_REPORT {
# BYTE ;
# ;
# WORD ;#
# WORD ;
# WORD ;
# BYTE ;
# BYTE ;
# BOOL ;
# union
# {
# BYTE Direction;
# BYTE DirX;
# };
# BYTE DirY;
# } FFB_EFF_REPORT, *PFFB_EFF_REPORT;
# typedef struct _FFB_EFF_OP {
# BYTE EffectBlockIndex;
# FFBOP EffectOp;
# BYTE LoopCount;
# } FFB_EFF_OP, *PFFB_EFF_OP;
# typedef struct _FFB_EFF_PERIOD {
# BYTE EffectBlockIndex;
# DWORD Magnitude; # Range: 0 - 10000
# LONG Offset; # Range: <20>10000 - 10000
# DWORD Phase; # Range: 0 - 35999
# DWORD Period; # Range: 0 - 32767
# } FFB_EFF_PERIOD, *PFFB_EFF_PERIOD;
# typedef struct _FFB_EFF_COND {
# BYTE EffectBlockIndex;
# BOOL isY;
# LONG CenterPointOffset; # CP Offset: Range -<2D>10000 <20>- 10000
# LONG PosCoeff; # Positive Coefficient: Range -<2D>10000 <20>- 10000
# LONG NegCoeff; # Negative Coefficient: Range -<2D>10000 <20>- 10000
# DWORD PosSatur; # Positive Saturation: Range 0 <20> 10000
# DWORD NegSatur; # Negative Saturation: Range 0 <20> 10000
# LONG DeadBand; # Dead Band: : Range 0 <20> 1000
# } FFB_EFF_COND, *PFFB_EFF_COND;
# typedef struct _FFB_EFF_ENVLP {
# BYTE EffectBlockIndex;
# DWORD AttackLevel; # The Normalized magnitude of the stating point: 0 - 10000
# DWORD FadeLevel; # The Normalized magnitude of the stopping point: 0 - 10000
# DWORD AttackTime; # Time of the attack: 0 - 4294967295
# DWORD FadeTime; # Time of the fading: 0 - 4294967295
# } FFB_EFF_ENVLP, *PFFB_EFF_ENVLP;
class FfbPacketHelper:
def __init__(self, pointer):
self.ptr = pointer
def DeviceID(self):
devid = c_int(0)
res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
return res, devid
def Type(self):
ffbtype = FFBPType.Type(0)
res = _vj.Ffb_h_Type(self.ptr, byref(ffbtype))
return res, ffbtype
# def Packet(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def EBI(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
def Eff_Report(self):
devid = c_int(0)
res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
return res, devid
# def Eff_Const(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def Eff_Ramp(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def EffOp(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def DevCtrl(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def Eff_Period(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def Eff_Cond(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def DevGain(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def Eff_Envlp(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def EffNew(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
# def Eff_Constant(self):
# devid = c_int(0)
# res = _vj.Ffb_h_DeviceID(self.ptr, byref(devid))
# return res, devid
def pyvjoy_ffb_default_cb(ffb_pkt_p, userdata):
p = FfbPacketHelper(ffb_pkt_p)
# print(p.DeviceID())
print(p.Type())
# print(' '.join('%02x' % x for x in ffb_pkt[0]))
def add_ffb(vjoy_dev, ffb_cb=None):
global _ffb_cb, _vj
PYVJOY_CB_FUNC = CFUNCTYPE(None, POINTER(FfbPacket), c_void_p)
if not ffb_cb:
ffb_cb = pyvjoy_ffb_default_cb
_vj = vjoy_dev._vj
_vj.FfbStart(vjoy_dev.rID)
_ffb_cb = PYVJOY_CB_FUNC(ffb_cb)
_vj.FfbRegisterGenCB(_ffb_cb, 0)
_vj.Ffb_h_DeviceID.argtypes = [POINTER(FfbPacket), POINTER(c_int)]
_vj.Ffb_h_Type.argtypes = [POINTER(FfbPacket), POINTER(FFBPType.Type)]
_vj.Ffb_h_Packet.argtypes = [POINTER(FfbPacket), POINTER(
wintypes.WORD), POINTER(c_int), POINTER(wintypes.BYTE)]
_vj.Ffb_h_EBI.argtypes = [POINTER(FfbPacket), POINTER(c_int)]
_vj.Ffb_h_Eff_Report.argtypes = [POINTER(FfbPacket), POINTER(FFB_EFF_REPORT)]
# _vj.Ffb_h_Eff_Ramp.argtypes = [POINTER(FfbPacket), FFB_EFF_RAMP* RampEffect]
# _vj.Ffb_h_EffOp.argtypes = [POINTER(FfbPacket), FFB_EFF_OP* Operation]
# _vj.Ffb_h_DevCtrl.argtypes = [POINTER(FfbPacket), FFB_CTRL * Control]
# _vj.Ffb_h_Eff_Period.argtypes = [POINTER(FfbPacket), FFB_EFF_PERIOD* Effect]
# _vj.Ffb_h_Eff_Cond.argtypes = [POINTER(FfbPacket), FFB_EFF_COND* Condition]
_vj.Ffb_h_DevGain.argtypes = [POINTER(FfbPacket), POINTER(wintypes.BYTE)]
# _vj.Ffb_h_Eff_Envlp.argtypes = [POINTER(FfbPacket), FFB_EFF_ENVLP* Envelope]
# _vj.Ffb_h_EffNew.argtypes = [POINTER(FfbPacket), FFBEType * Effect]
# _vj.Ffb_h_Eff_Constant.argtypes = [POINTER(FfbPacket), FFB_EFF_CONSTANT * ConstantEffect]
_vj.Ffb_h_DeviceID.restype = wintypes.DWORD
_vj.Ffb_h_Type.restype = wintypes.DWORD
_vj.Ffb_h_Packet.restype = wintypes.DWORD
_vj.Ffb_h_EBI.restype = wintypes.DWORD
_vj.Ffb_h_Eff_Report.restype = wintypes.DWORD
_vj.Ffb_h_Eff_Ramp.restype = wintypes.DWORD
_vj.Ffb_h_EffOp.restype = wintypes.DWORD
_vj.Ffb_h_DevCtrl.restype = wintypes.DWORD
_vj.Ffb_h_Eff_Period.restype = wintypes.DWORD
_vj.Ffb_h_Eff_Cond.restype = wintypes.DWORD
_vj.Ffb_h_DevGain.restype = wintypes.DWORD
_vj.Ffb_h_Eff_Envlp.restype = wintypes.DWORD
_vj.Ffb_h_EffNew.restype = wintypes.DWORD
_vj.Ffb_h_Eff_Constant.restype = wintypes.DWORD
# VJOYINTERFACE_API VOID __cdecl FfbRegisterGenCB(FfbGenCB cb, PVOID data);
# Register a FFB callback function that will be called by the driver every time a FFB data packet arrives. For additional
# information see Receptor Unit section.
# VJOYINTERFACE_API BOOL __cdecl FfbStart(UINT rID);
# Enable the FFB mechanism of the specified VDJ.
# Return TRUE on success. Otherwise return FALSE.
# VJOYINTERFACE_API VOID __cdecl FfbStop(UINT rID);
# Disable the FFB mechanism of the specified VDJ.
# [NEW]
# VJOYINTERFACE_API BOOL __cdecl IsDeviceFfb(UINT rID);
# Return TRUE if specified device supports FFB. Otherwise return FALSE.
# [NEW]
# VJOYINTERFACE_API BOOL __cdecl IsDeviceFfbEffect(UINT rID, UINT Effect)
# Return TRUE if specified device supports a specific FFB Effect. Otherwise return FALSE.
# The FFB Effect is indicated by its Usage.
# List of effect Usages:
# HID_USAGE_CONST (0x26): Usage ET Constant Force
# HID_USAGE_RAMP (0x27): Usage ET Ramp
# HID_USAGE_SQUR (0x30): Usage ET Square
# HID_USAGE_SINE (0x31): Usage ET Sine
# HID_USAGE_TRNG (0x32): Usage ET Triangle
# HID_USAGE_STUP (0x33): Usage ET Sawtooth Up
# HID_USAGE_STDN (0x34): Usage ET Sawtooth Down
# HID_USAGE_SPRNG (0x40): Usage ET Spring
# HID_USAGE_DMPR (0x41): Usage ET Damper
# HID_USAGE_INRT (0x42): Usage ET Inertia
# HID_USAGE_FRIC (0x43): Usage ET Friction