Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,8 @@ editing the `conf` file in a text editor. Use the examples as reference.
Info: Detected display: DP-1 (id: 3) connected: false
Info: Detected display: DVI-D-1 (id: 4) connected: false
@endcode
You need to use the id value inside the parenthesis, e.g. `1`.
It is recommended to use the stable display connector name (text right before the parenthesis, e.g. DP-0) for this value.
For wlgrab/x11grab and kmsgrab the numeric id value can also be used.
<br>
<br>
**macOS:**
Expand Down
39 changes: 36 additions & 3 deletions src/platform/linux/kmsgrab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <errno.h>
#include <fcntl.h>
#include <filesystem>
#include <ranges>
#include <thread>
#include <unistd.h>

Expand Down Expand Up @@ -848,6 +849,38 @@ namespace platf {
BOOST_LOG(debug) << ss.str();
}

/**
* @brief Map display name to monitor index
*
* @param display_name Name of the display to determine monitor index for (or monitor index string value)
* @return monitor's display index
*/
static int64_t map_display_name_to_monitor_index(const std::string_view &display_name) {
// Handle (legacy) monitor index strings by converting them to integer
if (display_name.empty() || std::ranges::all_of(display_name, ::isdigit)) {
return util::from_view(display_name);
}
// display_name is a connector name (not empty and containing non-digits) try to resolve correct monitor index like correlate_wayland does
auto index_begin = display_name.find_last_of('-');
std::int64_t index;
if (index_begin == std::string_view::npos) {
index = 1;
} else {
index = std::max<int64_t>(1, util::from_view(display_name.substr(index_begin + 1)));
}
auto type = kms::from_view(display_name.substr(0, index_begin));
for (auto &card_descriptor : kms::card_descriptors) {
for (const auto &monitor_descriptor : card_descriptor.crtc_to_monitor | std::views::values) {
if (monitor_descriptor.type == type && monitor_descriptor.index == index) {
BOOST_LOG(debug) << "Mapped '"sv << display_name << "' to a monitor index " << monitor_descriptor.monitor_index;
return monitor_descriptor.monitor_index;
}
}
}
BOOST_LOG(warning) << "Couldn't map '"sv << display_name << "' to a monitor index. Falling back to first monitor in list (index=0).";
return 0; // Fallback to first index if nothing can be matched
}

