Ctrl K
ComunidadGuías…

server/src/config.h

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * @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