Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(GamepadManager): Add support for Dualsense/Dualshock controllers #264

Merged
merged 10 commits into from
Oct 19, 2023
8 changes: 8 additions & 0 deletions assets/state/states/settings_general_controller.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[gd_resource type="Resource" script_class="State" load_steps=2 format=3 uid="uid://bcekyu20uvkxv"]

[ext_resource type="Script" path="res://core/systems/state/state.gd" id="1_ltxd0"]

[resource]
script = ExtResource("1_ltxd0")
name = "General Controller Settings"
data = {}
5 changes: 5 additions & 0 deletions core/global/launch_manager.gd
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ func launch(app: LibraryLaunchItem) -> RunningApp:
# Set the OGUI ID environment variable
env["OGUI_ID"] = app.name

# Set input environment variables
var hidapi_enabled: bool = SettingsManager.get_value("general.controller", "sdl_hidapi_enabled", false)
if not hidapi_enabled:
env["SDL_JOYSTICK_HIDAPI"] = "0"

# Build any environment variables to include in the command
var env_vars := PackedStringArray()
for key in env.keys():
Expand Down
78 changes: 40 additions & 38 deletions core/systems/input/gamepad_manager.gd
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ signal gamepad_added(gamepad: ManagedGamepad)
signal gamepad_removed

var platform := load("res://core/global/platform.tres") as Platform
var device_hider := load("res://core/systems/input/device_hider.tres") as DeviceHider
var device_unhider := load("res://core/systems/input/device_hider.tres") as DeviceHider
var input_thread := load("res://core/systems/threading/input_thread.tres") as SharedThread

var gamepads := GamepadArray.new()
Expand All @@ -38,8 +38,8 @@ func _init() -> void:
logger.info("Not initializing. Ran from editor.")
return

# If we crashed, unhide any device events that were orphaned
await device_hider.restore_all_hidden()
# Unhide any device events that were orphaned by bad programs like HandyGCCS
await device_unhider.restore_all_hidden()

# Discover any gamepads and grab exclusive access to them. Create a
# duplicate virtual gamepad for each physical one.
Expand Down Expand Up @@ -143,17 +143,14 @@ func exit() -> void:
gamepad.phys_device.grab(false)
gamepad.virt_device.close()
gamepad.phys_device.close()
device_hider.restore_event_device(gamepad.phys_path)
if gamepad is HandheldGamepad:
logger.debug("Cleaning up handheld gamepad: " + gamepad.gamepad.phys_path)
gamepad.gamepad.phys_device.grab(false)
gamepad.gamepad.virt_device.close()
gamepad.gamepad.phys_device.close()
device_hider.restore_event_device(gamepad.gamepad.phys_path)

gamepad.kb_device.grab(false)
gamepad.kb_device.close()
device_hider.restore_event_device(gamepad.kb_event_path)


## Sets the gamepad intercept mode
Expand All @@ -167,8 +164,6 @@ func set_intercept(mode: ManagedGamepad.INTERCEPT_MODE) -> void:
## access variables from the main thread
func _process_input(_delta: float) -> void:
# Process the input for all currently managed gamepads
if not is_instance_valid(gamepads):
return
for gamepad in gamepads.items():
gamepad.process_input()

Expand Down Expand Up @@ -236,32 +231,20 @@ func _on_gamepad_change(device: int, connected: bool) -> void:

# Hide the device from other processes
var path := discovered_handheld.get_path()
logger.debug("Trying to re-hide handheld gamepad")
var hidden_path := await device_hider.hide_event_device(path)
if hidden_path == "":
logger.warn("Unable to re-hide handheld gamepad: " + path)

# Setup any handheld gamepads if they are discovered and not yet configured
if not gamepads.has_handheld() and discovered_handheld:
var path := discovered_handheld.get_path()
logger.info("A handheld gamepad was discovered at: " + path)
# Hide the device from other processes
logger.debug("Trying to hide handheld gamepad")
var hidden_path := await device_hider.hide_event_device(path)
if hidden_path == "":
logger.warn("Unable to hide handheld gamepad: " + path)
logger.warn("Opening the raw handheld gamepad instead")
# Try to open the non-hidden device instead
hidden_path = path

# Create a new managed gamepad with physical/virtual gamepad pair
logger.debug("Opening handheld gamepad at: " + hidden_path)
logger.debug("Opening handheld gamepad at: " + path)
var gamepad := HandheldGamepad.new()
if gamepad.open(hidden_path) != OK:
logger.error("Unable to create handheld gamepad for: " + hidden_path)
if hidden_path != path:
logger.debug("Restoring device back to its regular path")
device_hider.restore_event_device(hidden_path)
if gamepad.open(path) != OK:
logger.error("Unable to create handheld gamepad for: " + path)

else:
gamepad.setup(discovered_keyboards)
gamepads.add(gamepad)
Expand All @@ -277,24 +260,14 @@ func _on_gamepad_change(device: int, connected: bool) -> void:
continue

# See if we've identified the gamepad defined by the device platform.
if dev.get_phys() == "":
logger.debug("Device appears to be virtual, skipping " + path)
if is_device_virtual(dev):
logger.debug("Device appears to be virtual , skipping " + path)
continue

# Hide the device from other processes
var hidden_path := await device_hider.hide_event_device(path)
if hidden_path == "":
logger.warn("Unable to hide gamepad: " + path)
logger.warn("Opening the raw gamepad instead")
# Try to open the non-hidden device instead
hidden_path = path

# Create a new managed gamepad with physical/virtual gamepad pair
var gamepad := ManagedGamepad.new()
if gamepad.open(hidden_path) != OK:
logger.error("Unable to create managed gamepad for: " + hidden_path)
if hidden_path != path:
device_hider.restore_event_device(hidden_path)
if gamepad.open(path) != OK:
logger.error("Unable to create managed gamepad for: " + path)
continue

gamepads.add(gamepad)
Expand All @@ -312,6 +285,35 @@ func _get_event_from_phys(phys_path: String) -> String:
return event


## Returns true if the InputDevice is a virtual device.
func is_device_virtual(device: InputDevice) -> bool:
var event := device.get_path()
logger.debug("Checking if " + event + " is a virtual device.")

if device.get_phys() != "":
logger.debug(event + " is real, it has a physical address: " + device.get_phys())
return false

var event_file := event.split("/")[-1]
var sysfs_devices := SysfsDevice.get_all()
logger.debug("Looking for " + event_file)
for sysfs_device in sysfs_devices:
logger.debug("Event Handlers: " + str(sysfs_device.handlers))
if not event_file in sysfs_device.handlers:
continue
logger.debug("Found sysfs device for " + event_file)

if "/devices/virtual" in sysfs_device.sysfs_path:
logger.debug("Device appears to be virtual")
return true

logger.debug("Device is not in /devices/virtual. Treating as real.")
return false

logger.debug("Unable to match device to any sysfs device.")
return true


## Structure for looking up and maintaining Gamepads objects
class GamepadArray:
var handheld: HandheldGamepad
Expand Down
Loading