/**
* @brief Base KMS display capture backend shared by RAM and VRAM paths.
*/
Expand All @@ -873,7 +906,7 @@ namespace platf {
int init(const std::string &display_name, const ::video::config_t &config) {
delay = ::video::capture_frame_interval(config);

int monitor_index = util::from_view(display_name);
int monitor_index = map_display_name_to_monitor_index(display_name);
int monitor = 0;

fs::path card_dir {"/dev/dri"sv};
Expand Down Expand Up @@ -2099,8 +2132,8 @@ namespace platf {
kms::env_height = std::max(kms::env_height, (int) (crtc->y + crtc->height));

kms::print(plane.get(), fb.get(), crtc.get());

display_names.emplace_back(std::to_string(count++));
display_names.emplace_back(std::format("{}-{}", drmModeGetConnectorTypeName(it->second.type), it->second.index));
count++;
}

cds.emplace_back(kms::card_descriptor_t {
Expand Down
73 changes: 31 additions & 42 deletions src/platform/linux/x11grab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
// standard includes
#include <fstream>
#include <ranges>
#include <thread>

// plaform includes
Expand Down Expand Up @@ -506,47 +507,41 @@ namespace platf {
refresh();

int streamedMonitor = -1;
if (!display_name.empty()) {
if (!display_name.empty() && std::ranges::all_of(display_name, ::isdigit)) {
// Resolve (legacy) monitor index from display_name
streamedMonitor = (int) util::from_view(display_name);
}

if (streamedMonitor != -1) {
BOOST_LOG(info) << "Configuring selected display ("sv << streamedMonitor << ") to stream"sv;
screen_res_t screenr {x11::rr::GetScreenResources(xdisplay.get(), xwindow)};
int output = screenr->noutput;

output_info_t result;
int monitor = 0;
for (int x = 0; x < output; ++x) {
output_info_t out_info {x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x])};
if (out_info) {
if (monitor++ == streamedMonitor) {
result = std::move(out_info);
break;
}
screen_res_t screenr {x11::rr::GetScreenResources(xdisplay.get(), xwindow)};
int output = screenr->noutput;

output_info_t result;
bool result_found = false;
int monitor = 0;
for (int x = 0; x < output; ++x) {
output_info_t out_info {x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x])};
if (out_info) {
// Match monitor by index if a valid one is present, otherwise try to resolve by matching output name to display_name
if ((streamedMonitor >= 0 && monitor == streamedMonitor) || (streamedMonitor < 0 && out_info->name == display_name)) {
result = std::move(out_info);
result_found = true;
break;
}
monitor++;
}
}

if (!result) {
BOOST_LOG(error) << "Could not stream display number ["sv << streamedMonitor << "], there are only ["sv << monitor << "] displays."sv;
return -1;
}

if (result->crtc) {
crtc_info_t crt_info {x11::rr::GetCrtcInfo(xdisplay.get(), screenr.get(), result->crtc)};
BOOST_LOG(info)
<< "Streaming display: "sv << result->name << " with res "sv << crt_info->width << 'x' << crt_info->height << " offset by "sv << crt_info->x << 'x' << crt_info->y;
if (result_found && result->crtc) {
crtc_info_t crt_info {x11::rr::GetCrtcInfo(xdisplay.get(), screenr.get(), result->crtc)};
BOOST_LOG(info)
<< "Streaming display: "sv << result->name << " with res "sv << crt_info->width << 'x' << crt_info->height << " offset by "sv << crt_info->x << 'x' << crt_info->y;

width = crt_info->width;
height = crt_info->height;
offset_x = crt_info->x;
offset_y = crt_info->y;
} else {
BOOST_LOG(warning) << "Couldn't get requested display info, defaulting to recording entire virtual desktop"sv;
width = xattr.width;
height = xattr.height;
}
width = crt_info->width;
height = crt_info->height;
offset_x = crt_info->x;
offset_y = crt_info->y;
} else {
BOOST_LOG(warning) << "Couldn't get info for requested display ["sv << display_name << "], defaulting to recording entire virtual desktop"sv;
width = xattr.width;
height = xattr.height;
}
Expand Down Expand Up @@ -971,22 +966,16 @@ namespace platf {
screen_res_t screenr {x11::rr::GetScreenResources(xdisplay.get(), xwindow)};
int output = screenr->noutput;

std::vector<std::string> names;
int monitor = 0;
for (int x = 0; x < output; ++x) {
output_info_t out_info {x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x])};
if (out_info) {
BOOST_LOG(info) << "Detected display: "sv << out_info->name << " (id: "sv << monitor << ")"sv << out_info->name << " connected: "sv << (out_info->connection == RR_Connected);
++monitor;
BOOST_LOG(info) << "Detected display: "sv << out_info->name << " (id: "sv << monitor++ << ")"sv << out_info->name << " connected: "sv << (out_info->connection == RR_Connected);
names.emplace_back(out_info->name);
}
}

std::vector<std::string> names;
names.reserve(monitor);

for (auto x = 0; x < monitor; ++x) {
names.emplace_back(std::to_string(x));
}

return names;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ const props = defineProps([
])

const config = ref(props.config)
const outputNamePlaceholder = (props.platform === 'windows') ? '{de9bb7e2-186e-505b-9e93-f48793333810}' : '0'
let _outputNamePlaceholder = '0';
if(props.platform === 'windows') {
_outputNamePlaceholder = '{de9bb7e2-186e-505b-9e93-f48793333810}';
} else if(props.platform === 'linux' || props.platform === 'freebsd') {
_outputNamePlaceholder = 'DP-0';
}
const outputNamePlaceholder = _outputNamePlaceholder; // NOSONAR(javascript:S1481,javascript:S1854): Constant used by vue.js binding for placeholder below
</script>

<template>
Expand All @@ -33,21 +39,19 @@ const outputNamePlaceholder = (props.platform === 'windows') ? '{de9bb7e2-186e-5
<template #freebsd>
<pre style="white-space: pre-line;">
Info: Detecting displays
Info: Detected display: DVI-D-0 (id: 0) connected: false
Info: Detected display: HDMI-0 (id: 1) connected: true
Info: Detected display: DP-0 (id: 2) connected: true
Info: Detected display: DP-1 (id: 3) connected: false
Info: Detected display: DVI-D-1 (id: 4) connected: false
Info: Detected display: HDMI-A-1 connected: true
Info: Detected display: DP-1 connected: true
Info: Detected display: DP-2 connected: false
Info: Detected display: DVI-D-3 connected: false
</pre>
</template>
<template #linux>
<pre style="white-space: pre-line;">
Info: Detecting displays
Info: Detected display: DVI-D-0 (id: 0) connected: false
Info: Detected display: HDMI-0 (id: 1) connected: true
Info: Detected display: DP-0 (id: 2) connected: true
Info: Detected display: DP-1 (id: 3) connected: false
Info: Detected display: DVI-D-1 (id: 4) connected: false
Info: Detected display: HDMI-A-1 connected: true
Info: Detected display: DP-1 connected: true
Info: Detected display: DP-2 connected: false
Info: Detected display: DVI-D-3 connected: false
</pre>
</template>
<template #macos>
Expand Down
3 changes: 2 additions & 1 deletion src_assets/common/assets/web/public/assets/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@
"origin_web_ui_allowed_pc": "Only localhost may access Web UI",
"origin_web_ui_allowed_wan": "Anyone may access Web UI",
"output_name": "Display Id",
"output_name_desc_unix": "During Sunshine startup, you should see the list of detected displays. Note: You need to use the id value inside the parenthesis. Below is an example; the actual output can be found in the Troubleshooting tab.",
"output_name_desc_macos": "During Sunshine startup, you should see the list of detected displays. Note: You need to use the id value inside the parenthesis. Below is an example; the actual output can be found in the Troubleshooting tab.",
"output_name_desc_unix": "During Sunshine startup, you should see the list of detected displays. Note: It is recommended to use the display's connector name (e.g. DP-0) but depending on the capture backend used other values may be possible (see documentation for details). Below is an example; the actual output can be found in the Troubleshooting tab.",
"output_name_desc_windows": "Manually specify a display device id to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. During Sunshine startup, you should see the list of detected displays. Below is an example; the actual output can be found in the Troubleshooting tab.",
"packetsize": "Packet Size Limit",
"packetsize_desc": "Limit the packet size to avoid fragmentation on a low MTU link. This helps reduce packet loss and micro-stuttering, while streaming over a layer 2 VPN to clients that cannot configure this value, e.g. Moonlight for Android/iOS. Reduce the bitrate when using low values. Values larger than 1456 require jumbo frames. Range: 0, 200-65535. A value of 0 will disable the limit.",
Expand Down
Loading