diff --git a/README.md b/README.md
index 0596d96..e6fd70c 100644
--- a/README.md
+++ b/README.md
@@ -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.
-
+
## 🚀 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
-
-
+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
+
+
diff --git a/docs/PARSEC_VDD_RE.md b/docs/PARSEC_VDD_RE.md
new file mode 100644
index 0000000..8107002
--- /dev/null
+++ b/docs/PARSEC_VDD_RE.md
@@ -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 (0–15). |
+| `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 0–4. The driver
+reads them at adapter init from:
+
+```
+HKLM\SOFTWARE\Parsec\vdd\
+ w (REG_DWORD) width
+ h (REG_DWORD) height
+ hz (REG_DWORD) refresh rate
+```
+
+Written via:
+
+```c
+RegCreateKeyExW(hkey, "SOFTWARE\\Parsec\\vdd\\", ..., 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 2–4).
+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. |
diff --git a/docs/VDD_CLI_USAGE.md b/docs/VDD_CLI_USAGE.md
index 361c443..965e658 100644
--- a/docs/VDD_CLI_USAGE.md
+++ b/docs/VDD_CLI_USAGE.md
@@ -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 (0–15); 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.
diff --git a/docs/VDD_LIBRARY_USAGE.md b/docs/VDD_LIBRARY_USAGE.md
index 6eb52ed..fdc8e69 100644
--- a/docs/VDD_LIBRARY_USAGE.md
+++ b/docs/VDD_LIBRARY_USAGE.md
@@ -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.
---