/** * @file src/config.h * @brief Declarations for the configuration of Sunshine. */ #pragma once // standard includes #include #include #include #include #include #include #include #include #include #include // local includes #include "nvenc/nvenc_config.h" namespace config { constexpr int SUNSHINE_VIRTUAL_DISPLAY_MAX_PERMANENT_COUNT = 4; // Valid range for the packetsize limit (stream_t::packetsize) constexpr int PACKETSIZE_MIN = 200; constexpr int PACKETSIZE_MAX = 65535; constexpr int PACKETSIZE_SMALL = 500; constexpr int PACKETSIZE_LARGE = 1456; // track modified config options inline std::unordered_map modified_config_settings; // when a stream is active, we defer some settings until all sessions end inline std::unordered_map pending_config_settings; inline constexpr std::array redacted_config = { "csrf_allowed_origins" }; void log_config_settings(const std::unordered_map &vars, bool save); struct video_t { enum class virtual_display_mode_e { disabled, ///< Use physical display (output_name) per_client, ///< Create unique virtual display per client shared ///< Use single shared virtual display for all clients }; enum class virtual_display_layout_e { exclusive, ///< Deactivate all other displays (only the virtual display stays visible) extended, ///< Keep other displays active and extend the desktop with the virtual display extended_primary, ///< Extend the desktop and force the virtual display to be primary extended_isolated, ///< Extend the desktop and move the virtual display far away from other monitors extended_primary_isolated ///< Extend the desktop, force the virtual display to be primary, and isolate it }; bool limit_framerate; // Config key: dd_wa_virtual_double_refresh (legacy alias: double_refreshrate) bool double_refreshrate; // ffmpeg params int qp; // higher == more compression and less quality int hevc_mode; int av1_mode; bool pyrowave; ///< Advertise PyroWave (bitStreamFormat 3) when the capture GPU can encode it. int min_threads; // Minimum number of threads/slices for CPU encoding struct { std::string sw_preset; std::string sw_tune; std::optional svtav1_preset; } sw; nvenc::nvenc_config nv; bool nv_realtime_hags; bool nv_opengl_vulkan_on_dxgi; bool nv_sunshine_high_power_mode; struct { int preset; int multipass; int h264_coder; int aq; int vbv_percentage_increase; int split_encode_mode; } nv_legacy; struct { std::optional qsv_preset; std::optional qsv_cavlc; bool qsv_slow_hevc; } qsv; struct { std::optional amd_usage_h264; std::optional amd_usage_hevc; std::optional amd_usage_av1; std::optional amd_rc_h264; std::optional amd_rc_hevc; std::optional amd_rc_av1; std::optional amd_qvbr_quality_level; std::optional amd_enforce_hrd; std::optional amd_quality_h264; // nullopt = follow usage-preset default std::optional amd_quality_hevc; // nullopt = follow usage-preset default std::optional amd_quality_av1; // nullopt = follow usage-preset default std::optional amd_preanalysis; std::optional amd_vbaq; // nullopt = follow usage-preset default int amd_coder; // Native AMF encoder (amdvce_experimental) tuning knobs. int amd_ltr_frames; // Long-term reference frames for RFI (0 = off) int amd_input_queue_size; // AMF input queue depth (0 = driver default) // Curated tri-state native-AMF feature knobs. nullopt (auto) leaves the // AMF driver default untouched; 1 forces the property on and 0 forces it // off — both explicit values are applied and read back like any other. std::optional amd_smart_access_video; // Multi-VCN encode (Smart Access Video): 1=on, 0=off std::optional amd_lowlatency_mode; // AMF LOWLATENCY_MODE (H.264/HEVC): 1=on, 0=off std::optional amd_high_motion_quality_boost; // High-motion quality boost: 1=on, 0=off std::optional amd_av1_screen_content; // AV1 screen-content tools: 1=on, 0=off std::optional amd_av1_latency_mode; // AV1 encoding-latency mode (0-3) } amd; struct { int vt_allow_sw; int vt_require_sw; int vt_realtime; int vt_coder; } vt; struct { bool strict_rc_buffer; } vaapi; struct { int tune; // 0=default, 1=hq, 2=ll, 3=ull, 4=lossless int rc_mode; // 0=driver, 1=cqp, 2=cbr, 4=vbr } vk; // NVIDIA TrueHDR (RTX HDR) SDR->HDR synthesis. Conversion is opt-in per app via // runtime overrides. // Tuning dials mirror the NVIDIA App RTX HDR overlay. Per-app overrides live in apps.json // (see proc::ctx_t). struct rtx_hdr_t { bool enabled; ///< Enables conversion when an app/client runtime override opts the stream in. bool force_sdr; ///< Legacy compatibility setting; app-enabled RTX HDR always forces SDR source. int sdr_brightness; ///< 0..100 brightness boost for desktop/non-matching RTX HDR fallback frames int contrast; ///< -100..100 (overlay "Contrast", default 0 = neutral) int saturation; ///< -100..100 (overlay "Saturation", default 0 = neutral) int middle_gray; ///< 10..100 (overlay "Middle Gray", default 50) int peak_brightness; ///< 400..2000 nits (overlay "Peak Brightness", default 1000) } rtx_hdr; std::string capture; std::string encoder; std::string adapter_name; std::string adapter_pnp_id; std::string output_name; virtual_display_mode_e virtual_display_mode; virtual_display_layout_e virtual_display_layout; bool remote_monitor_mute_audio; ///< Do not capture or transmit audio for Remote Monitor sessions. bool remote_monitor_disconnect_on_stream_end; ///< Release a Remote Monitor when its RTSP stream ends. bool remote_monitor_disconnect_on_client_disconnect; ///< Release a Remote Monitor when its paired client transport disconnects. bool remote_monitor_terminate_on_first_request; ///< Let extra clients terminate the active game with one Terminate request. bool remote_monitor_confirm_app_replacement; ///< Require confirmation before an ungated launch replaces the active game. struct dd_t { struct workarounds_t { bool dummy_plug_hdr10; ///< Force 30 Hz and HDR for physical dummy plugs (requires VSYNC override). }; enum class config_option_e { disabled, ///< Disable the configuration for the device. verify_only, ///< @seealso{display_device::SingleDisplayConfiguration::DevicePreparation} ensure_active, ///< @seealso{display_device::SingleDisplayConfiguration::DevicePreparation} ensure_primary, ///< @seealso{display_device::SingleDisplayConfiguration::DevicePreparation} ensure_only_display ///< @seealso{display_device::SingleDisplayConfiguration::DevicePreparation} }; enum class resolution_option_e { disabled, ///< Do not change resolution. automatic, ///< Change resolution and use the one received from Moonlight. manual ///< Change resolution and use the manually provided one. }; enum class refresh_rate_option_e { disabled, ///< Do not change refresh rate. automatic, ///< Change refresh rate and use the one received from Moonlight. manual, ///< Change refresh rate and use the manually provided one. prefer_highest ///< Prefer the highest available refresh rate for the selected resolution. }; enum class hdr_option_e { disabled, ///< Do not change HDR settings. automatic ///< Change HDR settings and use the state requested by Moonlight. }; enum class hdr_request_override_e { automatic, ///< Use HDR state requested by the client. force_on, ///< Force HDR enabled for the session. force_off ///< Force HDR disabled for the session. }; struct mode_remapping_entry_t { std::string requested_resolution; std::string requested_fps; std::string final_resolution; std::string final_refresh_rate; }; struct mode_remapping_t { std::vector mixed; ///< To be used when `resolution_option` and `refresh_rate_option` is set to `automatic`. std::vector resolution_only; ///< To be use when only `resolution_option` is set to `automatic`. std::vector refresh_rate_only; ///< To be use when only `refresh_rate_option` is set to `automatic`. }; config_option_e configuration_option; resolution_option_e resolution_option; std::string manual_resolution; ///< Manual resolution in case `resolution_option == resolution_option_e::manual`. refresh_rate_option_e refresh_rate_option; std::string manual_refresh_rate; ///< Manual refresh rate in case `refresh_rate_option == refresh_rate_option_e::manual`. hdr_option_e hdr_option; hdr_request_override_e hdr_request_override; std::chrono::milliseconds config_revert_delay; ///< Time to wait until settings are reverted (after stream ends/app exists). bool config_revert_on_disconnect; ///< Specify whether to revert display configuration on client disconnect. int paused_virtual_display_timeout_secs; ///< Optional delay before virtual display cleanup while stream is paused (0 keeps alive). enum class helper_engine_e : int { automatic, ///< v2 engine on pre-release builds, legacy engine on stable releases. v2, ///< Always use the v2 state-machine engine. legacy ///< Always use the legacy engine. }; bool always_restore_from_golden; ///< When true, prefer golden snapshot over session snapshots during restore (reduces stuck virtual screens). helper_engine_e display_helper_engine; ///< Which display helper engine to run. int snapshot_restore_hotkey; ///< Virtual-key code for restore hotkey (0 disables). std::uint32_t snapshot_restore_hotkey_modifiers; ///< Modifier flags for the restore hotkey. bool use_sunshine_virtual_display_driver; ///< Use the ArtLight Server Display Driver instead of rollback drivers such as SudoVDA. bool activate_virtual_display; ///< Auto-activate Sunshine virtual display when selected as the target output. int virtual_display_scale_percent; ///< Virtual-display scale percent (-1 is automatic; 0 preserves the compositor's choice). int virtual_display_permanent_count; ///< Number of always-present Sunshine virtual displays to request when explicitly configured. bool virtual_display_permanent_count_configured; ///< False preserves installs that predate this setting. std::vector virtual_display_outputs; ///< Linux connector names reserved for private streaming displays; empty enables managed-VKMS discovery. std::vector snapshot_exclude_devices; ///< Device IDs to skip when saving display snapshots. mode_remapping_t mode_remapping; workarounds_t wa; bool vulkan_hdr_layer; ///< Register the Vulkan HDR implicit layer that exposes HDR surface formats on virtual displays. Disable to recover from Vulkan access violations in third-party apps. bool wayland_hdr_compatibility; ///< Opt in to native KDE/Wayland HDR launch environment compatibility for resolved HDR streams. } dd; int max_bitrate; // Maximum bitrate, sets ceiling in kbps for bitrate requested from client double minimum_fps_target; ///< Lowest framerate that will be used when streaming. Range 0-1000, 0 = half of client's requested framerate. bool wgc_pacing_smoothing; ///< Smooth WGC delivered frame cadence under low-latency (Reflex) source caps by snapping the pacing-group re-anchor back onto the prior grid instead of the jittery arrival phase. Disable for byte-for-byte legacy pacing. std::string fallback_mode; bool ignore_encoder_probe_failure; }; struct audio_t { std::string sink; std::string virtual_sink; bool stream; bool install_steam_drivers; bool keep_default; bool auto_capture; bool sink_capture_only; ///< Capture the selected Windows audio sink without changing default outputs. }; 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 struct stream_t { std::chrono::milliseconds ping_timeout; std::string file_apps; int fec_percentage; int video_max_batch_size_kb; int pyrowave_critical_fec_percentage; ///< Parity on the PyroWave shards holding the coarsest wavelet level (at least 2 shards); 0 = none. // Video encryption settings for LAN and WAN streams int lan_encryption_mode; int wan_encryption_mode; // Cap the RTP send pacer (kbps). 0 = legacy ~80% of 1 Gbps assumption, which // collapses to a no-op on a slower WiFi link. Set to ~1.3x stream bitrate when // streaming over WiFi to spread the per-frame burst across the full frame slot. int pacing_max_bitrate_kbps; // Limit the packetsize to avoid fragmentation on a low MTU link. 0 = off. int packetsize; // ── USB device sharing (USB/IP) ──────────────────────────────────────────────────────────── // // On by default, and it is safe to be, because this switch is not the gate. The gate is what // the exporter is sharing: the list this reads is the far machine's offered devices, and a // machine offering nothing lands on ListOutcome::NothingOffered and returns before it attaches // anything. So this decides whether THIS PC reaches for devices already on offer - never whether // a device moves. Off here would be a second click on top of a decision the person already made // on the machine the device is plugged into. // // What it does still matters: an attached device is GONE from the machine it is plugged into for // the length of the stream. Leave input_usbip_busids empty to take everything on offer; name // devices to narrow it. // // Naming: exporter = the machine the device is plugged into; importer = the machine it arrives // on. This build is the importer. Never write bare "host" or "client" here. bool input_usbip_enabled = true; // Which exporter to attach from. Empty is the NORMAL case, and means "the machine this stream was // asked for from" - rtsp_stream::launch_session_t::rtsp_source_address, which is the address the // launch request came from. That is the machine the person is sitting at, and it is where the // devices are plugged in whenever they are sharing their own keyboard and mouse, which is the // ordinary case. A value here is an override for an unusual topology where the exporter is some // third machine - not the ordinary path. std::string input_usbip_exporter; // Optional comma-separated busid allowlist. Empty means everything the exporter offers, which // is what someone who has just switched this on almost always wants. std::string input_usbip_busids; }; struct nvhttp_t { // Could be any of the following values: // pc|lan|wan std::string origin_web_ui_allowed; std::string pkey; std::string cert; std::string sunshine_name; std::string file_state; std::string vibeshine_file_state; std::string external_ip; }; struct input_t { std::unordered_map keybindings; std::chrono::milliseconds back_button_timeout; std::chrono::milliseconds key_repeat_delay; std::chrono::duration key_repeat_period; std::string gamepad; bool ds4_back_as_touchpad_click; bool motion_as_ds4; bool touchpad_as_ds4; // When forcing DS5 emulation via Inputtino, randomize the virtual controller MAC // to avoid client-side config mixing when controllers are swapped. bool ds5_inputtino_randomize_mac; // Apply native Sony audio endpoint compatibility to streamed Proton launches. bool proton_dualsense_compatibility; bool keyboard; bool mouse; bool controller; bool always_send_scancodes; bool high_resolution_scrolling; bool native_pen_touch; bool enable_input_only_mode; bool forward_rumble; /** * The combination that ends a stream whose keyboard has been taken away from its client, * written the way a person would: "Ctrl+Alt+Shift+Q". Shared by both platforms, so the same * value means the same thing on Windows and Linux. Guaranteed valid by the time anything * reads it - config.cpp falls back to the default rather than letting a typo stop the server. */ std::string input_quit_hotkey; }; struct frame_limiter_t { enum class virtual_display_capture_mode_e { enabled, disabled, legacy, vrr, ///< Fixed 1000 Hz virtual display. }; bool enable {false}; // Provider selector. Linux defaults to Proton with a MangoHUD overlay; // Windows defaults to RTSS or NVIDIA Control Panel. // Supported values: "auto", "mangohud", "proton", "mangohud-proton", // "nvidia-control-panel", "rtss", "none". std::string provider; // Optional FPS limit override in millihertz. 0 uses the stream's requested FPS. std::uint32_t fps_limit_millihz {0}; // Linux MangoHUD overlay presentation. "custom" preserves the user's // configuration; 1-4 select MangoHUD's standard built-in presets. std::string mangohud_preset {"custom"}; bool mangohud_always_show_graph {false}; // MangoHUD's own limiter timing. Early favors smooth pacing; late favors latency. std::string mangohud_limiter_method {"late"}; // When enabled, Sunshine forces the NVIDIA driver VSYNC setting to Off during streams when available. // When NVIDIA overrides are unavailable, the display helper falls back to the highest refresh rate instead. // Restores the previous VSYNC state when streaming stops. bool disable_vsync {false}; // Virtual-display capture policy. Enabled keeps the virtual display at a fixed 4x // refresh and lets WGC admit 2x desktop / 4x game frames without changing the mode. // VRR holds the virtual display at a fixed 1000 Hz regardless of stream FPS, with the // same WGC admission: DWM composes and WGC timestamps frames on the display's refresh // grid, so 1000 Hz keeps every frame within 1 ms of when the game presented it for // clients that pace playback from RTP timestamps. // Legacy uses a fixed 2x refresh; disabled leaves the automatic policy off. virtual_display_capture_mode_e virtual_display_capture_mode { virtual_display_capture_mode_e::enabled }; [[nodiscard]] bool virtual_display_limiter_enabled() const { return virtual_display_capture_mode != virtual_display_capture_mode_e::disabled; } [[nodiscard]] bool game_aware_virtual_display_refresh_enabled() const { return virtual_display_capture_mode == virtual_display_capture_mode_e::enabled || virtual_display_capture_mode == virtual_display_capture_mode_e::vrr; } [[nodiscard]] int fixed_virtual_display_refresh_multiplier() const { if (virtual_display_capture_mode == virtual_display_capture_mode_e::legacy) { return 2; } return virtual_display_capture_mode == virtual_display_capture_mode_e::enabled ? 4 : 1; } // Exact virtual-display refresh that replaces the multiplier; 0 when unused. VRR mode // always uses it; Automatic uses it when the client requested VRR presentation // (clientVrrRequested), because such clients pace playback from the RTP timestamps. [[nodiscard]] std::uint32_t fixed_virtual_display_refresh_millihz(const bool client_vrr_requested = false) const { const bool vrr = virtual_display_capture_mode == virtual_display_capture_mode_e::vrr || (client_vrr_requested && virtual_display_capture_mode == virtual_display_capture_mode_e::enabled); return vrr ? 1'000'000u : 0u; } }; // Windows-only: RTSS integration settings struct rtss_t { // RTSS install path. If empty, defaults to "%PROGRAMFILES%/RivaTuner Statistics Server" std::string install_path; // SyncLimiter mode. One of: "async", "front edge sync", "back edge sync", "nvidia reflex". // If empty or unrecognized, SyncLimiter is not modified. std::string frame_limit_type; // When enabled, the configured SyncLimiter mode may replace the automatic NVIDIA Reflex // policy for virtual-display streams. Game-provided frame generation still selects Reflex // unless the app/client supplies an explicit RTSS mode override. bool allow_virtual_display_override {false}; }; struct lossless_scaling_t { std::string exe_path; bool legacy_auto_detect {false}; }; namespace flag { enum flag_e : std::size_t { PIN_STDIN = 0, ///< Read PIN from stdin instead of http FRESH_STATE, ///< Do not load or save state FORCE_VIDEO_HEADER_REPLACE, ///< force replacing headers inside video data UPNP, ///< Try Universal Plug 'n Play CONST_PIN, ///< Use "universal" pin FLAG_SIZE ///< Number of flags }; } // namespace flag struct prep_cmd_t { prep_cmd_t(std::string &&do_cmd, std::string &&undo_cmd, bool &&elevated): do_cmd(std::move(do_cmd)), undo_cmd(std::move(undo_cmd)), elevated(std::move(elevated)) { } explicit prep_cmd_t(std::string &&do_cmd, bool &&elevated): do_cmd(std::move(do_cmd)), elevated(std::move(elevated)) { } std::string do_cmd; std::string undo_cmd; bool elevated; }; struct server_cmd_t { server_cmd_t(std::string &&cmd_name, std::string &&cmd_val, bool &&elevated): cmd_name(std::move(cmd_name)), cmd_val(std::move(cmd_val)), elevated(std::move(elevated)) { } std::string cmd_name; std::string cmd_val; bool elevated; }; struct sunshine_t { bool hide_tray_controls; bool enable_pairing; bool enable_discovery; bool envvar_compatibility_mode; std::string locale; int min_log_level; std::bitset flags; std::string credentials_file; std::string username; std::string password; std::string salt; std::string config_file; struct cmd_t { std::string name; int argc; char **argv; } cmd; std::uint16_t port; std::string address_family; std::string bind_address; std::string log_file; bool notify_pre_releases; bool legacy_ordering; bool system_tray; std::vector prep_cmds; std::vector state_cmds; std::vector server_cmds; std::chrono::seconds session_token_ttl; ///< Session token time-to-live (seconds) std::chrono::seconds remember_me_refresh_token_ttl; ///< Trusted device (remember-me) refresh TTL // Interval in seconds between automatic update checks (0 disables periodic checks) int update_check_interval_seconds {86400}; bool session_history_enabled {true}; ///< Persist stream/session history to SQLite int session_history_ttl_days {0}; ///< Delete ended sessions older than this many days (0 disables age pruning) int session_history_db_size_limit_mb {0}; ///< Approximate live DB quota in MiB (0 disables size pruning) std::vector csrf_allowed_origins; bool realtime_stats_enabled {true}; ///< Sample live host stats (CPU/GPU/RAM/VRAM) for the web UI int realtime_stats_poll_interval_ms {2000}; ///< Host stats sampler interval in milliseconds }; extern video_t video; extern audio_t audio; extern stream_t stream; extern nvhttp_t nvhttp; extern input_t input; extern frame_limiter_t frame_limiter; extern rtss_t rtss; extern lossless_scaling_t lossless_scaling; extern sunshine_t sunshine; int parse(int argc, char *argv[]); std::unordered_map parse_config(const std::string_view &file_content); /** * Merge one raw configuration layer into another while treating the Windows * capture-adapter name and persistent PnP identity as a single value. * * A layer that supplies adapter_name without adapter_pnp_id intentionally * selects legacy name-only behavior and clears an inherited PnP identity. * adapter_pnp_id without adapter_name is ignored. */ void merge_config_overrides( std::unordered_map &base, const std::unordered_map &overrides ); bool adapter_config_overrides_compatible_with_active( const std::unordered_map &requested_overrides ); // Hot-reload helpers void apply_config_now(); void mark_deferred_reload(); void maybe_apply_deferred(); // Gate helpers so session start/resume can hold a shared lock while apply holds a unique lock. std::shared_lock acquire_apply_read_gate(); void record_active_adapter_config(); // Runtime, non-persisted config overrides (e.g. per-application overrides). // Values use the same raw representation as the config file (strings for string keys, // JSON dumps for non-string keys). void set_runtime_config_overrides(std::unordered_map overrides); void clear_runtime_config_overrides(); std::unordered_map runtime_config_overrides_snapshot(); bool has_runtime_config_override(std::string_view key); bool runtime_config_override_enabled(std::string_view key); bool has_runtime_config_overrides(); void set_runtime_output_name_override(std::optional output_name); std::optional runtime_output_name_override(); std::string get_active_output_name(); // A recovery worker can publish a temporary virtual-output override. The // lease makes rollback conditional so an older recovery cannot erase an // override installed by a newer session. using runtime_output_override_lease_t = std::uint64_t; runtime_output_override_lease_t set_runtime_output_name_override_with_lease(std::string output_name); bool clear_runtime_output_name_override_if_lease(runtime_output_override_lease_t lease); #ifdef _WIN32 // The lock-screen virtual-output retry worker is owned work. Main stops // and joins it before configuration, display-helper, and mail teardown. void request_deferred_virtual_output_reapply_shutdown(); void join_deferred_virtual_output_reapply_worker(); #endif } // namespace config