server/src/config.h
Rama main · réplica de lectura del repositorio original.
/**
* @file src/config.h
* @brief Declarations for the configuration of Sunshine.
*/
#pragma once
// standard includes
#include <array>
#include <bitset>
#include <chrono>
#include <cstdint>
#include <optional>
#include <shared_mutex>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
// 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<std::string, std::string> modified_config_settings;
// when a stream is active, we defer some settings until all sessions end
inline std::unordered_map<std::string, std::string> pending_config_settings;
inline constexpr std::array redacted_config = {
"csrf_allowed_origins"
};
void log_config_settings(const std::unordered_map<std::string, std::string> &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<int> 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<int> qsv_preset;
std::optional<int> qsv_cavlc;
bool qsv_slow_hevc;
} qsv;
struct {
std::optional<int> amd_usage_h264;
std::optional<int> amd_usage_hevc;
std::optional<int> amd_usage_av1;
std::optional<int> amd_rc_h264;
std::optional<int> amd_rc_hevc;
std::optional<int> amd_rc_av1;
std::optional<int> amd_qvbr_quality_level;
std::optional<int> amd_enforce_hrd;
std::optional<int> amd_quality_h264; // nullopt = follow usage-preset default
std::optional<int> amd_quality_hevc; // nullopt = follow usage-preset default
std::optional<int> amd_quality_av1; // nullopt = follow usage-preset default
std::optional<int> amd_preanalysis;
std::optional<int> 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<int> amd_smart_access_video; // Multi-VCN encode (Smart Access Video): 1=on, 0=off
std::optional<int> amd_lowlatency_mode; // AMF LOWLATENCY_MODE (H.264/HEVC): 1=on, 0=off
std::optional<int> amd_high_motion_quality_boost; // High-motion quality boost: 1=on, 0=off
std::optional<int> amd_av1_screen_content; // AV1 screen-content tools: 1=on, 0=off
std::optional<int> 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<mode_remapping_entry_t> mixed; ///< To be used when `resolution_option` and `refresh_rate_option` is set to `automatic`.
std::vector<mode_remapping_entry_t> resolution_only; ///< To be use when only `resolution_option` is set to `automatic`.
std::vector<mode_remapping_entry_t> 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<std::string> virtual_display_outputs; ///< Linux connector names reserved for private streaming displays; empty enables managed-VKMS discovery.
std::vector<std::string> 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<int, int> keybindings;
std::chrono::milliseconds back_button_timeout;
std::chrono::milliseconds key_repeat_delay;
std::chrono::duration<double> 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<flag::FLAG_SIZE> 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_cmd_t> prep_cmds;
std::vector<prep_cmd_t> state_cmds;
std::vector<server_cmd_t> 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<std::string> 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<std::string, std::string> 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<std::string, std::string> &base,
const std::unordered_map<std::string, std::string> &overrides
);
bool adapter_config_overrides_compatible_with_active(
const std::unordered_map<std::string, std::string> &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<std::shared_mutex> 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<std::string, std::string> overrides);
void clear_runtime_config_overrides();
std::unordered_map<std::string, std::string> 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<std::string> output_name);
std::optional<std::string> 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