diff --git a/src/config.h b/src/config.h index 9261e8a129d..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,11 +83,7 @@ namespace config { bool install_steam_drivers; }; - struct display_options_t { - int id; - std::string name; - bool is_primary_display; - }; + 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 diff --git a/src/confighttp.cpp b/src/confighttp.cpp index a721eed840b..17ffe8aa96d 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -533,34 +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); - - 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)); + outputJson.emplace("status", "true"); + outputJson.emplace("platform", SUNSHINE_PLATFORM); + outputJson.emplace("version", PROJECT_VER); + + 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())); - pt::ptree config_file; + nlohmann::json config_file; for (auto &[name, value] : vars) { - config_file.put(std::move(name), std::move(value)); + config_file.emplace(std::move(name), std::move(value)); } - outputTree.push_back(std::make_pair("config_file", config_file)); + outputJson.emplace(std::make_pair("config_file", config_file)); } void diff --git a/src/display_device/dd.h b/src/display_device/dd.h index 3f71e2f3600..bf59ebcca4a 100644 --- a/src/display_device/dd.h +++ b/src/display_device/dd.h @@ -17,6 +17,92 @@ #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 7f8b2ff1b6f..cd8f3438d7a 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -578,7 +578,7 @@ namespace platf { std::vector display_names(mem_type_e hwdevice_type); - std::vector + std::vector display_options(); /** diff --git a/src/platform/macos/display.mm b/src/platform/macos/display.mm index 3f1fbd1b722..3468d46f55e 100644 --- a/src/platform/macos/display.mm +++ b/src/platform/macos/display.mm @@ -188,30 +188,6 @@ return display_names; } - std::vector - display_options() { - __block std::vector 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 = config::display_options_t { - [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. diff --git a/src/platform/macos/display_options.mm b/src/platform/macos/display_options.mm new file mode 100644 index 00000000000..afe25d666cf --- /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), + 0 + }; + 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