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: Add script to disconnect dualshock/sense with HOME + TRIANGLE #1476

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c84633b
Add script to monitor and disconnect Bluetooth gamepads and correspon…
thekk1 Jul 27, 2024
bb1be24
Removed type=oneshot from service file, because this service should r…
thekk1 Jul 27, 2024
d87580f
Using /tmp instead of /var/tmp to avoid the need of root
thekk1 Jul 27, 2024
b8f4745
Changed script name in the usage description.
thekk1 Jul 27, 2024
0a02518
Fixed some problem with PID files
thekk1 Jul 28, 2024
c347ed3
Revert "fix: Remove pip installed packages and use newly made rpm pac…
KyleGospo Jul 28, 2024
2bec731
Merge branch 'testing' into fix/issue_#1289_DS4/5_disconnect
thekk1 Jul 28, 2024
11e09e7
Merge branch 'testing' into fix/issue_#1289_DS4/5_disconnect
KyleGospo Jul 28, 2024
5633fed
chore: Match upstream
KyleGospo Jul 28, 2024
b8f47bf
Minor performance improvement and fixed behavior bug.
thekk1 Jul 28, 2024
e639f84
Merge branch 'ublue-os:main' into fix/issue_#1289_DS4/5_disconnect
thekk1 Jul 28, 2024
c829b76
Fix for systems with multiple BT controllers.
thekk1 Jul 29, 2024
79c9880
Merge branch 'ublue-os:main' into fix/issue_#1289_DS4/5_disconnect
thekk1 Jul 29, 2024
c64447e
Enable system service globally.
thekk1 Jul 29, 2024
7c0ab97
Use parsing multiple commands to bluetoothctl instead of call every s…
thekk1 Jul 29, 2024
5b0e61b
refactor: handle dualshock detection with udev rules
Zeglius Jul 30, 2024
a955399
refactor: rewrite bazzite-bluetooth-ds4-ds5-workaround to python
Zeglius Jul 30, 2024
8391e83
chore: remove leftover service activation
Zeglius Jul 30, 2024
6db8b73
fix: fix broken udev rule
Zeglius Aug 1, 2024
f9e38db
fix: incompatible udev rule
Zeglius Aug 4, 2024
e51f397
fix: tweak exitcode
Zeglius Aug 5, 2024
2b8c496
chore: Add CPUQuota to ds4/5 workaround
Zeglius Aug 5, 2024
818a882
chore: add brief doc to ds4-ds5 workaround
Zeglius Aug 5, 2024
9366de2
chore: add ujust enable-ds4-workaround
Zeglius Aug 5, 2024
f546b38
chore: rename ujust enable-ds45-workaround to toggle-ds45-workaround
Zeglius Aug 7, 2024
6fb040f
Merge remote-tracking branch 'upstream/testing' into fix/issue_#1289
Zeglius Aug 8, 2024
cc852e0
chore: Move from /usr/etc to /etc
Zeglius Aug 8, 2024
7eeedab
chore: simplify bazzite-bluetooth-ds4-ds5-workaround with evdev
Zeglius Aug 8, 2024
ba8cf19
chore: rename bazzite-bluetooth-ds4-ds5-workaround udev rule
Zeglius Aug 8, 2024
fc61f79
chore: replace script readme at bazzite-bluetooth-ds4-ds5-workaround
Zeglius Aug 8, 2024
e81bcf0
fix(just): minor tweaks to toggle-ds45-workaround
Zeglius Aug 8, 2024
b61ddad
fix(just): Update PLACEHOLDER path in toggle-ds45-workaround
Zeglius Aug 8, 2024
8b94595
chore: update readme at [email protected]
Zeglius Aug 8, 2024
50e84a9
chore: rename 80-bazzite-ps-controller-sleep.rules to 80-bazzite-blue…
Zeglius Aug 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# DO NOT TOUCH ME
# This file is here in order to disable /usr/lib/udev/rules.d/80-bazzite-ps-controller-sleep.rules
# Read /usr/lib/systemd/system/[email protected]
# Delete me if you want to enable workaround
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# By Bazzite
# This workaround allows for DualShock/Dualsense controllers of disconnecting
# when pressing HOME + TRIANGLE
#
# Files composing workaround:
#
# - /usr/lib/udev/rules.d/80-bazzite-bluetooth-ds4-ds5-workaround.rules
# Activates the service with controller MAC as parameter.
#
# - /usr/lib/systemd/system/[email protected]
# Allows to start binary that handles keycode reading.
#
# - /usr/libexec/bazzite-bluetooth-ds4-ds5-workaround
# Reads keycodes and disconnects controllers when HOME + TRIANGLE is read.
#
# - /etc/udev/rules.d/80-bazzite-bluetooth-ds4-ds5-workaround.rules
# Empty file that acts as a deactivator of the workaround

