Skip to content

feat(vdd): add virtual display management#5114

Open
fatebugs wants to merge 12 commits into
LizardByte:masterfrom
fatebugs:pr/vdd
Open

feat(vdd): add virtual display management#5114
fatebugs wants to merge 12 commits into
LizardByte:masterfrom
fatebugs:pr/vdd

Conversation

@fatebugs
Copy link
Copy Markdown

@fatebugs fatebugs commented May 14, 2026

Description

Integrate Parsec VDD to enable dynamic virtual display management on Windows. This allows Sunshine to create and manage virtual displays without requiring a physical monitor, which is essential for headless streaming setups.

Key changes

  • VDD Core (vdd_control.cpp/h): Virtual display lifecycle management — create, remove, enumerate displays, and handle display mode/resolution configuration
  • Configuration (config.cpp/h): New VDD configuration options (enable/disable, adapter selection, display count, resolution presets)
  • REST API (confighttp.cpp): Endpoints for runtime VDD control (add/remove display, query status)
  • Startup (main.cpp): Automatic VDD initialization on service start when enabled
  • System Tray (system_tray.cpp): Quick VDD controls in tray menu
  • Web UI (VirtualDisplay.vue): Full management panel for virtual displays with add/remove/resolution controls
  • Web UI integration (AudioVideo.vue, config.html): Navigation and display area integration
  • i18n (en/zh/zh_TW): Full translations for all VDD-related UI strings
  • Build (cmake + third-party/parsec-vdd): VDD header integration, compile definitions

Screenshot

iShot_2026-05-14_21 02 04 iShot_2026-05-14_21 02 51

Issues Fixed or Closed

  • N/A

Roadmap Issues

  • N/A

Type of Change

  • feat: New feature (non-breaking change which adds functionality)
  • fix: Bug fix (non-breaking change which fixes an issue)
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.)
  • refactor: Code change that neither fixes a bug nor adds a feature
  • perf: Code change that improves performance
  • test: Adding missing tests or correcting existing tests
  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Other changes that don't modify src or test files
  • revert: Reverts a previous commit
  • BREAKING CHANGE: Introduces a breaking change (can be combined with any type above)

Checklist

  • Code follows the style guidelines of this project
  • Code has been self-reviewed
  • Code has been commented, particularly in hard-to-understand areas
  • Code docstring/documentation-blocks for new or existing methods/components have been added or updated
  • Unit tests have been added or updated for any new or modified functionality

AI Usage

  • None: No AI tools were used in creating this PR
  • Light: AI provided minor assistance (formatting, simple suggestions)
  • Moderate: AI helped with code generation or debugging specific parts
  • Heavy: AI generated most or all of the code changes

Copilot AI review requested due to automatic review settings May 14, 2026 11:57
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@ReenigneArcher ReenigneArcher marked this pull request as draft May 14, 2026 12:35
@fatebugs
Copy link
Copy Markdown
Author

@ReenigneArcher What else do I need to do to merge this PR into the master?

@ReenigneArcher

This comment was marked as resolved.

@neatnoise
Copy link
Copy Markdown
Contributor

