import pyvjoy from ctypes import CFUNCTYPE, c_int, c_void_p, c_char_p, c_ulong, Structure, Union, POINTER, byref, wintypes _ffb_cb = None _vj = None class FfbPacket(Structure): _fields = [ "size", c_ulong, "cmd", c_ulong, "data", c_char_p ] 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� Issues a stop on every running effect. # CTRL_DEVRST = 4, # Device Reset� Clears any device paused condition, enables all actuators and clears all effects from memory. # CTRL_DEVPAUSE = 5, # Device Pause� The all effects on the device are paused at the current time step. # CTRL_DEVCONT = 6, # Device Continue� 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, # }; # typedef struct _FFB_DATA { # 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�) ("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�) 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: �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 -�10000 �- 10000 # LONG PosCoeff; # Positive Coefficient: Range -�10000 �- 10000 # LONG NegCoeff; # Negative Coefficient: Range -�10000 �- 10000 # DWORD PosSatur; # Positive Saturation: Range 0 � 10000 # DWORD NegSatur; # Negative Saturation: Range 0 � 10000 # LONG DeadBand; # Dead Band: : Range 0 � 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