diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 401f97af97d..21439bb4caa 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -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 diff --git a/.gitmodules b/.gitmodules index 4efc01425bd..e296d4169ac 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/cmake/compile_definitions/common.cmake b/cmake/compile_definitions/common.cmake index 6e038295847..6a6db564195 100644 --- a/cmake/compile_definitions/common.cmake +++ b/cmake/compile_definitions/common.cmake @@ -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" @@ -138,4 +139,5 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${CURL_LIBRARIES} + ${JSON_LIBRARIES} ${PLATFORM_LIBRARIES}) diff --git a/cmake/compile_definitions/macos.cmake b/cmake/compile_definitions/macos.cmake index 9d445869b1a..58171e6a8af 100644 --- a/cmake/compile_definitions/macos.cmake +++ b/cmake/compile_definitions/macos.cmake @@ -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" diff --git a/cmake/compile_definitions/windows.cmake b/cmake/compile_definitions/windows.cmake index 26e89a20b82..1fd4465985c 100644 --- a/cmake/compile_definitions/windows.cmake +++ b/cmake/compile_definitions/windows.cmake @@ -74,7 +74,6 @@ list(PREPEND PLATFORM_LIBRARIES avrt iphlpapi shlwapi - PkgConfig::NLOHMANN_JSON ${CURL_STATIC_LIBRARIES}) if(SUNSHINE_ENABLE_TRAY) diff --git a/cmake/dependencies/common.cmake b/cmake/dependencies/common.cmake index 29bed10e5cd..499123bc4c5 100644 --- a/cmake/dependencies/common.cmake +++ b/cmake/dependencies/common.cmake @@ -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") diff --git a/cmake/dependencies/windows.cmake b/cmake/dependencies/windows.cmake index a7ecce3963d..01b39180008 100644 --- a/cmake/dependencies/windows.cmake +++ b/cmake/dependencies/windows.cmake @@ -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) \ No newline at end of file diff --git a/cmake/prep/options.cmake b/cmake/prep/options.cmake index 1555036eeb0..c45a6e2ea86 100644 --- a/cmake/prep/options.cmake +++ b/cmake/prep/options.cmake @@ -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 diff --git a/docs/source/building/windows.rst b/docs/source/building/windows.rst index 90b3d9e3c33..cd2038cc492 100644 --- a/docs/source/building/windows.rst +++ b/docs/source/building/windows.rst @@ -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 \ diff --git a/docs/source/source_code/source_code.rst b/docs/source/source_code/source_code.rst index fecc6801c92..4503321626e 100644 --- a/docs/source/source_code/source_code.rst +++ b/docs/source/source_code/source_code.rst @@ -62,6 +62,13 @@ Source src/* +.. toctree:: + :caption: src/display_device + :maxdepth: 1 + :glob: + + src/display_device/* + .. toctree:: :caption: src/platform :maxdepth: 1 diff --git a/src/config.h b/src/config.h index 55098c0c5a1..0947ebeee03 100644 --- a/src/config.h +++ b/src/config.h @@ -12,6 +12,7 @@ #include #include "nvenc/nvenc_config.h" +#include "display_device/dd.h" namespace config { struct video_t { @@ -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 diff --git a/src/confighttp.cpp b/src/confighttp.cpp index de25bf0e7cf..17ffe8aa96d 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include "config.h" #include "confighttp.h" #include "crypto.h" @@ -531,23 +533,24 @@ namespace confighttp { print_req(request); - pt::ptree outputTree; + nlohmann::json outputJson; 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; 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 diff --git a/src/display_device/dd.h b/src/display_device/dd.h new file mode 100644 index 00000000000..bf59ebcca4a --- /dev/null +++ b/src/display_device/dd.h @@ -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 instead, avoiding duplication + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +// lib includes +#include + +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) + }; + } +} \ No newline at end of file diff --git a/src/platform/common.h b/src/platform/common.h index 007f7ece61b..cd8f3438d7a 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -578,6 +578,9 @@ namespace platf { std::vector display_names(mem_type_e hwdevice_type); + std::vector + 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. diff --git a/src/platform/linux/cuda.cpp b/src/platform/linux/cuda.cpp index 129a23e6bd5..9359dc87c92 100644 --- a/src/platform/linux/cuda.cpp +++ b/src/platform/linux/cuda.cpp @@ -1049,4 +1049,36 @@ namespace platf { return display_names; } + + std::vector + nvfbc_display_options() { + if (cuda::init() || cuda::nvfbc::init()) { + return {}; + } + + std::vector 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 diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index bad467d95bf..9576fec61b6 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -1749,4 +1749,76 @@ namespace platf { return display_names; } + std::vector + kms_display_options() { + int count = 0; + + if (!fs::exists("/dev/dri")) { + return {}; + } + + if (!gbm::create_device) { + return {}; + } + + std::vector 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 diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index 980c0804858..97efe06598f 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -777,6 +777,8 @@ namespace platf { #ifdef SUNSHINE_BUILD_CUDA std::vector nvfbc_display_names(); + std::vector + nvfbc_display_options(); std::shared_ptr nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); @@ -789,6 +791,8 @@ namespace platf { #ifdef SUNSHINE_BUILD_WAYLAND std::vector wl_display_names(); + std::vector + wl_display_options(); std::shared_ptr wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); @@ -801,6 +805,8 @@ namespace platf { #ifdef SUNSHINE_BUILD_DRM std::vector kms_display_names(mem_type_e hwdevice_type); + std::vector + kms_display_options(); std::shared_ptr kms_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); @@ -813,6 +819,8 @@ namespace platf { #ifdef SUNSHINE_BUILD_X11 std::vector x11_display_names(); + std::vector + x11_display_options(); std::shared_ptr x11_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); @@ -840,6 +848,23 @@ namespace platf { return {}; } + std::vector + 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. diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index a6ac4adbb96..1633fae8f8f 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -454,4 +454,46 @@ namespace platf { return display_names; } + std::vector + 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_options; + + for (int x = 0; x < interface.monitors.size(); ++x) { + auto monitor = interface.monitors[x].get(); + + auto option = config::display_options_t { + 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 diff --git a/src/platform/linux/x11grab.cpp b/src/platform/linux/x11grab.cpp index 3f6cd0c51b4..960cf8ea692 100644 --- a/src/platform/linux/x11grab.cpp +++ b/src/platform/linux/x11grab.cpp @@ -64,6 +64,7 @@ namespace platf { namespace rr { _FN(GetScreenResources, XRRScreenResources *, (Display * dpy, Window window)); + _FN(GetOutputPrimary, RROutput, (Display * dpy, Window window)); _FN(GetOutputInfo, XRROutputInfo *, (Display * dpy, XRRScreenResources *resources, RROutput output)); _FN(GetCrtcInfo, XRRCrtcInfo *, (Display * dpy, XRRScreenResources *resources, RRCrtc crtc)); _FN(FreeScreenResources, void, (XRRScreenResources * resources)); @@ -86,6 +87,7 @@ namespace platf { std::vector> funcs { { (dyn::apiproc *) &GetScreenResources, "XRRGetScreenResources" }, + { (dyn::apiproc *) &GetOutputPrimary, "XRRGetOutputPrimary" }, { (dyn::apiproc *) &GetOutputInfo, "XRRGetOutputInfo" }, { (dyn::apiproc *) &GetCrtcInfo, "XRRGetCrtcInfo" }, { (dyn::apiproc *) &FreeScreenResources, "XRRFreeScreenResources" }, @@ -838,6 +840,44 @@ namespace platf { return names; } + std::vector + 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 = x11::rr::GetOutputPrimary(xdisplay.get(), xwindow); + + std::vector 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) { + std::string name = out_info->name; + auto is_connected_value = (out_info->connection == RR_Connected) ? "true" : "false"; + auto option = config::display_options_t { + monitor, + name + " connected: " + is_connected_value, + main_display == screenr->outputs[x] + }; + display_options.emplace_back(option); + ++monitor; + } + } + + return display_options; + } + void freeImage(XImage *p) { XDestroyImage(p); diff --git a/src/platform/macos/display_options.mm b/src/platform/macos/display_options.mm new file mode 100644 index 00000000000..082a0690708 --- /dev/null +++ b/src/platform/macos/display_options.mm @@ -0,0 +1,83 @@ +/** + * @file src/platform/macos/display_options.mm + * @brief todo + */ +#include "src/platform/common.h" +#include "src/platform/macos/av_video.h" + +#include "src/config.h" +#include "src/logging.h" + +#include +#include + +namespace fs = std::filesystem; + +namespace platf { + using namespace std::literals; + + dd::options::info_t + display_mode(NSArray* screens, CGDirectDisplayID displayID) { + auto id = [NSNumber numberWithUnsignedInt:displayID]; + for (NSScreen *screen in screens) { + if (screen.deviceDescription[@"NSScreenNumber"] == id) { + NSRect frame = screen.frame; + auto origin = dd::origin_t { + int(frame.origin.x), + int(frame.origin.y) + }; + auto resolution = dd::resolution_t { + uint32_t(frame.size.width), + uint32_t(frame.size.height), + screen.backingScaleFactor + }; + + //auto minimumFramesPerSecond = uint32_t(screen.minimumFramesPerSecond); + //double displayUpdateGranularity = screen.displayUpdateGranularity; + auto refresh_rate = dd::refresh_rate_t { + uint32_t(screen.maximumFramesPerSecond), + 1 + }; + auto current_mode = dd::mode_t { + resolution, + refresh_rate + }; + auto settings = dd::options::current_settings_t { + origin, + current_mode + }; + auto info = dd::options::info_t { + [id stringValue].UTF8String, + screen.localizedName.UTF8String, + settings + }; + return info; + } + } + + return {}; + } + + std::vector + display_options() { + CGDirectDisplayID active_displays[kMaxDisplays]; + uint32_t count; + if (CGGetActiveDisplayList(kMaxDisplays, active_displays, &count) != kCGErrorSuccess) { + return {}; + } + + std::vector display_options; + + display_options.reserve(count); + + auto screens = [NSScreen screens]; + + for (uint32_t i = 0; i < count; i++) { + display_options.emplace_back( + platf::display_mode(screens, active_displays[i]) + ); + } + + return display_options; + } +} // namespace platf diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index 227efe7628f..84a6e7e6014 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -1138,6 +1138,53 @@ namespace platf { return display_names; } + std::vector + display_options() { + // 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; + auto status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory); + if (FAILED(status)) { + return {}; + } + + std::vector display_options; + + 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 = config::display_options_t { + 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. diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 146c18b5303..bff7c2b3f3b 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -57,6 +57,7 @@

{{ $t('config.configuration') }}

:platform="platform" :resolutions="resolutions" :fps="fps" + :displays="displays" > @@ -131,6 +132,7 @@

{{ $t('config.configuration') }}

data() { return { platform: "", + displays: [], saved: false, restarted: false, config: null, @@ -288,8 +290,9 @@

{{ $t('config.configuration') }}

fetch("/api/config") .then((r) => r.json()) .then((r) => { - this.config = r; - this.platform = this.config.platform; + this.config = r.config_file; + this.platform = r.platform; + this.displays = r.displays; var app = document.getElementById("app"); if (this.platform === "windows") { @@ -308,12 +311,7 @@

{{ $t('config.configuration') }}

}); } - // remove values we don't want in the config file - delete this.config.platform; - delete this.config.status; - delete this.config.version; - - // TODO: let each tab's Component handle it's own data instead of doing it here + // TODO: let each tab's Component handle it's own data instead of doing it here // Populate default values from tabs options this.tabs.forEach(tab => { diff --git a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue index 851e1e03a7e..5ea59bb5193 100644 --- a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue +++ b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue @@ -1,5 +1,5 @@