chore(docs): update readme and docs, add RE doc

This commit is contained in:
Nguyen Duy
2026-05-15 12:45:09 +07:00
parent 765806b7ec
commit 525773f449
4 changed files with 400 additions and 56 deletions
+57 -38
View File
@@ -42,32 +42,30 @@ for an enhanced, flexible visual experience.
## 📺 ParsecDisplay App
ParsecDisplay is a comprehensive virtual display manager for Parsec VDD, built
with C# and WPF. The app provides an intuitive interface to manage virtual
displays, showing the number of active displays and allowing users to add or
remove specific virtual displays. It also supports features like changing
display resolution, capturing screenshots, and more, making it a versatile tool
for flexible display management.
ParsecDisplay is a virtual display manager for Parsec VDD, built with C# and
WPF. It provides a tray-based interface to add and remove virtual displays,
change their resolution, refresh rate, and orientation, capture screenshots,
and more.
👉 Check out [Releases](https://github.com/nomi-san/parsec-vdd/releases) to
download it.
<p align="center">
<img src="https://github.com/nomi-san/parsec-vdd/assets/38210249/71b25bc6-eee1-4d80-94e0-e39eab7f8fb9" />
<img src="https://github.com/user-attachments/assets/2c014dbb-2358-4906-90fb-f94a62087065" />
</p>
## 🚀 Using Core API
### Design notes
Parsec VDD is designed to work with Parsec client-connection sessions. When the
user connects to the host, the app will start controlling the driver, it sends
IO control codes and gets results. When adding a virtual display, you will get
its index to be used for unplugging, the maximum number of displays could be
added up to 16 per adapter. You have to ping the driver periodically to keep
added displays alive, otherwise all of them will be unplugged after a second.
There's no direct way to manipulate added displays, you should call Win32
Display API to change their display mode (see the ParsecDisplay source).
Parsec VDD is designed to work with Parsec client-connection sessions. When
the user connects to the host, the app starts controlling the driver
sending IO control codes and receiving results. Adding a virtual display
returns an index, used later to unplug it; up to 16 displays can be added
per adapter. The driver must be pinged periodically to keep added displays
alive, otherwise all of them will be unplugged after about a second. There
is no direct way to manipulate added displays call the Win32 Display API
to change their display mode (see the ParsecDisplay source).
```mermaid
flowchart LR
@@ -88,13 +86,13 @@ flowchart LR
For detailed instructions and usage examples, refer to the [VDD_LIBRARY_USAGE](./docs/VDD_LIBRARY_USAGE.md).
- The core API is designed as single C/C++ header that can be added to any
- The core API is designed as a single C/C++ header that can be added to any
project, 👉 [core/parsec-vdd.h](./core/parsec-vdd.h)
- There is also a simple demo program, 👉 [core/vdd-demo.cc](./core/vdd-demo.cc)
### Picking a driver
You have to install the driver to make them work.
You have to install the driver before any virtual displays can be created.
| Version | Minimum OS | IddCx | Notes |
| :---------------- | :-------------- | :---: | :-------------------------------------------------------- |
@@ -141,9 +139,10 @@ In addition, you can run the driver setup in silent mode to install it quickly.
### 1. HDR support
Parsec VDD does not support HDR on its displays (see the EDID below).
Theoretically, you can unlock support by editing the EDID, then adding HDR
metadata and setting 10-bit+ color depth. Unfortunately, you cannot flash its
firmware like a physical device, or modify the registry value.
Theoretically, you can unlock support by editing the EDID to include HDR
metadata and a 10-bit+ color depth. Unfortunately, you cannot flash its
firmware the way you would a physical monitor — there is no registry
setting to toggle either.
All IDDs have their own fixed EDID block inside the driver binary to initialize
the monitor specs. So the solution is to modify this block in the driver DLL
@@ -152,8 +151,8 @@ the monitor specs. So the solution is to modify this block in the driver DLL
### 2. Custom resolutions
Before connecting, the virtual display looks in the `HKLM\SOFTWARE\Parsec\vdd`
registry for additional preset resolutions. Currently this supports a maximum of
5 values.
registry for additional preset resolutions. Currently this supports a maximum
of 5 entries.
```yaml
HKLM\SOFTWARE\Parsec\vdd:
@@ -180,10 +179,32 @@ HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Connectivity
```
This option causes your main display to turn off when virtual displays are
added, making it difficult to turn the display on and disrupting the remote
desktop session.
added, making it difficult to turn the main display back on and disrupting
the remote desktop session.
### 2. // todo
### 2. Windows 10 Connectivity registry quirk
Windows 10 caches display arrangements keyed by the _combination_ of attached
display IDs. When a middle display is unplugged, the remaining subset
(e.g. `DISP001_DISP003`) is a new combo Windows hasn't seen — those displays
fall back to default mode and arrangement.
The app works around this by always unplugging **right-to-left** (latest
driver index first) during sleep, exit, and `vdd remove all`. See
[issue #23](https://github.com/nomi-san/parsec-vdd/issues/23)
for the full write-up.
### 3. Headless before user login
The app is a GUI process and requires an interactive user session (Vista+
session 0 isolation). On a freshly-booted headless host with no auto-login,
nothing runs until the user signs in. Workarounds:
- Enable auto-login on the host.
- Or use a Task Scheduler entry that runs at logon with desktop interaction.
- Or use the service-based fork
[ParsecVDA-Always-Connected](https://github.com/timminator/ParsecVDA-Always-Connected)
for fully headless single-display deployments.
## 🤔 Comparison with other IDDs
@@ -207,8 +228,8 @@ projects.
[virtual-display-rs]: https://github.com/MolotovCherry/virtual-display-rs
[Virtual-Display-Driver (HDR)]: https://github.com/itsmikethetech/Virtual-Display-Driver
**Signed** means that the driver files have a valid digital signature.
**H-Cursor** means hardware cursor support, without it, you will get a double
**Signed** means the driver files have a valid digital signature.
**H-Cursor** means hardware-cursor support without it, you get a double
cursor on some remote desktop apps. **Tweakable** is the ability to customize
display modes. Visit
[MSDN IddCx versions](https://learn.microsoft.com/en-us/windows-hardware/drivers/display/iddcx-versions)
@@ -228,19 +249,17 @@ Common preset display modes:
| 1600 x 900 | HD+ | 16:9 | 60/144/240 |
| 1280 x 720 | HD | 16:9 | 60/144/240 |
Check out [docs/PARSEC_VDD_SPECS](./docs/PARSEC_VDD_SPECS.md) to see full of
preset display modes the driver specs.
## 🤝 Sponsors
<table>
<tr>
<td><img src="https://github.com/user-attachments/assets/58e9a6f4-6630-437d-a758-b284c0ed41e7" /></td>
<td>Free code signing on Windows provided by <a href="https://signpath.io">SignPath.io</a>, certificate by <a href="https://signpath.org">SignPath Foundation</a></td>
</tr>
</table>
Check out [docs/PARSEC_VDD_SPECS](./docs/PARSEC_VDD_SPECS.md) for the full
list of preset display modes and driver specs.
## 🍻 Credits
- Thanks to Parsec for the driver
- The app's background was from old parsecgaming.com
<table>
<tr>
<td><img src="https://github.com/user-attachments/assets/58e9a6f4-6630-437d-a758-b284c0ed41e7" /></td>
<td>Trusted code signing on Windows provided by <a href="https://signpath.io">SignPath.io</a>, certificate by <a href="https://signpath.org">SignPath Foundation</a></td>
</tr>
</table>
+300
View File
@@ -0,0 +1,300 @@
# Parsec VDD — Reverse-Engineered Reference
Protocol-level documentation of the Parsec Virtual Display Driver as exercised
by Parsec's host application. This focuses on what the kernel-mode driver
expects from user-mode callers: device discovery, IOCTL formats, lifecycle,
and timing. It's intended as a reference for re-implementations (CLI tools,
managers, third-party hosts).
---
## 1. Architecture
Parsec VDD is an Indirect Display Driver (IDD) miniport that creates virtual
monitors on Windows 10+. Each running instance is identified by:
- **Hardware ID:** `Root\Parsec\VDA`
- **Setup class GUID:** `{4d36e968-e325-11ce-bfc1-08002be10318}` (Display)
- **Device interface GUID:** `{00b41627-04c4-429e-a26e-0265cf50c8fa}`
A host creates a software device with `SwDeviceCreate`, then communicates
through the device interface GUID via `DeviceIoControl`.
**Key constraints**
- Maximum **16 virtual monitors** per adapter (driver-side tracking is a
`DWORD[16]`, with `-1` marking empty slots).
- Maximum resolution clamped to **8192 × 4320** by the host.
- Default refresh rate is **60 Hz** when not specified.
- Custom preset resolutions live in the registry — up to 5 slots.
---
## 2. Device Lifecycle
```
MM_adapter_create (SwDeviceCreate)
MM_open_device (SetupDiGetClassDevs + CreateFileW)
MM_connect (opens handle, spawns keep-alive thread)
MM_device_add (IOCTL 0x22E004) × N monitors
MM_keepalive_thread (IOCTL 0x22A00C every ~200 ms — keeps displays alive)
host_vdd_privacy_thread (optional — blanks physical displays)
↓ [on disconnect]
MM_disconnect (IOCTL 0x22A008 × 16, close handle)
```
The device handle is opened **asynchronously** (`FILE_FLAG_OVERLAPPED`); every
IOCTL is issued with an `OVERLAPPED` structure and awaited via
`GetOverlappedResultEx`.
---
## 3. IOCTL Reference
All four IOCTLs use a **32-byte (`0x20`) zero-initialized input buffer**.
| IOCTL | Hex | Function | Direction | In | Out | Timeout | Purpose |
|-------|-----|----------|-----------|----|-----|---------|---------|
| `CTL_CODE(0x22, 0x802, 0, 0)` | `0x22E004` | `MM_device_add` | In/Out | 32 B | 4 B | 5000 ms | Create a virtual monitor. Output is the assigned monitor index (015). |
| `CTL_CODE(0x22, 0x802, 2, 0)` | `0x22A008` | `MM_disconnect` | In | 32 B | 0 | 1000 ms | Remove a single monitor. `InBuffer[1..4]` = monitor index. |
| `CTL_CODE(0x22, 0x803, 2, 0)` | `0x22A00C` | `MM_update_keepalive` | In | 32 B | 0 | 1000 ms | Watchdog ping. Must be sent every ~200 ms or the driver removes **all** monitors after ~1 s. |
| `CTL_CODE(0x22, 0x804, 0, 0)` | `0x22E010` | `MM_query_adapter_status` | In/Out | 32 B | 4 B | 1000 ms | Query the adapter. Output is a 4-byte status word (typically the driver minor version). |
### Buffer layout
```c
struct VDD_IOCTL_Input {
uint8_t reserved; // [0]
uint32_t monitor_index; // [1..4] — for remove/create
uint8_t padding[27]; // [5..31]
};
```
### IoControl pattern (important)
The driver can return `FALSE` from `DeviceIoControl` with a non-`ERROR_IO_PENDING`
status code **while still queuing** the operation. The robust pattern,
mirrored from the reference C library:
1. Call `DeviceIoControl`**ignore its synchronous return**.
2. Call `GetOverlappedResultEx(handle, &ov, ..., timeout, FALSE)`.
3. If the wait fails (timeout / error), call `CancelIoEx` followed by a
**blocking** `GetOverlappedResult(..., bWait = TRUE)` before returning.
Skipping step 3 lets the kernel write into the (now-defunct) stack frame
that held the `OVERLAPPED` / output buffer, which AVs the next caller on the
same thread.
---
## 4. Adapter Creation — `MM_adapter_create`
Registers a software device at runtime via `SwDeviceCreate`. Waits on a
creation-complete callback (signaled by `WaitForSingleObject`). Once
registered, the device interface is enumerated with `SetupDiGetClassDevs` and
opened via `CreateFileW` using `FILE_FLAG_OVERLAPPED | FILE_FLAG_WRITE_THROUGH
| FILE_FLAG_NO_BUFFERING`.
---
## 5. Monitor Create — `MM_device_add`
```c
// Find the first empty slot (value == -1) in the 16-entry array
for (i = 0; i < 16; i++) {
if (context->monitors[i] == -1) break;
}
// Send IOCTL 0x22E004
DeviceIoControl(hDevice, 0x22E004, in_buf, 0x20, &out_index, 4, ...);
// Wait via GetOverlappedResultEx (timeout 5000 ms)
// out_index must be < 16; stored in context->monitors[i]
```
The returned monitor index becomes the **driver index** for that virtual
display. The UID seen later in monitor device-instance paths is
`0x100 + driver_index` (used to map back to a specific virtual monitor when
walking `EnumDisplayDevices` results).
---
## 6. Keep-Alive Watchdog — `MM_keepalive_thread`
Spawned by `MM_connect`. Loops continuously sending `IOCTL_UPDATE` at
~200 ms cadence:
```c
while (*alive_flag) {
Sleep(100); // 100 ms granularity
QueryPerformanceCounter(&now);
QueryPerformanceFrequency(&freq);
elapsed_ms = (now - last) / (freq / 1000.0);
if (elapsed_ms > 200.0) {
MM_update_keepalive(context); // IOCTL 0x22A00C
last = now;
}
}
```
**Critical behavior.** If pings stop, the driver removes **all** virtual
monitors after roughly 1 second. This is its built-in watchdog for cleaning
up after host processes that crashed without explicit disconnect. Any
re-implementation must keep up the cadence (a dedicated thread or 100 ms
timer is the standard pattern).
---
## 7. Display Enumeration — `MM_enumerate_displays`
Walks all displays using the Windows configuration-manager APIs:
1. `EnumDisplayDevicesW(NULL, i, ...)` — outer loop over adapters.
2. `EnumDisplayDevicesW(adapter, j, EDD_GET_DEVICE_INTERFACE_NAME)` — inner loop over monitors on that adapter.
3. `CM_Get_Device_Interface_PropertyW` → device node.
4. `CM_Locate_DevNodeW` + `CM_Get_DevNode_PropertyW` → hardware IDs.
Each entry is **836 bytes (209 DWORDs)**:
```
+0x004 adapter device ID (128 wchars)
+0x108 display name (32 wchars)
+0x148 monitor device ID (128 wchars)
+0x248 interface path (128 wchars)
Flags at DWORD offsets 4/5/6/7:
[4] = active, [5] = primary, [6] = removable, [7] = is-parsec
```
The `is-parsec` flag is determined by matching the hardware ID against
`Root\Parsec\VDA` (with case-insensitive substring search).
> **Note for RDP / cloud-server hosts:** `EnumDisplayDevicesW` is bound to the
> calling thread's window station — it cannot see console-session displays
> from inside an RDP session. A supplemental `SetupDi` enumeration of the
> MONITOR class (GUID `{4d36e96e-e325-11ce-bfc1-08002be10318}`) walks the
> kernel device tree directly and is session-independent.
---
## 8. Custom Resolutions
Up to **5 custom resolutions** can be added at slot indices 04. The driver
reads them at adapter init from:
```
HKLM\SOFTWARE\Parsec\vdd\<slot>
w (REG_DWORD) width
h (REG_DWORD) height
hz (REG_DWORD) refresh rate
```
Written via:
```c
RegCreateKeyExW(hkey, "SOFTWARE\\Parsec\\vdd\\<slot>", ..., KEY_READ | KEY_WRITE, ...);
RegSetValueExW(hkey, "w", REG_DWORD, &width, 4);
RegSetValueExW(hkey, "h", REG_DWORD, &height, 4);
RegSetValueExW(hkey, "hz", REG_DWORD, &refrate, 4);
```
To raise the 5-slot limit you'd have to patch the driver DLL itself
(`mm.dll`) — 5 is fixed in the driver-side parsing.
---
## 9. Privacy Mode — `host_vdd_privacy_thread`
Optional dedicated thread polling every 30 ms while the host wants to hide
physical displays from a connected client:
- `WTSQuerySessionInformationW` — check active session state.
- `EnumWindows` — detect whether any windows are still on real displays.
- `SetDisplayConfig(flags=0x44)``SDC_TOPOLOGY_INTERNAL`: blank/disable real displays.
- `SetDisplayConfig(flags=0x84)``SDC_TOPOLOGY_EXTEND`: restore real displays on disconnect.
- `MM_query_adapter_status` — poll for VDD adapter health.
When combined with `IDD_DISPLAY` only mode, this gives Parsec's "Privacy
Mode" — physical screens go dark while a virtual display drives the remote
client. **Note:** this conflicts with standalone managers like ParsecDisplay;
running both will fight over the topology. The README documents the
workaround (clear `HKLM\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Connectivity`).
---
## 10. Device Status Codes — `host_vdd_init`
Checks the device at `Root\Parsec\VDA` up to 4 times during startup.
Maps `CM_Get_DevNode_Status` flags to a numeric status:
| Status | Meaning |
|:------:|---------|
| 0 | UNKNOWN |
| 2 | OK |
| 3 | UNKNOWN PROBLEM |
| 4 | DISABLED |
| 5 | DRIVER ERROR |
| 6 | RESTART REQUIRED |
| 7 | DISABLED SERVICE |
| other | NOT INSTALLED |
These are surfaced to the user as informative dialogs in Parsec's host UI
("driver disabled — re-enable in Device Manager", "restart required after
install", etc.).
---
## 11. Adapter Recovery — `host_vdd_adapter_monitor`
A separate monitoring thread that handles driver disable/re-enable cycles
(e.g. the user updates the driver in Device Manager):
- Polls every 100 ms (idle) or 1000 ms (active).
- Re-attempts `MM_adapter_create` up to **3 times** when the device disappears.
- Bails out after **5 faults within 5 seconds** to avoid CPU spin on a
permanently broken driver.
- Adapter is dynamically re-created when the driver returns to a healthy state.
This is what makes the Parsec host survive a driver upgrade without a
restart — and the same pattern is mirrored in ParsecDisplay's
`Vdd.Controller.StatusThread`.
---
## 12. Session Startup Ordering
The order in which a Parsec host initializes display-related machinery at
session start (excluding VUSB / gamepad / microphone code paths, which live
outside this driver):
1. **Custom resolutions**`vdd_register_custom_resolution` × 3 (indices 24).
2. **Virtual displays**`host_vdd_init` opens the device, spawns
keep-alive + privacy threads, adds N monitors.
3. **Resolution clamping** — capped at 8192 × 4320 (configurable via session params).
4. **Screen blanking**`PostMessageW(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, -1)`.
5. **Host video thread** — creates the capture queue + encoding thread.
---
## Function map
Behavioral labels used throughout this document. Names are conventional —
choose any that suit your re-implementation.
| Function | Purpose |
|----------|---------|
| `MM_adapter_create` | `SwDeviceCreate` registration of the IDD adapter. |
| `MM_open_device` | `SetupDiGetClassDevs` + `CreateFileW` to obtain the device handle. |
| `MM_connect` | High-level init: open device, spawn keep-alive thread. |
| `MM_disconnect` | Tear down: remove all monitors, close handle. |
| `MM_device_add` | `IOCTL 0x22E004` — create a virtual monitor. |
| `MM_query_adapter_status` | `IOCTL 0x22E010` — adapter health / version. |
| `MM_update_keepalive` | `IOCTL 0x22A00C` — watchdog ping. |
| `MM_keepalive_thread` | Loop sending keep-alive at ~200 ms cadence. |
| `MM_enumerate_displays` | Walk all displays via `EnumDisplayDevices` + CM APIs. |
| `vdd_register_custom_resolution` | Write a `(w, h, hz)` triple into the registry slot. |
| `host_vdd_init` | Driver-status check + open handle + add initial monitors. |
| `host_vdd_privacy_thread` | Optional: hide physical displays while a session is active. |
| `host_vdd_adapter_monitor` | Survive driver disable/re-enable cycles. |
+16 -4
View File
@@ -42,12 +42,18 @@ Use command `-a` or `add` to add a virtual display.
vdd -a
```
The exit code is the index of added display, you can reuse this index to remove
the display. Less than 0 means error occurred.
Output looks like:
```
Added a virtual display with index 0.
```
The exit code is the driver index of the added display (015); you can reuse
it to remove the display later. A negative exit code means an error occurred.
### Removing virtual display
Use command `-r` or `remove` to remove the last added.
Use command `-r` or `remove` to remove the last-added display.
```sh
vdd -r
@@ -59,12 +65,18 @@ Remove the added virtual display at index `0`.
vdd -r 0
```
To remove all the added, replace the index with `all` or `*`.
To remove all added displays, replace the index with `all` or `*`.
```
vdd -r all
```
`remove all` iterates in **reverse driver-index order** so the Windows 10
Connectivity registry doesn't invent a new (default-mode) configuration for
the surviving subset. Per-display failures are reported as warnings and the
loop continues; the exit code is `0` on full success and `1` if any
individual removal failed.
### Listing added displays
List all added virtual displays.
+27 -14
View File
@@ -1,6 +1,10 @@
# C/C++ API Usage
This document describes how to use the Parsec Virtual Display Driver (VDD) C/C++ API, as defined in `core/parsec-vdd.h`. For full project details, see the [README](../README.md) and [PARSEC_VDD_SPECS.md](./PARSEC_VDD_SPECS.md). For code example, see `core/vdd-demo.cc`.
This document describes the legacy single-header C/C++ API at
[`core/parsec-vdd.h`](../core/parsec-vdd.h). It's intentionally minimal — just
enough to add/remove monitors and keep them alive. For project context, see
the [README](../README.md). For supported display modes, see
[PARSEC_VDD_SPECS.md](./PARSEC_VDD_SPECS.md).
---
@@ -8,7 +12,7 @@ This document describes how to use the Parsec Virtual Display Driver (VDD) C/C++
Parsec VDD enables creation and management of virtual displays on Windows 10+ systems. The C/C++ API allows direct control over the driver, including querying status, adding/removing displays, and updating device state.
- Up to **8 virtual displays** per adapter (default).
- Up to **16 virtual displays** per adapter (the legacy header caps at 8 to avoid plugging lag — adjust if needed).
- Supports high resolutions and refresh rates (see the [specs](./PARSEC_VDD_SPECS.md)).
- Can be used independently of the Parsec app.
@@ -41,6 +45,7 @@ enum DeviceStatus {
```c
DeviceStatus QueryDeviceStatus(const GUID *classGuid, const char *deviceId);
```
- Checks the status of a device by class GUID and hardware ID.
- Returns a `DeviceStatus` value.
@@ -51,6 +56,7 @@ DeviceStatus QueryDeviceStatus(const GUID *classGuid, const char *deviceId);
```c
HANDLE OpenDeviceHandle(const GUID *interfaceGuid);
```
- Opens a handle to the device interface.
- Returns `INVALID_HANDLE_VALUE` or a valid handle.
@@ -59,20 +65,21 @@ HANDLE OpenDeviceHandle(const GUID *interfaceGuid);
```c
void CloseDeviceHandle(HANDLE handle);
```
- Closes a previously opened device handle.
### VDD Core Operations
#### Constants
| Constant | Value | Description |
|---------------------|-----------------------------------------|----------------------------|
| `VDD_DISPLAY_ID` | `"PSCCDD0"` | Display device ID |
| `VDD_DISPLAY_NAME` | `"ParsecVDA"` | Display name |
| `VDD_ADAPTER_GUID` | `{00b41627-04c4-429e-a26e-0265cf50c8fa}`| Adapter GUID |
| `VDD_CLASS_GUID` | `{4d36e968-e325-11ce-bfc1-08002be10318}`| Device class GUID |
| `VDD_HARDWARE_ID` | `"Root\\Parsec\\VDA"` | Hardware ID |
| `VDD_MAX_DISPLAYS` | `8` | Maximum virtual displays |
| Constant | Value | Description |
| ------------------ | ---------------------------------------- | ------------------------ |
| `VDD_DISPLAY_ID` | `"PSCCDD0"` | Display device ID |
| `VDD_DISPLAY_NAME` | `"ParsecVDA"` | Display name |
| `VDD_ADAPTER_GUID` | `{00b41627-04c4-429e-a26e-0265cf50c8fa}` | Adapter GUID |
| `VDD_CLASS_GUID` | `{4d36e968-e325-11ce-bfc1-08002be10318}` | Device class GUID |
| `VDD_HARDWARE_ID` | `"Root\\Parsec\\VDA"` | Hardware ID |
| `VDD_MAX_DISPLAYS` | `8` | Maximum virtual displays |
#### IOCTL Codes
@@ -91,6 +98,7 @@ enum VddCtlCode {
```c
DWORD VddIoControl(HANDLE vdd, VddCtlCode code, const void *data, size_t size);
```
- Sends an IOCTL to the VDD device.
#### Query Driver Version
@@ -98,6 +106,7 @@ DWORD VddIoControl(HANDLE vdd, VddCtlCode code, const void *data, size_t size);
```c
int VddVersion(HANDLE vdd);
```
- Returns the minor version of the VDD driver.
#### Update/Ping VDD
@@ -105,13 +114,15 @@ int VddVersion(HANDLE vdd);
```c
void VddUpdate(HANDLE vdd);
```
- Should be called periodically (<100ms) to keep displays alive.
- **Must** be called every ~100 ms (no longer than ~200 ms apart) to keep displays alive. If pings stop for ~1 second the driver **removes all virtual monitors** — this is its built-in watchdog for crashed hosts. Either run a dedicated thread or schedule a timer.
#### Add Virtual Display
```c
int VddAddDisplay(HANDLE vdd);
```
- Adds a new virtual display.
- Returns the index of the added display.
@@ -120,13 +131,14 @@ int VddAddDisplay(HANDLE vdd);
```c
void VddRemoveDisplay(HANDLE vdd, int index);
```
- Removes the display at the given index.
---
## Example Usage
Check out [core/vdd-demo.cc](/core/vdd-demo.cc).
- Minimal demo using the legacy single-header API: [`core/vdd-demo.cc`](../core/vdd-demo.cc).
---
@@ -136,7 +148,8 @@ See [PARSEC_VDD_SPECS.md](./PARSEC_VDD_SPECS.md) for supported resolutions and r
## Further Reading
- [README.md](../README.md): Project overview, app features, and installation.
- [PARSEC_VDD_SPECS.md](./PARSEC_VDD_SPECS.md): Supported display modes and technical specs.
- [README.md](../README.md): project overview, app features, lifecycle diagram.
- [PARSEC_VDD_SPECS.md](./PARSEC_VDD_SPECS.md): supported display modes and technical specs.
- [PARSEC_VDD_RE.md](../docs/PARSEC_VDD_RE.md): full reverse-engineered IOCTL reference — struct layouts, lifecycle, status codes.
---