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(ui): list available displays with select #2490

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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
1 change: 0 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,6 @@ jobs:
mingw-w64-x86_64-curl
mingw-w64-x86_64-graphviz
mingw-w64-x86_64-miniupnpc
mingw-w64-x86_64-nlohmann-json
mingw-w64-x86_64-nodejs
mingw-w64-x86_64-nsis
mingw-w64-x86_64-onevpl
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@
path = third-party/wlr-protocols
url = https://gitlab.freedesktop.org/wlroots/wlr-protocols
branch = master
[submodule "third-party/nlohmann_json"]
path = third-party/nlohmann_json
url = https://github.com/nlohmann/json
2 changes: 2 additions & 0 deletions cmake/compile_definitions/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ set(SUNSHINE_TARGET_FILES
"${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/src/RtspParser.c"
"${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/src/Video.h"
"${CMAKE_SOURCE_DIR}/third-party/tray/tray.h"
"${CMAKE_SOURCE_DIR}/src/display_device/dd.h"
"${CMAKE_SOURCE_DIR}/src/upnp.cpp"
"${CMAKE_SOURCE_DIR}/src/upnp.h"
"${CMAKE_SOURCE_DIR}/src/cbs.cpp"
Expand Down Expand Up @@ -138,4 +139,5 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${Boost_LIBRARIES}
${OPENSSL_LIBRARIES}
${CURL_LIBRARIES}
${JSON_LIBRARIES}
${PLATFORM_LIBRARIES})
1 change: 1 addition & 0 deletions cmake/compile_definitions/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ set(PLATFORM_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.h"
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.m"
"${CMAKE_SOURCE_DIR}/src/platform/macos/display.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/display_options.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/input.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/macos/microphone.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/misc.mm"
Expand Down
1 change: 0 additions & 1 deletion cmake/compile_definitions/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ list(PREPEND PLATFORM_LIBRARIES
avrt
iphlpapi
shlwapi
PkgConfig::NLOHMANN_JSON
${CURL_STATIC_LIBRARIES})

if(SUNSHINE_ENABLE_TRAY)
Expand Down
9 changes: 9 additions & 0 deletions cmake/dependencies/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ pkg_check_modules(CURL REQUIRED libcurl)
pkg_check_modules(MINIUPNP miniupnpc REQUIRED)
include_directories(SYSTEM ${MINIUPNP_INCLUDE_DIRS})

# nlohmann_json
if(SUNSHINE_SYSTEM_NLOHMANN_JSON)
pkg_check_modules(NLOHMANN_JSON nlohmann_json>=3.9.0 REQUIRED IMPORTED_TARGET)
set(JSON_LIBRARIES PkgConfig::NLOHMANN_JSON)
else()
add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/nlohmann_json")
set(JSON_LIBRARIES nlohmann_json::nlohmann_json)
endif()

# ffmpeg pre-compiled binaries
if(WIN32)
if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
Expand Down
5 changes: 1 addition & 4 deletions cmake/dependencies/windows.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
# windows specific dependencies

set(Boost_USE_STATIC_LIBS ON) # cmake-lint: disable=C0103
find_package(Boost 1.71.0 COMPONENTS locale log filesystem program_options REQUIRED)

# nlohmann_json
pkg_check_modules(NLOHMANN_JSON nlohmann_json REQUIRED IMPORTED_TARGET)
find_package(Boost 1.71.0 COMPONENTS locale log filesystem program_options REQUIRED)
1 change: 1 addition & 0 deletions cmake/prep/options.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ option(SUNSHINE_CONFIGURE_ONLY "Configure special files only, then exit." OFF)
option(SUNSHINE_ENABLE_TRAY "Enable system tray icon. This option will be ignored on macOS." ON)
option(SUNSHINE_REQUIRE_TRAY "Require system tray icon. Fail the build if tray requirements are not met." ON)

option(SUNSHINE_SYSTEM_NLOHMANN_JSON "Use system installation of nlohmann_json rather than the submodule." OFF)
option(SUNSHINE_SYSTEM_WAYLAND_PROTOCOLS "Use system installation of wayland-protocols rather than the submodule." OFF)

option(CUDA_INHERIT_COMPILE_OPTIONS
Expand Down
1 change: 0 additions & 1 deletion docs/source/building/windows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ Install dependencies:
mingw-w64-x86_64-curl \
mingw-w64-x86_64-graphviz \
mingw-w64-x86_64-miniupnpc \
mingw-w64-x86_64-nlohmann-json \
mingw-w64-x86_64-nodejs \
mingw-w64-x86_64-onevpl \
mingw-w64-x86_64-openssl \
Expand Down
7 changes: 7 additions & 0 deletions docs/source/source_code/source_code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ Source

src/*

.. toctree::
:caption: src/display_device
:maxdepth: 1
:glob:

src/display_device/*

.. toctree::
:caption: src/platform
:maxdepth: 1
Expand Down
3 changes: 3 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <vector>

#include "nvenc/nvenc_config.h"
#include "display_device/dd.h"

namespace config {
struct video_t {
Expand Down Expand Up @@ -82,6 +83,8 @@ namespace config {
bool install_steam_drivers;
};



constexpr int ENCRYPTION_MODE_NEVER = 0; // Never use video encryption, even if the client supports it
constexpr int ENCRYPTION_MODE_OPPORTUNISTIC = 1; // Use video encryption if available, but stream without it if not supported
constexpr int ENCRYPTION_MODE_MANDATORY = 2; // Always use video encryption and refuse clients that can't encrypt
Expand Down
23 changes: 13 additions & 10 deletions src/confighttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include <Simple-Web-Server/server_https.hpp>
#include <boost/asio/ssl/context_base.hpp>

#include <nlohmann/json.hpp>

#include "config.h"
#include "confighttp.h"
#include "crypto.h"
Expand Down Expand Up @@ -531,23 +533,24 @@

print_req(request);

pt::ptree outputTree;
nlohmann::json outputJson;

Check warning on line 536 in src/confighttp.cpp

View check run for this annotation

Codecov / codecov/patch

src/confighttp.cpp#L536

Added line #L536 was not covered by tests
auto g = util::fail_guard([&]() {
std::ostringstream data;

pt::write_json(data, outputTree);
response->write(data.str());
response->write(outputJson.dump());
});

outputTree.put("status", "true");
outputTree.put("platform", SUNSHINE_PLATFORM);
outputTree.put("version", PROJECT_VER);
outputJson.emplace("status", "true");
outputJson.emplace("platform", SUNSHINE_PLATFORM);
outputJson.emplace("version", PROJECT_VER);

auto vars = config::parse_config(file_handler::read_file(config::sunshine.config_file.c_str()));
nlohmann::json displays = platf::display_options();
outputJson.emplace("displays", displays);

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

Check warning on line 549 in src/confighttp.cpp

View check run for this annotation

Codecov / codecov/patch

src/confighttp.cpp#L549

Added line #L549 was not covered by tests
for (auto &[name, value] : vars) {
outputTree.put(std::move(name), std::move(value));
config_file.emplace(std::move(name), std::move(value));
}
outputJson.emplace(std::make_pair("config_file", config_file));
}

void
Expand Down
108 changes: 108 additions & 0 deletions src/display_device/dd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* @file src/display_device/dd.h
* @brief todo this file may not exist in the future,
* todo and we only import <libdisplaydevice/display_device.h> instead, avoiding duplication
*/

#pragma once

#include <bitset>
#include <chrono>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

// lib includes
#include <nlohmann/json.hpp>

namespace dd {

enum class device_state_e {
inactive,
active,
primary /**< Primary state is also implicitly active. */
};

/**
* @brief The device's HDR state in the operating system.
*/
enum class hdr_state_e {
unknown, /**< HDR state could not be retrieved from the OS (even if the display supports it). */
disabled,
enabled
};

/**
* @brief Display's origin position.
* @note Display origin may vary given the compositor running.
*/
struct origin_t {
int x;
int y;

[[nodiscard]] bool is_primary() const {
return this->x == 0 && this->y == 0;
}
// For JSON serialization
NLOHMANN_DEFINE_TYPE_INTRUSIVE(origin_t, x, y)
};

struct resolution_t {
unsigned int width;
unsigned int height;
double scale_factor;

// For JSON serialization
NLOHMANN_DEFINE_TYPE_INTRUSIVE(resolution_t, width, height, scale_factor)
};

/**
* @brief Display's refresh rate.
* @note Floating point is stored in a "numerator/denominator" form.
*/
struct refresh_rate_t {
unsigned int numerator;
unsigned int denominator;

// For JSON serialization
NLOHMANN_DEFINE_TYPE_INTRUSIVE(refresh_rate_t, numerator, denominator)
};

/**
* @brief Display's mode (resolution + refresh rate).
* @see resolution_t
* @see refresh_rate_t
*/
struct mode_t {
resolution_t resolution;
refresh_rate_t refresh_rate;

// For JSON serialization
NLOHMANN_DEFINE_TYPE_INTRUSIVE(mode_t, resolution, refresh_rate)
};


namespace options {
struct current_settings_t {
origin_t origin;
mode_t mode;

[[nodiscard]] bool is_primary() const {
return origin.is_primary();
}

// For JSON serialization
NLOHMANN_DEFINE_TYPE_INTRUSIVE(current_settings_t, origin, mode)
};

struct info_t {
std::string id;
std::string friendly_name;
current_settings_t current_settings;

// For JSON serialization
NLOHMANN_DEFINE_TYPE_INTRUSIVE(info_t, id, friendly_name, current_settings)
};
}
}
3 changes: 3 additions & 0 deletions src/platform/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,9 @@ namespace platf {
std::vector<std::string>
display_names(mem_type_e hwdevice_type);

std::vector<dd::options::info_t>
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<config::display_options_t>
nvfbc_display_options() {
if (cuda::init() || cuda::nvfbc::init()) {
return {};
}

std::vector<config::display_options_t> 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 = config::display_options_t {
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
72 changes: 72 additions & 0 deletions src/platform/linux/kmsgrab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1749,4 +1749,76 @@ namespace platf {
return display_names;
}

std::vector<config::display_options_t>
kms_display_options() {
int count = 0;

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

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

std::vector<config::display_options_t> 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];

auto option = config::display_options_t {
count++,
std::to_string(count) + ", type: " + std::to_string(monitor.index), // TODO: Get real display name
false // TODO: Dunno how to find if it is primary
};
display_options.emplace_back(option);
}
}

return display_options;
}

} // namespace platf
Loading
Loading