Skip to content

Commit

Permalink
feat(ui): list available displays with select
Browse files Browse the repository at this point in the history
  • Loading branch information
Hazer committed Apr 30, 2024
1 parent 7fb8c76 commit 002a2d4
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 4 deletions.
10 changes: 10 additions & 0 deletions src/confighttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,16 @@ namespace confighttp {
outputTree.put("platform", SUNSHINE_PLATFORM);
outputTree.put("version", PROJECT_VER);

pt::ptree displays;
for (const auto &[id, name, is_primary_display] : platf::display_options()) {
pt::ptree display_value;
display_value.put("id", id);
display_value.put("name", name);
display_value.put("is_primary", is_primary_display);
displays.push_front(std::make_pair(std::to_string(id), display_value));
}
outputTree.push_back(std::make_pair("displays", displays));

auto vars = config::parse_config(file_handler::read_file(config::sunshine.config_file.c_str()));

for (auto &[name, value] : vars) {
Expand Down
9 changes: 9 additions & 0 deletions src/platform/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,12 @@ namespace platf {

int width, height;

struct info {
int id;
std::string name;
bool is_primary_display;
};

protected:
// collect capture timing data (at loglevel debug)
stat_trackers::min_max_avg_tracker<double> sleep_overshoot_tracker;
Expand Down Expand Up @@ -578,6 +584,9 @@ namespace platf {
std::vector<std::string>
display_names(mem_type_e hwdevice_type);

std::vector<display_t::info>
display_options();

/**
* @brief Returns if GPUs/drivers have changed since the last call to this function.
* @return `true` if a change has occurred or if it is unknown whether a change occurred.
Expand Down
32 changes: 32 additions & 0 deletions src/platform/linux/cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1049,4 +1049,36 @@ namespace platf {

return display_names;
}

std::vector<display_t::info>
nvfbc_display_options() {
if (cuda::init() || cuda::nvfbc::init()) {
return {};
}

std::vector<display_t::info> display_options;

auto handle = cuda::nvfbc::handle_t::make();
if (!handle) {
return {};
}

auto status_params = handle->status();
if (!status_params) {
return {};
}

for (auto x = 0; x < status_params->dwOutputNum; ++x) {
auto &output = status_params->outputs[x];
std::string name = output.name;
auto option = display_t::info {
x,
name + ", dwID: " + std::to_string(output.dwId),
false // TODO: Find proper way of doing it, found no way of checking for primary display myself
};
display_options.emplace_back(option);
}

return display_options;
}
} // namespace platf
79 changes: 79 additions & 0 deletions src/platform/linux/kmsgrab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1747,4 +1747,83 @@ namespace platf {
return display_names;
}

std::vector<display_t::info>
kms_display_options() {
int count = 0;

if (!fs::exists("/dev/dri")) {
return {};
}

if (!gbm::create_device) {
return {};
}

std::vector<display_t::info> display_options;

kms::conn_type_count_t conn_type_count;

fs::path card_dir { "/dev/dri"sv };
for (auto &entry : fs::directory_iterator { card_dir }) {
auto file = entry.path().filename();

auto filestring = file.generic_string();
if (std::string_view { filestring }.substr(0, 4) != "card"sv) {
continue;
}

kms::card_t card;
if (card.init(entry.path().c_str())) {
continue;
}

auto crtc_to_monitor = kms::map_crtc_to_monitor(card.monitors(conn_type_count));

auto end = std::end(card);
for (auto plane = std::begin(card); plane != end; ++plane) {
// Skip unused planes
if (!plane->fb_id) {
continue;
}

if (card.is_cursor(plane->plane_id)) {
continue;
}

auto fb = card.fb(plane.get());
if (!fb) {
continue;
}

if (!fb->handles[0]) {
break;
}

// This appears to return the offset of the monitor
auto crtc = card.crtc(plane->crtc_id);
if (!crtc) {
continue;
}

auto monitor = crtc_to_monitor[plane->crtc_id];
std::string option_name;
if (!monitor) {
option_name = "w: " + std::to_string((int) (crtc->x + crtc->width)) +
" h: " + std::to_string((int) (crtc->y + crtc->height));
} else {
option_name = monitor->index;
}

auto option = display_t::info {
count++,
std::to_string(count) + ": " + option_name,
false // TODO: Dunno how to find if it is primary
};
display_options.emplace_back(option);
}
}

return display_options;
}

} // namespace platf
17 changes: 17 additions & 0 deletions src/platform/linux/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,23 @@ namespace platf {
return {};
}

std::vector<display_t::info>
display_options() {
#ifdef SUNSHINE_BUILD_CUDA
if (sources[source::NVFBC]) return nvfbc_display_options();
#endif
#ifdef SUNSHINE_BUILD_WAYLAND
if (sources[source::WAYLAND]) return wl_display_options();
#endif
#ifdef SUNSHINE_BUILD_DRM
if (sources[source::KMS]) return kms_display_options();
#endif
#ifdef SUNSHINE_BUILD_X11
if (sources[source::X11]) return x11_display_options();
#endif
return {};
}

/**
* @brief Returns if GPUs/drivers have changed since the last call to this function.
* @return `true` if a change has occurred or if it is unknown whether a change occurred.
Expand Down
42 changes: 42 additions & 0 deletions src/platform/linux/wlgrab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,4 +454,46 @@ namespace platf {
return display_names;
}

std::vector<display_t::info>
wl_display_options() {
wl::display_t display;
if (display.init()) {
return {};
}

wl::interface_t interface;
interface.listen(display.registry());

display.roundtrip();

if (!interface[wl::interface_t::XDG_OUTPUT]) {
return {};
}

if (!interface[wl::interface_t::WLR_EXPORT_DMABUF]) {
return {};
}

for (auto &monitor : interface.monitors) {
monitor->listen(interface.output_manager);
}

display.roundtrip();

std::vector<display_t::info> display_options;

for (int x = 0; x < interface.monitors.size(); ++x) {
auto monitor = interface.monitors[x].get();

auto option = display_t::info {
x,
monitor->name + ": " + monitor->description,
false // TODO: Find proper way of doing it, found no way of checking for primary display myself
};
display_options.emplace_back(option);
}

return display_options;
}

} // namespace platf
36 changes: 36 additions & 0 deletions src/platform/linux/x11grab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,42 @@ namespace platf {
return names;
}

std::vector<display_t::info>
x11_display_options() {
if (load_x11() || load_xcb()) {
return {};
}

x11::xdisplay_t xdisplay { x11::OpenDisplay(nullptr) };
if (!xdisplay) {
return {};
}

auto xwindow = DefaultRootWindow(xdisplay.get());
screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) };
int output = screenr->noutput;

auto main_display = XRRGetOutputPrimary(xdisplay.get(), xwindow);

std::vector<display_t::info> display_options;

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) {
auto option = display_t::info {
monitor,
out_info->name + " connected: " + std::to_string(out_info->connection == RR_Connected),
main_display == screenr->outputs[x]
};
display_options.emplace_back(option);
++monitor;
}
}

return display_options;
}

void
freeImage(XImage *p) {
XDestroyImage(p);
Expand Down
24 changes: 24 additions & 0 deletions src/platform/macos/display.mm
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,30 @@
return display_names;
}

std::vector<display_t::info>
display_options() {
__block std::vector<display_t::info> display_options;

auto display_array = [AVVideo displayNames];

auto main_display_id = CGMainDisplayID();
display_options.reserve([display_array count]);

[display_array enumerateObjectsUsingBlock:^(NSDictionary *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
NSNumber *display_id = obj[@"id"];
NSString *name = obj[@"displayName"];

auto option = display_t::info {
[display_id intValue],
name.UTF8String,
main_display_id == [display_id unsignedIntValue]
};
display_options.emplace_back(option);
}];

return display_options;
}

/**
* @brief Returns if GPUs/drivers have changed since the last call to this function.
* @return `true` if a change has occurred or if it is unknown whether a change occurred.
Expand Down
48 changes: 48 additions & 0 deletions src/platform/windows/display_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,54 @@ namespace platf {
return display_names;
}

std::vector<display_t::info>
display_options() {
std::vector<display_t::info> display_options;

HRESULT status;
// We sync the thread desktop once before we start the enumeration process
// to ensure test_dxgi_duplication() returns consistent results for all GPUs
// even if the current desktop changes during our enumeration process.
// It is critical that we either fully succeed in enumeration or fully fail,
// otherwise it can lead to the capture code switching monitors unexpectedly.
syncThreadDesktop();

dxgi::factory1_t factory;
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory);
if (FAILED(status)) {
return {};
}

dxgi::adapter_t adapter;
int monitorIndex = 0;
for (int x = 0; factory->EnumAdapters1(x, &adapter) != DXGI_ERROR_NOT_FOUND; ++x) {
DXGI_ADAPTER_DESC1 adapter_desc;
adapter->GetDesc1(&adapter_desc);

dxgi::output_t::pointer output_p {};
for (int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
dxgi::output_t output { output_p };

DXGI_OUTPUT_DESC desc;
output->GetDesc(&desc);

auto device_name = to_utf8(desc.DeviceName);

// Don't include the display in the list if we can't actually capture it
if (desc.AttachedToDesktop && dxgi::test_dxgi_duplication(adapter, output, true)) {
auto option = display_t::info {
monitorIndex++,
std::move(device_name),
false // TODO: Correclty check if this is the primary display for windows, idk how
};
display_options.emplace_back(option);
}
}
}

return display_options;
}

/**
* @brief Returns if GPUs/drivers have changed since the last call to this function.
* @return `true` if a change has occurred or if it is unknown whether a change occurred.
Expand Down
Loading

0 comments on commit 002a2d4

Please sign in to comment.