[Unit]
Description=Disconnect DS4 and DS5 controller on shortcut HOME + triangle
After=bluetooth.target
Requires=bluetooth.target
ConditionFileIsExecutable=/usr/libexec/bazzite-bluetooth-ds4-ds5-workaround

[Service]
User=root
Type=simple
CPUQuota=25%
ExecStart=/usr/libexec/bazzite-bluetooth-ds4-ds5-workaround %I
Restart=on-failure
RestartSec=5
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ACTION=="add", SUBSYSTEMS=="input", ATTRS{name}=="*Wireless Controller", RUN+="/usr/bin/systemctl start --no-block --runtime bazzite-bluetooth-ds4-ds5-workaround@$attr{uniq}.service"
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#!/usr/bin/python

"""
# Script Name: bazzite-bluetooth-ds4-ds5-workaround
# Related issues:
# https://github.com/ublue-os/bazzite/issues/1289
# https://github.com/ValveSoftware/steam-for-linux/issues/8678
#
# Original version written by https://github.com/thekk1, see https://github.com/ublue-os/bazzite/pull/1416
#
# Description:
# This script monitors connected PS4 and PS5 gamepad devices and disconnects
# the Bluetooth connection if specific button combinations are detected (Home and Triangle buttons).
#
# This script should be removed if there is a native solution under one of the mentioned issues.
#
# Usage:
# ./bazzite-bluetooth-ds4-ds5-workaround <device_mac_address>

"""
# See https://gist.github.com/noohgnas/7c896791d437e51122c59fe9576a1bcf for reference
import subprocess
import sys
from typing import cast

import evdev
from evdev import InputDevice, InputEvent


class KeyStatus:
def __init__(self) -> None:
self.home: bool = False
self.triangle: bool = False
self.other: set[int] = set()


GAMEPAD_KEYS = {
"HOME": [
316, # Dualshock
],
"TRIANGLE": [
307, # Dualshock
],
}

EV_KEY = 1


def trigger_callback(device_mac: str):
"""Action to realize when HOME + TRIANGLE is held at the same time"""
# Get bluetooth adapters
p = subprocess.Popen(
["/usr/bin/bluetoothctl", "list"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
p.wait()
adapt_strlines = p.stdout
if adapt_strlines is None:
print("ERROR: No adapters detected")
return

for adap in adapt_strlines:
print(f"DEBUG: {adap}")
adap = adap.strip().split(" ", maxsplit=2)[1]
p2 = subprocess.Popen(
["/usr/bin/bash", "-c", f"""echo -e "select {adap}\ndisconnect {device_mac}" | bluetoothctl """],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True,
)
p2.wait()


def get_device(mac: str) -> InputDevice | None:
"""Obtain the /dev/input/eventX path for a bluetooth
Returns:
str: path address
"""
res: str | None = None
mac = mac.lower()
devices = [evdev.InputDevice(d) for d in evdev.list_devices()]
for device in [d for d in devices if d.uniq]:
if mac == str(device.uniq).lower():
res = device
break
return res


def main(argv: list[str]):
if len(argv[1:]) != 1:
print("ERROR: Missing argument", file=sys.stderr)
exit(1)

device_mac = argv[1]
if not device_mac or device_mac == "":
print("ERROR: Missing device bluetooth mac address", file=sys.stderr)
exit(0)

global keys_status
keys_status = KeyStatus()

gamepad = get_device(device_mac)
if gamepad is None:
print("ERROR: Incorrect device bluetooth mac address", file=sys.stderr)
exit(69) # systemd-analyze exit-status UNAVAILABLE

for event in gamepad.read_loop():
event = cast(InputEvent, event)

code = event.code
value = event.value
if event.type == 1:
if code in GAMEPAD_KEYS["HOME"]:
keys_status.home = value == 1
elif code in GAMEPAD_KEYS["TRIANGLE"]:
keys_status.triangle = value == 1
else:
(
keys_status.other.add(code)
if value == 1
else keys_status.other.remove(code)
)

# print(
# f"home: {keys_status.home}, triangle: {keys_status.triangle}, other: {keys_status.other}",
# )
if (
value == 1
and keys_status.home
and keys_status.triangle
and len(keys_status.other) <= 0
):
#### Here we manage what happens when the HOME TRIANGLE is pressed
print("HOME + TRIANGLE detected")
trigger_callback(device_mac)


if __name__ == "__main__":
try:
main(sys.argv)
except KeyboardInterrupt as _:
pass
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,100 @@ bazzite-cli:
benchmark:
echo 'Running a 1 minute benchmark ...'
cd /tmp && stress-ng --matrix 0 -t 1m --times

# Enable/disable HOME + TRIANGLE controller shutdown option for DualShock/DualSense
toggle-ds45-workaround ACTION="":
#!/usr/bin/bash

set -eo pipefail
source /usr/lib/ujust/ujust.sh

OPTION={{ ACTION }}
PLACEHOLDER=/etc/udev/rules.d/80-bazzite-bluetooth-ds4-ds5-workaround.rules
WORKAROUND_NAME="bazzite-bluetooth-ds4-ds5-workaround"

function is_enabled() {
if [[ ! -e $PLACEHOLDER ]]; then
return 0
fi
return 1
}

function enable_workaround() {
# Check if the placeholder rule exists and if does, delete it
if is_enabled ; then
echo "$WORKAROUND_NAME is already enabled"
return
fi
sudo rm -v "$PLACEHOLDER"
echo "$WORKAROUND_NAME was enabled"
}

function disable_workaround() {
# Set placeholder to disable workaround
if ! is_enabled; then
echo "$WORKAROUND_NAME is already disabled"
return
fi
sudo cp -v /usr/$PLACEHOLDER $PLACEHOLDER || { : | sudo tee $PLACEHOLDER; }
echo "$WORKAROUND_NAME was disabled"
}

function show_help() {
echo "Usage: ujust enable-ds45-workaround <option>"
echo " <option>: Specify the quick option to skip the prompt"
echo " Use 'enable' to select Enable"
echo " Use 'disable' to select Disable"
}

## UI
case "${OPTION,,}" in
"enable")
OPTION="enable"
;;

"disable")
OPTION="disable"
;;

"help")
show_help
;;

"")
# We do nothing here, continue
;;

*)
echo "ERROR: Incorrect action"
show_help
exit 1
;;
esac
# If no OPTION was passed as arg, select with terminal ui
if [[ -z $OPTION ]]; then
printf "$State: "
if is_enabled; then
printf "${green}Enabled${n}\n"
else
printf "${red}Disabled${n}\n"
fi
OPTION=$(Choose Enable Disable)
OPTION="${OPTION,,}"
fi
case "${OPTION,,}" in
"enable")
enable_workaround
exit 0
;;

"disable")
disable_workaround
exit 0
;;

"" | *)
echo "ERROR: Invalid option"
exit 1
;;
esac