mirror of
https://github.com/LizardByte/Sunshine.git
synced 2026-05-06 21:50:57 +08:00
feat(input): implement multiseat support and virtual device naming (#4954)
This commit is contained in:
@@ -159,6 +159,33 @@ If the input is still not working, you may need to add your user to the `input`
|
||||
sudo usermod -aG input $USER
|
||||
```
|
||||
|
||||
#### Multiseat
|
||||
|
||||
If you run multiple concurrent Wayland sessions on separate logind seats (e.g. `seat0`, `seat1`),
|
||||
your compositor may ignore injected input unless Sunshine's virtual devices are assigned to the correct seat.
|
||||
|
||||
Sunshine determines its target seat from `XDG_SEAT`, which is typically set automatically by your display manager.
|
||||
If needed, you can override it manually in your systemd service file or shell environment before starting Sunshine.
|
||||
|
||||
When the seat is not `seat0`, Sunshine appends the seat name to its virtual device names, for example:
|
||||
|
||||
- Keyboard passthrough (seat1)
|
||||
- Sunshine PS5 (virtual) pad (seat1)
|
||||
|
||||
Sunshine creates two mouse devices: a relative one and an absolute one.
|
||||
|
||||
To assign Sunshine's virtual devices to the correct seat, create this udev rules file
|
||||
(/etc/udev/rules.d/72-sunshine-virtual-seat.rules):
|
||||
```udev
|
||||
SUBSYSTEM=="input", KERNEL=="input*", ATTR{name}=="*(seat1)*", TAG+="seat", ENV{ID_SEAT}="seat1"
|
||||
```
|
||||
|
||||
Then reload udev:
|
||||
|
||||
```bash
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger -s input
|
||||
```
|
||||
|
||||
### KMS Streaming fails
|
||||
KMS screencasting requires elevated privileges which are not allowed for Flatpak or AppImage packages.
|
||||
This means that you must install Sunshine using the native package format of your distribution, if available.
|
||||
|
||||
@@ -13,12 +13,28 @@
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/platform/linux/input/inputtino_seat.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf {
|
||||
|
||||
inline std::string inputtino_name_for_seat(std::string_view base_name) {
|
||||
auto seat_id = inputtino_seat::get_target_seat();
|
||||
if (seat_id.empty() || seat_id == "seat0") {
|
||||
return std::string(base_name);
|
||||
}
|
||||
|
||||
std::string name;
|
||||
name.reserve(base_name.size() + seat_id.size() + 3);
|
||||
name.append(base_name);
|
||||
name.append(" (");
|
||||
name.append(seat_id);
|
||||
name.push_back(')');
|
||||
return name;
|
||||
}
|
||||
|
||||
using joypads_t = std::variant<inputtino::XboxOneJoypad, inputtino::SwitchJoypad, inputtino::PS5Joypad>;
|
||||
|
||||
struct joypad_state {
|
||||
@@ -30,13 +46,13 @@ namespace platf {
|
||||
struct input_raw_t {
|
||||
input_raw_t():
|
||||
mouse(inputtino::Mouse::create({
|
||||
.name = "Mouse passthrough",
|
||||
.name = inputtino_name_for_seat("Mouse passthrough"sv),
|
||||
.vendor_id = 0xBEEF,
|
||||
.product_id = 0xDEAD,
|
||||
.version = 0x111,
|
||||
})),
|
||||
keyboard(inputtino::Keyboard::create({
|
||||
.name = "Keyboard passthrough",
|
||||
.name = inputtino_name_for_seat("Keyboard passthrough"sv),
|
||||
.vendor_id = 0xBEEF,
|
||||
.product_id = 0xDEAD,
|
||||
.version = 0x111,
|
||||
@@ -66,13 +82,13 @@ namespace platf {
|
||||
struct client_input_raw_t: public client_input_t {
|
||||
client_input_raw_t(input_t &input):
|
||||
touch(inputtino::TouchScreen::create({
|
||||
.name = "Touch passthrough",
|
||||
.name = inputtino_name_for_seat("Touch passthrough"sv),
|
||||
.vendor_id = 0xBEEF,
|
||||
.product_id = 0xDEAD,
|
||||
.version = 0x111,
|
||||
})),
|
||||
pen(inputtino::PenTablet::create({
|
||||
.name = "Pen passthrough",
|
||||
.name = inputtino_name_for_seat("Pen passthrough"sv),
|
||||
.vendor_id = 0xBEEF,
|
||||
.product_id = 0xDEAD,
|
||||
.version = 0x111,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_gamepad.h"
|
||||
#include "inputtino_seat.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
@@ -27,7 +28,7 @@ namespace platf::gamepad {
|
||||
};
|
||||
|
||||
auto create_xbox_one() {
|
||||
return inputtino::XboxOneJoypad::create({.name = "Sunshine X-Box One (virtual) pad",
|
||||
return inputtino::XboxOneJoypad::create({.name = inputtino_name_for_seat("Sunshine X-Box One (virtual) pad"sv),
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c#L147
|
||||
.vendor_id = 0x045E,
|
||||
.product_id = 0x02EA,
|
||||
@@ -35,7 +36,7 @@ namespace platf::gamepad {
|
||||
}
|
||||
|
||||
auto create_switch() {
|
||||
return inputtino::SwitchJoypad::create({.name = "Sunshine Nintendo (virtual) pad",
|
||||
return inputtino::SwitchJoypad::create({.name = inputtino_name_for_seat("Sunshine Nintendo (virtual) pad"sv),
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L981
|
||||
.vendor_id = 0x057e,
|
||||
.product_id = 0x2009,
|
||||
@@ -50,7 +51,7 @@ namespace platf::gamepad {
|
||||
device_mac = std::format("02:00:00:00:00:{:02x}", globalIndex);
|
||||
}
|
||||
|
||||
return inputtino::PS5Joypad::create({.name = "Sunshine PS5 (virtual) pad", .vendor_id = 0x054C, .product_id = 0x0CE6, .version = 0x8111, .device_phys = device_mac, .device_uniq = device_mac});
|
||||
return inputtino::PS5Joypad::create({.name = inputtino_name_for_seat("Sunshine PS5 (virtual) pad"sv), .vendor_id = 0x054C, .product_id = 0x0CE6, .version = 0x8111, .device_phys = device_mac, .device_uniq = device_mac});
|
||||
}
|
||||
|
||||
int alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
|
||||
|
||||
23
src/platform/linux/input/inputtino_seat.cpp
Normal file
23
src/platform/linux/input/inputtino_seat.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @file src/platform/linux/input/inputtino_seat.cpp
|
||||
* @brief Implementation for multi-seat naming (udev-only).
|
||||
*/
|
||||
// standard includes
|
||||
#include <cstdlib>
|
||||
|
||||
// local includes
|
||||
#include "inputtino_seat.h"
|
||||
|
||||
namespace platf::inputtino_seat {
|
||||
|
||||
std::string get_target_seat() {
|
||||
if (const char *seat = std::getenv("XDG_SEAT")) {
|
||||
if (seat[0] != '\0') {
|
||||
return seat;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace platf::inputtino_seat
|
||||
17
src/platform/linux/input/inputtino_seat.h
Normal file
17
src/platform/linux/input/inputtino_seat.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file src/platform/linux/input/inputtino_seat.h
|
||||
* @brief Helpers for multi-seat naming (udev-only).
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace platf::inputtino_seat {
|
||||
|
||||
/**
|
||||
* Determine the target seat for the current Sunshine instance.
|
||||
* Returns empty string if no seat could be determined.
|
||||
*/
|
||||
std::string get_target_seat();
|
||||
|
||||
} // namespace platf::inputtino_seat
|
||||
@@ -1,11 +1,12 @@
|
||||
# Allows Sunshine to acces /dev/uinput
|
||||
# Allows Sunshine to access /dev/uinput
|
||||
KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
|
||||
# Allows Sunshine to access /dev/uhid
|
||||
KERNEL=="uhid", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
|
||||
# Joypads
|
||||
KERNEL=="hidraw*", ATTRS{name}=="Sunshine PS5 (virtual) pad", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine X-Box One (virtual) pad", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine gamepad (virtual) motion sensors", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine Nintendo (virtual) pad", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
KERNEL=="hidraw*", ATTRS{name}=="Sunshine PS5 (virtual) pad*", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine X-Box One (virtual) pad*", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine gamepad (virtual) motion sensors*", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine Nintendo (virtual) pad*", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine PS5 (virtual) pad*", GROUP="input", MODE="0660", TAG+="uaccess"
|
||||
|
||||
Reference in New Issue
Block a user