bool SwapChainPanelNativeWindow::registerForSizeChangeEvents()
{
    ComPtr<ISizeChangedEventHandler> sizeChangedHandler;
    ComPtr<IFrameworkElement> frameworkElement;
    HRESULT result = Microsoft::WRL::MakeAndInitialize<SwapChainPanelSizeChangedHandler>(sizeChangedHandler.ReleaseAndGetAddressOf(), this->shared_from_this());

    if (SUCCEEDED(result))
    {
        result = mSwapChainPanel.As(&frameworkElement);
    }

    if (SUCCEEDED(result))
    {
        result = RunOnUIThread(
            [this, frameworkElement, sizeChangedHandler]
            {
                return frameworkElement->add_SizeChanged(sizeChangedHandler.Get(),
                                                         &mSizeChangedEventToken);
            },
            mSwapChainPanelDispatcher);
    }

    if (SUCCEEDED(result))
    {
        return true;
    }

    return false;
}
HRESULT GetSwapChainPanelSize(
    const ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> &swapChainPanel,
    const ComPtr<ICoreDispatcher> &dispatcher,
    SIZE *windowSize)
{
    ComPtr<IUIElement> uiElement;
    Size renderSize = {0, 0};
    HRESULT result = swapChainPanel.As(&uiElement);
    if (SUCCEEDED(result))
    {
        result = RunOnUIThread(
            [uiElement, &renderSize]
            {
                return uiElement->get_RenderSize(&renderSize);
            },
            dispatcher);
    }

    if (SUCCEEDED(result))
    {
        *windowSize = { lround(renderSize.Width), lround(renderSize.Height) };
    }

    return result;
}
HRESULT GetSwapChainPanelSize(
    const ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> &swapChainPanel,
    const ComPtr<ICoreDispatcher> &dispatcher,
    Size *windowSize)
{
    ComPtr<IUIElement> uiElement;
    HRESULT result = swapChainPanel.As(&uiElement);
    if (SUCCEEDED(result))
    {
        result = RunOnUIThread(
            [uiElement, windowSize] { return uiElement->get_RenderSize(windowSize); }, dispatcher);
    }

    return result;
}
void SwapChainPanelNativeWindow::unregisterForSizeChangeEvents()
{
    ComPtr<IFrameworkElement> frameworkElement;
    if (mSwapChainPanel && SUCCEEDED(mSwapChainPanel.As(&frameworkElement)))
    {
        RunOnUIThread(
            [this, frameworkElement]
            {
                return frameworkElement->remove_SizeChanged(mSizeChangedEventToken);
            },
            mSwapChainPanelDispatcher);
    }

    mSizeChangedEventToken.value = 0;
}
HRESULT SwapChainPanelNativeWindow::createSwapChain(ID3D11Device *device,
                                                    IDXGIFactory2 *factory,
                                                    DXGI_FORMAT format,
                                                    unsigned int width,
                                                    unsigned int height,
                                                    bool containsAlpha,
                                                    IDXGISwapChain1 **swapChain)
{
    if (device == nullptr || factory == nullptr || swapChain == nullptr || width == 0 ||
        height == 0)
    {
        return E_INVALIDARG;
    }

    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
    swapChainDesc.Width = width;
    swapChainDesc.Height = height;
    swapChainDesc.Format = format;
    swapChainDesc.Stereo = FALSE;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage =
        DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
    swapChainDesc.BufferCount = 2;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
    swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
    swapChainDesc.AlphaMode =
        containsAlpha ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE;

    *swapChain = nullptr;

    ComPtr<IDXGISwapChain1> newSwapChain;
    ComPtr<ISwapChainPanelNative> swapChainPanelNative;
    Size currentPanelSize = {};

    HRESULT result = factory->CreateSwapChainForComposition(device, &swapChainDesc, nullptr, newSwapChain.ReleaseAndGetAddressOf());

    if (SUCCEEDED(result))
    {
        result = mSwapChainPanel.As(&swapChainPanelNative);
    }

    if (SUCCEEDED(result))
    {
        result = RunOnUIThread(
            [swapChainPanelNative, newSwapChain]
            {
                return swapChainPanelNative->SetSwapChain(newSwapChain.Get());
            },
            mSwapChainPanelDispatcher);
    }

    if (SUCCEEDED(result))
    {
        // The swapchain panel host requires an instance of the swapchain set on the SwapChainPanel
        // to perform the runtime-scale behavior.  This swapchain is cached here because there are
        // no methods for retreiving the currently configured on from ISwapChainPanelNative.
        mSwapChain = newSwapChain;
        result = newSwapChain.CopyTo(swapChain);
    }

    // If the host is responsible for scaling the output of the swapchain, then
    // scale it now before returning an instance to the caller.  This is done by
    // first reading the current size of the swapchain panel, then scaling
    if (SUCCEEDED(result))
    {
        if (mSwapChainSizeSpecified || mSwapChainScaleSpecified)
        {
            result = GetSwapChainPanelSize(mSwapChainPanel, mSwapChainPanelDispatcher,
                                           &currentPanelSize);

            // Scale the swapchain to fit inside the contents of the panel.
            if (SUCCEEDED(result))
            {
                result = scaleSwapChain(currentPanelSize, mClientRect);
            }
        }
    }

    return result;
}