That is useful for headless setups. I tried it out.
Few remarks when headless setup was used:

  • I managed to make it work by setting manually these vars (I couldn't set vdd_enabled and vdd_refresh_rate via gui):
vdd_display_count = 1
vdd_enabled = 1
vdd_refresh_rate = 60
  • I had to force right adapter with adapter_name setting
  • I had an issue to save display settings via sunshine gui. It got lost after the sunshine restart even when display was added and clicked "Save" + "Apply"
  • I suggest to add some information about the parsec VDD installation requirement and links to the drivers.

@fatebugs
Copy link
Copy Markdown
Author

That is useful for headless setups. I tried it out. Few remarks when headless setup was used:

  • I managed to make it work by setting manually these vars (I couldn't set vdd_enabled and vdd_refresh_rate via gui):
vdd_display_count = 1
vdd_enabled = 1
vdd_refresh_rate = 60
  • I had to force right adapter with adapter_name setting
  • I had an issue to save display settings via sunshine gui. It got lost after the sunshine restart even when display was added and clicked "Save" + "Apply"
  • I suggest to add some information about the parsec VDD installation requirement and links to the drivers.

是的,我也发现了这个问题,我今天想办法修一下这个bug吧

fatebugs and others added 5 commits May 15, 2026 09:12
- Store each virtual display's resolution/refresh as JSON in config
- Restore per-display configs on startup, not just a flat count
- Add vdd_enabled toggle in Web UI for auto-start control
Add vdd_display_configs to the internal options list in config
consistency tests so the test doesn't fail on the new
auto-managed VDD persistence config key.
@Kishi85
Copy link
Copy Markdown
Contributor

Kishi85 commented May 15, 2026

Just a small note as I can't comment on the feature itself since this is a Windows-specific feature: For code maintainability any platform specific files (in this case seemingly vdd_control.cpp/h) should be put into the respective platform-specific source folder (in this case src/platform/windows/).

I'm also not sure if this should be sole part of Sunshine itself or should be rather integrated into libdisplaydevice as that was created specifically for interacting/managing display devices (on Windows for now).

@fatebugs
Copy link
Copy Markdown
Author

Just a small note as I can't comment on the feature itself since this is a Windows-specific feature: For code maintainability any platform specific files (in this case seemingly vdd_control.cpp/h) should be put into the respective source folder (in this case src/platform/windows/).

I'm also not sure if this should be sole part of Sunshine itself or should be rather integrated into libdisplaydevice as that was created specifically for interacting/managing display devices (on Windows for now).

so,perhaps the author of Sunshine has maintained other solutions, but I think in many cases, it would be better for Windows to directly create virtual displays in Sunshine rather than having to download other software to accomplish this task

- Catch nlohmann::json::parse_error instead of generic std::exception
- Use C++17 init-statement in vdd config parsing loop
@fatebugs fatebugs marked this pull request as ready for review May 15, 2026 05:26
@Kishi85
Copy link
Copy Markdown
Contributor

Kishi85 commented May 15, 2026

Just a small note as I can't comment on the feature itself since this is a Windows-specific feature: For code maintainability any platform specific files (in this case seemingly vdd_control.cpp/h) should be put into the respective source folder (in this case src/platform/windows/).
I'm also not sure if this should be sole part of Sunshine itself or should be rather integrated into libdisplaydevice as that was created specifically for interacting/managing display devices (on Windows for now).

so,perhaps the author of Sunshine has maintained other solutions, but I think in many cases, it would be better for Windows to directly create virtual displays in Sunshine rather than having to download other software to accomplish this task

I think you're misunderstanding my comment, it absolutely makes sense to have this as a feature in Sunshine. But since Sunshine is already using a separate (self developed) library for display management (at least on Windows) everything related to that including virtual displays might be better integrated there (and exposed using the library to Sunshine) from a coding point of view. So everything is in one place, stays extensible/reusable and is able to integrate other solutions (and maybe other Sunshine supported platforms) easier in the future.

Right now it's a feature that is specific to Windows as a platform (hence also my note about moving your new vddcontrol files to src/platform/windows) for one specific VDD (Parsec) that could be extended in the future and would benefit from a good codebase to do so.

@fatebugs
Copy link
Copy Markdown
Author

Just a small note as I can't comment on the feature itself since this is a Windows-specific feature: For code maintainability any platform specific files (in this case seemingly vdd_control.cpp/h) should be put into the respective source folder (in this case src/platform/windows/).
I'm also not sure if this should be sole part of Sunshine itself or should be rather integrated into libdisplaydevice as that was created specifically for interacting/managing display devices (on Windows for now).

so,perhaps the author of Sunshine has maintained other solutions, but I think in many cases, it would be better for Windows to directly create virtual displays in Sunshine rather than having to download other software to accomplish this task

I think you're misunderstanding my comment, it absolutely makes sense to have this as a feature in Sunshine. But since Sunshine is already using a separate (self developed) library for display management (at least on Windows) everything related to that including virtual displays might be better integrated there (and exposed using the library to Sunshine) from a coding point of view. So everything is in one place, stays extensible/reusable and is able to integrate other solutions (and maybe other Sunshine supported platforms) easier in the future.

Right now it's a feature that is specific to Windows as a platform (hence also my note about moving your new vddcontrol files to src/platform/windows) for one specific VDD (Parsec) that could be extended in the future and would benefit from a good codebase to do so.

好的呢,我看一看先

Relocate Windows-only virtual display driver control files to the
platform directory. Update include paths and CMake references.
@fatebugs fatebugs force-pushed the pr/vdd branch 2 times, most recently from 23fd2a5 to 6625780 Compare May 15, 2026 08:18
@fatebugs fatebugs marked this pull request as draft May 15, 2026 08:18
@fatebugs fatebugs marked this pull request as ready for review May 15, 2026 08:29
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Member

@ReenigneArcher ReenigneArcher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep the discussion here in English.

I agree with @Kishi85 on moving some of the code to libdisplaydevice and simplifying what needs to be done on the Sunshine side, as well as putting windows specific code into platform/windows.

We can also add a downloader for parsec-vdd, like we do for vigembus in the web-ui.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice your PR modifies localization files other than the default language (en). We do not accept contributions on GitHub for these files, but you can contribute on Crowdin. Please see https://docs.lizardbyte.dev/latest/developers/contributing.html#localization for more information.

Comment on lines +371 to +378
const i18nKey = 'config.' + key;
const translated = this.$t(i18nKey);
const label = translated === i18nKey
? key.replaceAll('_', ' ').replaceAll(/\b\w/g, l => l.toUpperCase())
: translated;
options.push({
key: key,
label: key.replaceAll('_', ' ').replaceAll(/\b\w/g, l => l.toUpperCase()),
label: label,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change is unrelated and should be separated from this PR.

const std::set<std::string, std::less<>> internalOptions = {
"flags" // Internal config flags, not user-configurable
"flags", // Internal config flags, not user-configurable
"vdd_display_configs" // Automatically managed by VDD persistence
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be in config.cpp if not user configurable. "flags" are because these are CLI options that temporarily override the config file.

Comment thread docs/configuration.md
Comment on lines +1399 to +1403
<div class="alert alert-info" role="alert">
<strong>Acknowledgement:</strong> The virtual display feature is powered by the
<a href="https://github.com/nomi-san/parsec-vdd" target="_blank">Parsec Virtual Display Driver</a>
created by <a href="https://github.com/nomi-san" target="_blank">nomi-san</a>.
</div>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<div class="alert alert-info" role="alert">
<strong>Acknowledgement:</strong> The virtual display feature is powered by the
<a href="https://github.com/nomi-san/parsec-vdd" target="_blank">Parsec Virtual Display Driver</a>
created by <a href="https://github.com/nomi-san" target="_blank">nomi-san</a>.
</div>

This doesn't match any existing patterns in the configuration.md file.

Comment thread src/config.cpp
Comment on lines +529 to +532
1920, // virtual_display_width
1080, // virtual_display_height
144, // virtual_display_refresh_rate
0, // virtual_display_count
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just set these based on what Moonlight requests?

Comment thread src/confighttp.cpp
Comment on lines +1075 to +1077
std::stringstream config_stream;
for (const auto &[k, v] : vars) {
config_stream << k << " = " << v << '\n';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change, should be a separate PR.

Comment thread src/confighttp.cpp
Comment on lines +2006 to +2011
#ifdef _WIN32
server.resource["^/api/vdd/status$"]["GET"] = getVddStatus;
server.resource["^/api/vdd/add$"]["POST"] = addVddDisplay;
server.resource["^/api/vdd/remove$"]["POST"] = removeVddDisplay;
server.resource["^/api/vdd/remove-all$"]["POST"] = removeAllVddDisplays;
#endif
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove ifdef and handle in the endpoint itself when not on Windows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants