Skip to content

Commit

Permalink
Added notification register and unregister.
Browse files Browse the repository at this point in the history
  • Loading branch information
JumpyzZ committed Nov 18, 2021
1 parent b55adcf commit 8ab8301
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 3 deletions.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,43 @@ gamepad.update()
time.sleep(1.0)
```

### Receive state changes

To receive LED ring changes and rumble/vibration requests, you need to define your own callback function, then call `gamepad.register_notification(your_callback)`. If nothing is supplied, it will simply print out all state changes ([int 0-255]LargeMortor, SmallMortor and LedNumber).

Your callback function need to have 6 parameters: `client, target, LargeMotor, SmallMotor, LedNumber, UserData`. For more information, see [sdk/include/ViGEm/Client.h](https://github.com/ViGEm/ViGEmBus/blob/442ae3b85693b48866d0627af6f485f918b08d03/sdk/include/ViGEm/Client.h).

To unregister, call `gampad.unregister_notification()`

Example:

```python
import vgamepad as vg
gamepad = vg.VX360Gamepad()

def example_callback(client, target, LargeMotor, SmallMotor, LedNumber, UserData):
#Do your things here, change LED light, power a motor, or just return these value.
pass
gamepad.register_notification()
#When state changes, callbacks are made, default callback function will print out like this:
```

```bash
LargeMotor: 64, SmallMotor: 30, LedNumber: 0
LargeMotor: 255, SmallMotor: 255, LedNumber: 0
LargeMotor: 67, SmallMotor: 32, LedNumber: 0
LargeMotor: 65, SmallMotor: 32, LedNumber: 0
LargeMotor: 64, SmallMotor: 32, LedNumber: 0
LargeMotor: 63, SmallMotor: 31, LedNumber: 0
LargeMotor: 63, SmallMotor: 81, LedNumber: 0
LargeMotor: 127, SmallMotor: 127, LedNumber: 0
LargeMotor: 16, SmallMotor: 83, LedNumber: 0
LargeMotor: 5, SmallMotor: 28, LedNumber: 0
LargeMotor: 0, SmallMotor: 19, LedNumber: 0
LargeMotor: 0, SmallMotor: 57, LedNumber: 0
LargeMotor: 0, SmallMotor: 51, LedNumber: 0
```

---

### Advanced users:
Expand All @@ -343,3 +380,4 @@ All contributions to this project are welcome. Please submit a PR with your name
- Yann Bouteiller
### Contributors:

- JumpyzZ
55 changes: 54 additions & 1 deletion vgamepad/win/vigem_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import platform
from pathlib import Path
from ctypes import CDLL, POINTER, c_void_p, c_uint, c_ushort, c_ulong, c_bool
from ctypes import CDLL, POINTER, CFUNCTYPE, c_void_p, c_uint, c_ushort, c_ulong, c_bool, c_ubyte
from vgamepad.win.vigem_commons import XUSB_REPORT, DS4_REPORT, DS4_REPORT_EX, VIGEM_TARGET_TYPE

if platform.architecture()[0] == "64bit":
Expand Down Expand Up @@ -227,3 +227,56 @@
vigem_target_x360_get_user_index.restype = c_uint

# TODO: add the missing APIs (those with C callback functions)
"""
Registers a function which gets called, when LED index or vibration state changes
occur on the provided target device. This function fails if the provided
target device isn't fully operational or in an erroneous state.
@param vigem The driver connection object.
@param target The target device object.
@param notification The notification callback.
@param userData The user data passed to the notification callback.
@returns A VIGEM_ERROR.
"""
vigem_target_x360_register_notification = vigemClient.vigem_target_x360_register_notification
vigem_target_x360_register_notification.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p)
vigem_target_x360_register_notification.restype = c_uint

"""
Removes a previously registered callback function from the provided target object.
@param target The target device object.
"""
vigem_target_x360_unregister_notification = vigemClient.vigem_target_x360_unregister_notification
vigem_target_x360_unregister_notification.argtypes = (c_void_p)
vigem_target_x360_unregister_notification.restype = None

"""
Registers a function which gets called, when LightBar or vibration state changes
occur on the provided target device. This function fails if the provided
target device isn't fully operational or in an erroneous state.
@param vigem The driver connection object.
@param target The target device object.
@param notification The notification callback.
@param userData The user data passed to the notification callback.
@returns A VIGEM_ERROR.
"""
vigem_target_ds4_register_notification = vigemClient.vigem_target_ds4_register_notification
vigem_target_ds4_register_notification.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p)
vigem_target_ds4_register_notification.restype = c_uint

"""
Removes a previously registered callback function from the provided target object.
@param target The target device object.
"""
vigem_target_ds4_unregister_notification = vigemClient.vigem_target_ds4_unregister_notification
vigem_target_ds4_unregister_notification.argtypes = (c_void_p)
vigem_target_ds4_unregister_notification.restype = None









#
26 changes: 24 additions & 2 deletions vgamepad/win/virtual_gamepad.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
import vgamepad.win.vigem_commons as vcom
import vgamepad.win.vigem_client as vcli
import ctypes
from ctypes import CFUNCTYPE, c_void_p, c_ubyte
from abc import ABC, abstractmethod

from inspect import signature #Check if user defined callback function is legal

def check_err(err):
if err != vcom.VIGEM_ERRORS.VIGEM_ERROR_NONE:
raise Exception(vcom.VIGEM_ERRORS(err).name)

def defaultCallback(client, target, LargeMotor, SmallMotor, LedNumber, UserData):
print("LargeMotor: {lm}, SmallMotor: {sm}, LedNumber: {ln}".format(lm = LargeMotor, sm = SmallMotor, ln = LedNumber))

class VBus:
"""
Expand All @@ -38,6 +41,7 @@ def __init__(self):
self.vbus = VBUS
self._busp = self.vbus.get_busp()
self._devicep = self.target_alloc()
self.CMPFUNC = CFUNCTYPE(None, c_void_p, c_void_p, c_ubyte, c_ubyte, c_ubyte, c_void_p)
vcli.vigem_target_add(self._busp, self._devicep)
assert vcli.vigem_target_is_attached(self._devicep), "The virtual device could not connect to ViGEmBus."

Expand Down Expand Up @@ -68,7 +72,7 @@ def set_pid(self, pid):
:param: the new product ID of the virtual device
"""
vcli.vigem_target_get_pid(self._devicep, pid)

def get_index(self):
"""
:return: the internally used index of the target device
Expand Down Expand Up @@ -207,6 +211,15 @@ def update(self):
def target_alloc(self):
return vcli.vigem_target_x360_alloc()

def register_notification(self, callback_func = defaultCallback):
if not signature(callback_func) == signature(defaultCallback):
raise TypeError("Needed callback function signature: {need}, but got: {got}".format(need = signature(defaultCallback), got = signature(callback_func)))
self.cmp_func = self.CMPFUNC(callback_func) #keep its reference, otherwise programe will crash when a callback is made.
check_err(vcli.vigem_target_x360_register_notification(self._busp, self._devicep, self.cmp_func, None))

def unregister_notification(self):
vcli.vigem_target_x360_unregister_notification(self._devicep)


class VDS4Gamepad(VGamepad):
"""
Expand Down Expand Up @@ -369,3 +382,12 @@ def update_extended_report(self, extended_report):

def target_alloc(self):
return vcli.vigem_target_ds4_alloc()

def register_callback(self, callback_func = defaultCallback):
if not signature(callback_func) == signature(defaultCallback):
raise TypeError("Needed callback function signature: {need}, but got: {got}".format(need = signature(defaultCallback), got = signature(callback_func)))
self.cmp_func = self.CMPFUNC(callback_func)
check_err(vcli.vigem_target_ds4_register_notification(self._busp, self._devicep, self.cmp_func, None))

def unregister_notification(self):
vcli.vigem_target_ds4_unregister_notification(self._devicep)

0 comments on commit 8ab8301

Please sign in to comment.