// Copyright (c) Microsoft Corporation
#include "Driver.h"
#include "Driver.tmh"
#include "Direct3DDevice.h"
#include "Edid.h"
#include "HelperMethods.h"
#include "IndirectMonitor.h"
#include "SwapChainProcessor.h"
#include "public/properties.h"
#include <memory>
#pragma region EventDeclaration
extern "C" DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD EvtWdfDriverDeviceAdd;
EVT_WDF_DEVICE_D0_ENTRY EvtWdfDeviceD0Entry;
EVT_IDD_CX_ADAPTER_INIT_FINISHED EvtIddCxAdapterInitFinished;
EVT_IDD_CX_ADAPTER_COMMIT_MODES EvtIddCxAdapterCommitModes;
EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION EvtIddCxParseMonitorDescription;
EVT_IDD_CX_MONITOR_GET_DEFAULT_DESCRIPTION_MODES EvtIddCxMonitorGetDefaultModes;
EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES EvtIddCxMonitorQueryModes;
EVT_IDD_CX_MONITOR_ASSIGN_SWAPCHAIN EvtIddCxMonitorAssignSwapChain;
EVT_IDD_CX_MONITOR_UNASSIGN_SWAPCHAIN EvtIddCxMonitorUnassignSwapChain;
struct IndirectDeviceContextWrapper {
display::test::IndirectDeviceContext* pContext;
void Cleanup() {
delete pContext;
pContext = nullptr;
}
};
struct IndirectMonitorContextWrapper {
display::test::IndirectMonitorContext* pContext;
void Cleanup() { pContext = nullptr; }
};
#pragma endregion
#pragma region EventDefinations
// This macro creates the methods for accessing an IndirectDeviceContextWrapper
// as a context for a WDF object
WDF_DECLARE_CONTEXT_TYPE(IndirectDeviceContextWrapper);
WDF_DECLARE_CONTEXT_TYPE(IndirectMonitorContextWrapper);
extern "C" BOOL WINAPI DllMain(_In_ HINSTANCE hInstance,
_In_ UINT dwReason,
_In_opt_ LPVOID lpReserved) {
UNREFERENCED_PARAMETER(hInstance);
UNREFERENCED_PARAMETER(lpReserved);
UNREFERENCED_PARAMETER(dwReason);
return TRUE;
}
_Use_decl_annotations_ extern "C" NTSTATUS DriverEntry(
PDRIVER_OBJECT pDriverObject,
PUNICODE_STRING pRegistryPath) {
WDF_DRIVER_CONFIG Config;
NTSTATUS Status;
WDF_OBJECT_ATTRIBUTES Attributes;
WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
WDF_DRIVER_CONFIG_INIT(&Config, EvtWdfDriverDeviceAdd);
Status = WdfDriverCreate(pDriverObject, pRegistryPath, &Attributes, &Config,
WDF_NO_HANDLE);
if (!NT_SUCCESS(Status)) {
return Status;
}
WPP_INIT_TRACING(pDriverObject, pRegistryPath);
// TODO: Call WPP_CLEANUP somewhere..
return Status;
}
_Use_decl_annotations_ NTSTATUS
EvtWdfDriverDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT pDeviceInit) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "EvtWdfDriverDeviceAdd");
NTSTATUS Status = STATUS_SUCCESS;
WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;
UNREFERENCED_PARAMETER(Driver);
// Register for power callbacks - in this driver only power-on is needed
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);
PnpPowerCallbacks.EvtDeviceD0Entry = EvtWdfDeviceD0Entry;
WdfDeviceInitSetPnpPowerEventCallbacks(pDeviceInit, &PnpPowerCallbacks);
IDD_CX_CLIENT_CONFIG IddConfig;
IDD_CX_CLIENT_CONFIG_INIT(&IddConfig);
// If the driver wishes to handle custom IoDeviceControl requests, it's
// necessary to use this callback since IddCx redirects IoDeviceControl
// requests to an internal queue. This driver does not require this.
// IddConfig.EvtIddCxDeviceIoControl = IoDeviceControl;
IddConfig.EvtIddCxAdapterInitFinished = EvtIddCxAdapterInitFinished;
IddConfig.EvtIddCxParseMonitorDescription = EvtIddCxParseMonitorDescription;
IddConfig.EvtIddCxMonitorGetDefaultDescriptionModes =
EvtIddCxMonitorGetDefaultModes;
IddConfig.EvtIddCxMonitorQueryTargetModes = EvtIddCxMonitorQueryModes;
IddConfig.EvtIddCxAdapterCommitModes = EvtIddCxAdapterCommitModes;
IddConfig.EvtIddCxMonitorAssignSwapChain = EvtIddCxMonitorAssignSwapChain;
IddConfig.EvtIddCxMonitorUnassignSwapChain = EvtIddCxMonitorUnassignSwapChain;
Status = IddCxDeviceInitConfig(pDeviceInit, &IddConfig);
if (!NT_SUCCESS(Status)) {
return Status;
}
WDF_OBJECT_ATTRIBUTES Attr;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper);
Attr.EvtCleanupCallback = [](WDFOBJECT Object) {
// Automatically cleanup the context when the WDF object is about to be
// deleted
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Object);
if (pContext) {
pContext->Cleanup();
}
};
WDFDEVICE Device = nullptr;
Status = WdfDeviceCreate(&pDeviceInit, &Attr, &Device);
if (!NT_SUCCESS(Status)) {
return Status;
}
Status = IddCxDeviceInitialize(Device);
// Create a new device context object and attach it to the WDF device object
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device);
pContext->pContext = new display::test::IndirectDeviceContext(Device);
return Status;
}
_Use_decl_annotations_ NTSTATUS
EvtWdfDeviceD0Entry(WDFDEVICE Device, WDF_POWER_DEVICE_STATE PreviousState) {
UNREFERENCED_PARAMETER(PreviousState);
// This function is called by WDF to start the device in the fully-on power
// state.
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device);
pContext->pContext->InitAdapter();
return STATUS_SUCCESS;
}
#pragma endregion
#pragma region IndirectContext
namespace display::test {
IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice)
: m_WdfDevice(WdfDevice) {
m_Adapter = {};
}
IndirectDeviceContext::~IndirectDeviceContext() {
if (m_hThread.Get()) {
TerminateThread(m_hThread.Get(), 0);
// Wait for the thread to terminate
WaitForSingleObject(m_hThread.Get(), INFINITE);
}
}
void IndirectDeviceContext::InitAdapter() {
// ==============================
// TODO: Update the below diagnostic information in accordance with the target
// hardware. The strings and version numbers are used for telemetry and may be
// displayed to the user in some situations.
//
// This is also where static per-adapter capabilities are determined.
// ==============================
IDDCX_ADAPTER_CAPS AdapterCaps = {};
AdapterCaps.Size = sizeof(AdapterCaps);
// Declare basic feature support for the adapter (required)
AdapterCaps.MaxMonitorsSupported =
static_cast<DWORD>(DriverProperties::kMaxMonitors);
AdapterCaps.EndPointDiagnostics.Size =
sizeof(AdapterCaps.EndPointDiagnostics);
AdapterCaps.EndPointDiagnostics.GammaSupport =
IDDCX_FEATURE_IMPLEMENTATION_NONE;
AdapterCaps.EndPointDiagnostics.TransmissionType =
IDDCX_TRANSMISSION_TYPE_WIRED_OTHER;
// Declare your device strings for telemetry (required)
AdapterCaps.EndPointDiagnostics.pEndPointFriendlyName = L"IDD Virtual Device";
AdapterCaps.EndPointDiagnostics.pEndPointManufacturerName =
L"IDD Virtual Manufacturer";
AdapterCaps.EndPointDiagnostics.pEndPointModelName = L"IDD Virtual Model";
// Declare your hardware and firmware versions (required)
IDDCX_ENDPOINT_VERSION Version = {};
Version.Size = sizeof(Version);
Version.MajorVer = 1;
AdapterCaps.EndPointDiagnostics.pFirmwareVersion = &Version;
AdapterCaps.EndPointDiagnostics.pHardwareVersion = &Version;
// Initialize a WDF context that can store a pointer to the device context
// object
WDF_OBJECT_ATTRIBUTES Attr;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper);
IDARG_IN_ADAPTER_INIT AdapterInit = {};
AdapterInit.WdfDevice = m_WdfDevice;
AdapterInit.pCaps = &AdapterCaps;
AdapterInit.ObjectAttributes = &Attr;
// Start the initialization of the adapter, which will trigger the
// AdapterFinishInit callback later
IDARG_OUT_ADAPTER_INIT AdapterInitOut;
NTSTATUS Status = IddCxAdapterInitAsync(&AdapterInit, &AdapterInitOut);
if (NT_SUCCESS(Status)) {
// Store a reference to the WDF adapter handle
m_Adapter = AdapterInitOut.AdapterObject;
// Store the device context object into the WDF object context
auto* pContext =
WdfObjectGet_IndirectDeviceContextWrapper(AdapterInitOut.AdapterObject);
pContext->pContext = this;
}
}
void IndirectDeviceContext::FinishInit() {
SyncRequestedConfig();
m_hThread.Attach(CreateThread(nullptr, 0, RunThread, this, 0, nullptr));
}
void IndirectDeviceContext::SyncRequestedConfig() {
// Read the properties structure sent from the client code that created
// the software device.
WDF_DEVICE_PROPERTY_DATA propertyRead;
WDF_DEVICE_PROPERTY_DATA_INIT(&propertyRead, &DisplayConfigurationProperty);
propertyRead.Lcid = LOCALE_NEUTRAL;
propertyRead.Flags = PLUGPLAY_PROPERTY_PERSISTENT;
DriverProperties driver_properties;
ULONG requiredSize = 0;
DEVPROPTYPE propType;
NTSTATUS Status = WdfDeviceQueryPropertyEx(
m_WdfDevice, &propertyRead, sizeof(DriverProperties), &driver_properties,
&requiredSize, &propType);
if (!NT_SUCCESS(Status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
"WdfDeviceQueryPropertyEx failed: %!STATUS!", Status);
return;
}
const std::vector<MonitorConfig>& requested_configs =
driver_properties.requested_configs();
std::vector<IndirectMonitor> requested_monitors;
for (const auto& config : requested_configs) {
IndirectMonitor indirect_monitor;
Edid edid(indirect_monitor.pEdidBlock.data());
bool success = edid.GetTimingEntry(0)->SetMode(
config.width(), config.height(), config.v_sync());
edid.SetProductCode(config.product_code());
edid.SetSerialNumber(config.product_code());
if (!success) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "SetMode() unsuccessful");
}
edid.UpdateChecksum();
indirect_monitor.pEdidBlock = edid.getEdidBlock();
indirect_monitor.pConfigList.push_back(config);
indirect_monitor.id = config.product_code();
requested_monitors.push_back(indirect_monitor);
}
// Attach monitors that were added but not attached yet.
for (const auto& indirect_monitor : requested_monitors) {
auto it = std::find_if(
monitors.begin(), monitors.end(),
[&indirect_monitor](const std::unique_ptr<IndirectMonitorContext>& m) {
return m && m->monitor_config().id == indirect_monitor.id;
});
if (it == monitors.end()) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
"New monitor config detected. Attaching monitor. %u",
indirect_monitor.id);
Status = AddMonitor(indirect_monitor);
if (!NT_SUCCESS(Status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
"AttachMonitor failed: %!STATUS!", Status);
}
}
}
// Detach monitors that are no longer in the requested config.
for (auto& monitor : monitors) {
if (monitor &&
std::find_if(requested_monitors.begin(), requested_monitors.end(),
[&monitor](const IndirectMonitor& m) {
return m.id == monitor->monitor_config().id;
}) == requested_monitors.end()) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
"Monitor removed from config. Detaching. %u. %ix%i",
monitor->monitor_config().id,
monitor->monitor_config().pConfigList[0].width(),
monitor->monitor_config().pConfigList[0].height());
Status = monitor->Detach();
if (!NT_SUCCESS(Status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
"Monitor detach failed: %!STATUS!", Status);
}
monitor.reset();
}
}
}
DWORD CALLBACK IndirectDeviceContext::RunThread(LPVOID Argument) {
IndirectDeviceContext* context =
reinterpret_cast<IndirectDeviceContext*>(Argument);
// Continually poll for changes to the requested monitor config.
while (true) {
context->SyncRequestedConfig();
Sleep(200);
}
}
NTSTATUS IndirectDeviceContext::AddMonitor(IndirectMonitor monitor) {
NTSTATUS Status = STATUS_SUCCESS;
auto it = std::find(monitors.begin(), monitors.end(), nullptr);
if (it == monitors.end()) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "All connectors are in use.");
return STATUS_INVALID_PARAMETER;
}
size_t connector_index = it - monitors.begin();
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "Connector index: %llu",
connector_index);
// ==============================
// TODO: In a real driver, the EDID should be retrieved dynamically from a
// connected physical monitor. The EDIDs provided here are purely for
// demonstration. Monitor manufacturers are required to correctly fill in
// physical monitor attributes in order to allow the OS to optimize settings
// like viewing distance and scale factor. Manufacturers should also use a
// unique serial number every single device to ensure the OS can tell the
// monitors apart.
// ==============================
WDF_OBJECT_ATTRIBUTES Attr;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectMonitorContextWrapper);
// In this driver, we report a monitor right away but a real driver
// would do this when a monitor connection event occurs
IDDCX_MONITOR_INFO MonitorInfo = {};
MonitorInfo.Size = sizeof(MonitorInfo);
MonitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
MonitorInfo.ConnectorIndex = static_cast<UINT>(connector_index);
MonitorInfo.MonitorDescription.Size = sizeof(MonitorInfo.MonitorDescription);
MonitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID;
MonitorInfo.MonitorDescription.DataSize = Edid::kBlockSize;
MonitorInfo.MonitorDescription.pData = monitor.pEdidBlock.data();
// Create a container ID
CoCreateGuid(&MonitorInfo.MonitorContainerId);
IDARG_IN_MONITORCREATE MonitorCreate = {};
MonitorCreate.ObjectAttributes = &Attr;
MonitorCreate.pMonitorInfo = &MonitorInfo;
// Create a monitor object with the specified monitor descriptor
IDARG_OUT_MONITORCREATE MonitorCreateOut;
Status = IddCxMonitorCreate(m_Adapter, &MonitorCreate, &MonitorCreateOut);
if (NT_SUCCESS(Status)) {
// Create a new monitor context object and attach it to the Idd monitor
// object
auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(
MonitorCreateOut.MonitorObject);
auto monitor_ctx = std::make_unique<IndirectMonitorContext>(
MonitorCreateOut.MonitorObject, monitor);
pMonitorContextWrapper->pContext = monitor_ctx.get();
// Tell the OS that the monitor has been plugged in
Status = monitor_ctx->Attach();
monitors[connector_index] = std::move(monitor_ctx);
}
return Status;
}
IndirectMonitorContext::IndirectMonitorContext(_In_ IDDCX_MONITOR Monitor,
IndirectMonitor config)
: m_Monitor(Monitor), monitor_config_(std::move(config)) {}
IndirectMonitorContext::~IndirectMonitorContext() {
m_ProcessingThread.reset();
}
void IndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain,
LUID RenderAdapter,
HANDLE NewFrameEvent) {
m_ProcessingThread.reset();
auto Device = std::make_unique<Direct3DDevice>(RenderAdapter);
if (FAILED(Device->Init())) {
// It's important to delete the swap-chain if D3D initialization fails, so
// that the OS knows to generate a new swap-chain and try again.
WdfObjectDelete(SwapChain);
} else {
// Create a new swap-chain processing thread
m_ProcessingThread.reset(
new SwapChainProcessor(SwapChain, std::move(Device), NewFrameEvent));
}
}
void IndirectMonitorContext::UnassignSwapChain() {
// Stop processing the last swap-chain
m_ProcessingThread.reset();
}
NTSTATUS IndirectMonitorContext::Attach() {
IDARG_OUT_MONITORARRIVAL ArrivalOut;
return IddCxMonitorArrival(m_Monitor, &ArrivalOut);
}
NTSTATUS IndirectMonitorContext::Detach() {
return IddCxMonitorDeparture(m_Monitor);
}
} // namespace display::test
#pragma endregion
#pragma region DDI Callbacks
_Use_decl_annotations_ NTSTATUS
EvtIddCxAdapterInitFinished(IDDCX_ADAPTER AdapterObject,
const IDARG_IN_ADAPTER_INIT_FINISHED* pInArgs) {
// This is called when the OS has finished setting up the adapter for use by
// the IddCx driver. It's now possible to report attached monitors.
auto* pDeviceContextWrapper =
WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject);
if (NT_SUCCESS(pInArgs->AdapterInitStatus)) {
pDeviceContextWrapper->pContext->FinishInit();
}
return STATUS_SUCCESS;
}
_Use_decl_annotations_ NTSTATUS
EvtIddCxAdapterCommitModes(IDDCX_ADAPTER AdapterObject,
const IDARG_IN_COMMITMODES* pInArgs) {
UNREFERENCED_PARAMETER(AdapterObject);
UNREFERENCED_PARAMETER(pInArgs);
// Do nothing when modes are picked - the swap-chain is taken
// care of by IddCx
// ==============================
// TODO: In a real driver, this function would be used to reconfigure the
// device to commit the new modes. Loop through pInArgs->pPaths and look for
// IDDCX_PATH_FLAGS_ACTIVE. Any path not active is inactive (e.g. the monitor
// should be turned off).
// ==============================
return STATUS_SUCCESS;
}
_Use_decl_annotations_ NTSTATUS
EvtIddCxParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* pInArgs,
IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs) {
// ==============================
// TODO: In a real driver, this function would be called to generate monitor
// modes for an EDID by parsing it. In this driver, the client's requested
// mode for each virtual display is encoded in the first timing entry of the
// EDID.
// ==============================
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
"Inside ParseMonitorDescription");
pOutArgs->MonitorModeBufferOutputCount =
display::test::IndirectMonitor::kModeListLength;
if (pInArgs->MonitorModeBufferInputCount <
display::test::IndirectMonitor::kModeListLength) {
// Return success if there was no buffer, since the caller was only asking
// for a count of modes
return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL
: STATUS_SUCCESS;
} else {
if (pInArgs->MonitorDescription.DataSize !=
display::test::Edid::kBlockSize) {
return STATUS_INVALID_PARAMETER;
}
display::test::Edid edid(
reinterpret_cast<unsigned char*>(pInArgs->MonitorDescription.pData));
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
"Making the displays requested: %ld, %ld, %ld",
edid.GetTimingEntry(0)->GetWidth(),
edid.GetTimingEntry(0)->GetHeight(),
edid.GetTimingEntry(0)->GetVerticalFrequency());
pInArgs->pMonitorModes[0] = display::test::CreateIddCxMonitorMode(
edid.GetTimingEntry(0)->GetWidth(), edid.GetTimingEntry(0)->GetHeight(),
edid.GetTimingEntry(0)->GetVerticalFrequency(),
IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR);
pOutArgs->PreferredMonitorModeIdx = 0; // Always prefer the first mode.
return STATUS_SUCCESS;
}
}
_Use_decl_annotations_ NTSTATUS EvtIddCxMonitorGetDefaultModes(
IDDCX_MONITOR MonitorObject,
const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* pInArgs,
IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* pOutArgs) {
// ==============================
// TODO: In a real driver, this function would be called to generate monitor
// modes for a monitor with no EDID. Drivers should report modes that are
// guaranteed to be supported by the transport protocol and by nearly all
// monitors (such 640x480, 800x600, or 1024x768). If the driver has access to
// monitor modes from a descriptor other than an EDID, those modes would also
// be reported here.
// ==============================
// TODO: Remove or simplify this code, if it is not needed
auto* pMonitorContextWrapper =
WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject);
auto& monitor_config_list =
pMonitorContextWrapper->pContext->monitor_config().pConfigList;
pOutArgs->DefaultMonitorModeBufferOutputCount =
static_cast<UINT>(monitor_config_list.size());
if (pInArgs->DefaultMonitorModeBufferInputCount != 0) {
for (DWORD i = 0;
i < std::min(pInArgs->DefaultMonitorModeBufferInputCount,
pOutArgs->DefaultMonitorModeBufferOutputCount);
i++) {
const display::test::MonitorConfig config = monitor_config_list[i];
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
"Making the default modes: %hu, %hu, %hu", config.width(),
config.height(), config.v_sync());
pInArgs->pDefaultMonitorModes[i] = display::test::CreateIddCxMonitorMode(
config.width(), config.height(), config.v_sync(),
IDDCX_MONITOR_MODE_ORIGIN_DRIVER);
}
pOutArgs->PreferredMonitorModeIdx = 0;
}
return STATUS_SUCCESS;
}
_Use_decl_annotations_ NTSTATUS
EvtIddCxMonitorQueryModes(IDDCX_MONITOR MonitorObject,
const IDARG_IN_QUERYTARGETMODES* pInArgs,
IDARG_OUT_QUERYTARGETMODES* pOutArgs) {
UNREFERENCED_PARAMETER(MonitorObject);
std::vector<IDDCX_TARGET_MODE> TargetModes;
// Create a set of modes supported for frame processing and scan-out. These
// are typically not based on the monitor's descriptor and instead are based
// on the static processing capability of the device. The OS will report the
// available set of modes for a given output as the intersection of monitor
// modes with target modes.
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "MonitorQueryModes");
auto* pMonitorContextWrapper =
WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject);
for (auto mode :
pMonitorContextWrapper->pContext->monitor_config().pConfigList) {
TargetModes.push_back(display::test::CreateIddCxTargetMode(
mode.width(), mode.height(), mode.v_sync()));
}
pOutArgs->TargetModeBufferOutputCount = static_cast<UINT>(TargetModes.size());
if (pInArgs->TargetModeBufferInputCount >= TargetModes.size()) {
copy(TargetModes.begin(), TargetModes.end(), pInArgs->pTargetModes);
}
return STATUS_SUCCESS;
}
_Use_decl_annotations_ NTSTATUS
EvtIddCxMonitorAssignSwapChain(IDDCX_MONITOR MonitorObject,
const IDARG_IN_SETSWAPCHAIN* pInArgs) {
auto* pMonitorContextWrapper =
WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject);
pMonitorContextWrapper->pContext->AssignSwapChain(
pInArgs->hSwapChain, pInArgs->RenderAdapterLuid,
pInArgs->hNextSurfaceAvailable);
return STATUS_SUCCESS;
}
_Use_decl_annotations_ NTSTATUS
EvtIddCxMonitorUnassignSwapChain(IDDCX_MONITOR MonitorObject) {
auto* pMonitorContextWrapper =
WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject);
pMonitorContextWrapper->pContext->UnassignSwapChain();
return STATUS_SUCCESS;
}
#pragma endregion