Skip to content
Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1327 lines (1098 sloc) 43 KB
// Copyright (c) Microsoft Corporation. All rights reserved
#include "stdafx.h"
#include "AppWindow.h"
#include "objbase.h"
#include <directmanipulation.h>
#define WNDCLASSNAME (L"DirectManipulationAppWindow")
#define WNDTITLE (L"Direct Manipulation Sample Application")
#define WINDOW_WIDTH (1024)
#define WINDOW_HEIGHT (768)
#define TEXTBLOCK_WIDTH (375)
#define TEXTBLOCK_HEIGHT (75)
#define OUTER_VIEWPORT_TEXT_MARGIN_X (50.0f)
#define OUTER_VIEWPORT_TEXT_MARGIN_Y (125.0f)
#define OUTER_VIEWPORT_TEXT_WIDTH (300.0f)
#define OUTER_VIEWPORT_TEXT_HEIGHT (50.0f)
#define OUTER_CONTENT_WIDTH (2000)
#define OUTER_CONTENT_HEIGHT (2000)
#define INNER_VIEWPORT_OFFSET (200)
#define BUTTON_OFFSET_X (5.0f)
#define BUTTON_OFFSET_Y (200.0f)
#define BUTTON_BORDER_WIDTH (2.0f)
#define BUTTON_TEXT_PADDING (5.0f)
#define BUTTON_WIDTH (175)
#define BUTTON_HEIGHT (40)
namespace DManipSample
{
CAppWindow::CAppWindow()
{
// Populate outer content rect as it has a static size.
_contentOuterRect.top = 0;
_contentOuterRect.left = 0;
_contentOuterRect.right = OUTER_CONTENT_WIDTH;
_contentOuterRect.bottom = OUTER_CONTENT_HEIGHT;
}
int CAppWindow::ShowAndServiceWindow()
{
HRESULT hr = S_OK;
HINSTANCE hModule = GetModuleHandle(nullptr);
WNDCLASSEX wc;
wc.cbSize = sizeof(wc);
wc.lpszClassName = WNDCLASSNAME;
wc.lpfnWndProc = CAppWindow::s_WndProc;
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.hInstance = hModule;
wc.hIcon = nullptr;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
wc.lpszMenuName = nullptr;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hIconSm = nullptr;
ATOM registeredClass = RegisterClassEx(&wc);
if(!registeredClass)
{
WCHAR error[MAX_PATH] = {0};
hr = StringCchPrintf(error, ARRAYSIZE(error), L"Failed to register class. GetLastError()=%d", GetLastError());
if(SUCCEEDED(hr))
{
MessageBox(nullptr, error, L"Error", MB_OK);
}
return 2;
}
_hWnd = CreateWindow(WNDCLASSNAME,
WNDTITLE,
WS_OVERLAPPEDWINDOW,
0,
0,
WINDOW_WIDTH,
WINDOW_HEIGHT,
nullptr,
nullptr,
hModule,
this);
if(!_hWnd)
{
WCHAR error[MAX_PATH] = {0};
hr = StringCchPrintf(error, ARRAYSIZE(error), L"Could not create window. GetLastError()=%d", GetLastError());
if(SUCCEEDED(hr))
{
MessageBox(nullptr, error, L"Error", MB_OK);
}
return 1;
}
hr = _Initialize();
ShowWindow(_hWnd, SW_SHOW);
UpdateWindow(_hWnd);
RECT windowRect;
GetWindowRect(_hWnd, &windowRect);
if(FAILED(hr))
{
WCHAR error[MAX_PATH] = {0};
hr = StringCchPrintf(error, ARRAYSIZE(error), L"Encountered error while initializing sample. HRESULT=0x%x", hr);
if(SUCCEEDED(hr))
{
MessageBox(nullptr, error, L"Error", MB_OK);
}
return -2;
}
if(SUCCEEDED(hr))
{
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
LRESULT CALLBACK CAppWindow::s_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
// If we are creating the window, set the pointer to the instance of CAppWindow associated with the window as the HWND's user data.
// That way when we get messages besides WM_CREATE we can call the instance's WndProc and reference non-static member variables.
if(message == WM_CREATE)
{
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(static_cast<CAppWindow*>(reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams)));
}
else
{
CAppWindow* appWindow = reinterpret_cast<CAppWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
if(appWindow)
{
result = appWindow->_WndProc(hWnd, message, wParam, lParam);
}
else
{
result = DefWindowProc(hWnd, message, wParam, lParam);
}
}
return result;
}
LRESULT CAppWindow::_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
HRESULT hr = S_OK;
switch (message)
{
case WM_SIZE:
{
hr = _SizeDependentChanges();
}
break;
case UWM_REDRAWSTATUS:
{
hr = _DrawViewportStatusText();
}
break;
case WM_POINTERDOWN:
{
// Determine if the button was hit. If so, change the button state.
UINT contactId = GET_POINTERID_WPARAM(wParam);
BOOL buttonHit = FALSE;
hr = _HitTestRect(contactId, hWnd, FALSE, BUTTON_OFFSET_X, BUTTON_OFFSET_Y, BUTTON_OFFSET_X + BUTTON_WIDTH, BUTTON_OFFSET_Y + BUTTON_HEIGHT, &buttonHit);
if(SUCCEEDED(hr) && buttonHit)
{
hr = _ChangeButtonState(_dcompVisualButtonDown);
}
}
break;
case WM_POINTERUPDATE:
{
//
// If the updated contact is not over the button, check to see if the contact is already being tracked by the outer viewport. If so, ignore it.
//
UINT contactId = GET_POINTERID_WPARAM(wParam);
BOOL buttonHit = FALSE;
BOOL outerViewportHasContact = _OuterViewportHasContact(contactId);
if(!outerViewportHasContact)
{
// Check if the button was hit.
hr = _HitTestRect(contactId, hWnd, FALSE, BUTTON_OFFSET_X, BUTTON_OFFSET_Y, BUTTON_OFFSET_X + BUTTON_WIDTH, BUTTON_OFFSET_Y + BUTTON_HEIGHT, &buttonHit);
if(SUCCEEDED(hr) && !buttonHit)
{
BOOL innerViewportHit = FALSE;
// Check if the inner viewport was hit.
if(SUCCEEDED(hr))
{
hr = _HitTestRect(contactId, hWnd, TRUE, static_cast<float>(_viewportInnerRect.left), static_cast<float>(_viewportInnerRect.top), static_cast<float>(_viewportInnerRect.right), static_cast<float>(_viewportInnerRect.bottom), &innerViewportHit);
}
if(SUCCEEDED(hr))
{
if(innerViewportHit)
{
hr = _viewportInner->SetContact(contactId);
}
if(SUCCEEDED(hr))
{
hr = _viewportOuter->SetContact(contactId);
}
if(SUCCEEDED(hr))
{
// Add to our list of active contacts.
_activeContacts.push_back(contactId);
}
}
}
}
}
break;
case WM_POINTERUP:
{
_activeContacts.remove(GET_POINTERID_WPARAM(wParam));
if(_dcompVisualButtonCurrent == _dcompVisualButtonDown)
{
hr = _ChangeButtonState(_dcompVisualButtonUpClicked);
}
}
break;
case WM_POINTERCAPTURECHANGED:
{
if(_dcompVisualButtonCurrent == _dcompVisualButtonDown)
{
hr = _ChangeButtonState(_dcompVisualButtonUpCapturedByViewport);
}
}
break;
case WM_KEYDOWN:
case WM_MOUSEWHEEL:
{
DWORD targetContact = (message == WM_KEYDOWN) ? DIRECTMANIPULATION_KEYBOARDFOCUS : DIRECTMANIPULATION_MOUSEFOCUS;
BOOL fHandled = FALSE;
MSG msg = { hWnd, message, wParam, lParam };
HRESULT setContactHr = _viewportOuter->SetContact(targetContact);
if(SUCCEEDED(setContactHr))
{
hr = _manager->ProcessInput(&msg, &fHandled);
}
else
{
hr = setContactHr;
}
// Call ReleaseContact if SetContact succeeded despite the result of ProcessInput. However,
// only overwrite hr if ProcessInput succeeded. Otherwise leave the HRESULT of ProcessInput
// in place.
if(SUCCEEDED(setContactHr))
{
HRESULT releaseContactHr = _viewportOuter->ReleaseContact(targetContact);
if(SUCCEEDED(hr))
{
hr = releaseContactHr;
}
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
default:
result = DefWindowProc(hWnd, message, wParam, lParam);
}
return result;
}
HRESULT CAppWindow::_HitTestRect(UINT contactId, HWND hWnd, BOOL applyTransform, float left, float top, float right, float bottom, BOOL* innerViewportHit)
{
HRESULT hr = S_OK;
// Translate content rect based on the current output transform of the outer viewport to see if we should call SetContact on the inner (nested) viewport.
POINTER_INFO info = {0};
GetPointerInfo(contactId, &info);
POINTF topLeftFloat = {left, top};
POINTF bottomRightFloat = {right, bottom};
if(applyTransform)
{
// Determine the outer viewport's content transform (since it may have been translated or scaled) so it can be taken into account when doing the hit testing for the
// inner viewport.
float contentTransform[6] = {0};
hr = _contentOuter->GetContentTransform(contentTransform, ARRAYSIZE(contentTransform));
if(SUCCEEDED(hr))
{
// Transform the inner viewport's top left and bottom right by the outer viewport's scale factor.
topLeftFloat.x *= contentTransform[0];
topLeftFloat.y *= contentTransform[0];
bottomRightFloat.x *= contentTransform[0];
bottomRightFloat.y *= contentTransform[0];
// Transform the inner viewport's top left and bottom right by the outer viewport's translate values. If the content transform contained a rotation value, we
// would need to factor the rotation in. The visuals and dmanip transforms in this sample cannot be given rotation values, simplifying the math.
topLeftFloat.x += contentTransform[4];
topLeftFloat.y += contentTransform[5];
bottomRightFloat.x += contentTransform[4];
bottomRightFloat.y += contentTransform[5];
}
}
POINT topLeft = {static_cast<int>(topLeftFloat.x), static_cast<int>(topLeftFloat.y)};
POINT bottomRight = {static_cast<int>(bottomRightFloat.x), static_cast<int>(bottomRightFloat.y)};
ClientToScreen(hWnd, &topLeft);
ClientToScreen(hWnd, &bottomRight);
// Determine if the contact hit the inner viewport.
RECT transformedRect = {topLeft.x, topLeft.y, bottomRight.x, bottomRight.y};
*innerViewportHit = PtInRect(&transformedRect, info.ptPixelLocation);
return hr;
}
HRESULT CAppWindow::_InitializeDevices()
{
HRESULT hr = S_OK;
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_1,
};
hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&_d3dDevice,
nullptr,
nullptr);
if(FAILED(hr))
{
// If the device could not be created with hardware acceleration, attempt to create via WARP.
hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_WARP,
nullptr,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&_d3dDevice,
nullptr,
nullptr);
}
if(SUCCEEDED(hr))
{
hr = _d3dDevice.As(&_dxgiDevice);
}
if(SUCCEEDED(hr))
{
hr = DCompositionCreateDevice2(_dxgiDevice.Get(), IID_PPV_ARGS(&_dcompDevice));
}
if(SUCCEEDED(hr))
{
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_PPV_ARGS(&_d2dFactory));
}
return hr;
}
HRESULT CAppWindow::_InitializeManagerAndViewport()
{
HRESULT hr = S_OK;
//
// Create and activate the instance of IDirectManipulationManager. This instance is used to
// CoCreate other DirectManipulation objects and enable / disable DirectManipulation
// per HWND.
//
if(SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_DirectManipulationManager,
nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&_manager));
}
ComPtr<IDirectManipulationUpdateManager> updateManager;
if(SUCCEEDED(hr))
{
hr = _manager->GetUpdateManager(IID_PPV_ARGS(&updateManager));
}
//
// Create the instance of IDirectManipulationDCompCompositor. This instance will handle transformation of
// the dcomp visuals and provide the instance of IDirectManipulationFrameInfoProvider that will manage
// frame timing.
//
if(SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_DCompManipulationCompositor,
nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&_compositor));
}
if(SUCCEEDED(hr))
{
hr = _compositor->SetUpdateManager(updateManager.Get());
}
ComPtr<IDirectManipulationFrameInfoProvider> frameInfo;
if(SUCCEEDED(hr))
{
hr = _compositor.As(&frameInfo);
}
//
// Create the inner and outer viewports and set their boundaries in the associated HWND.
//
if(SUCCEEDED(hr))
{
hr = _manager->CreateViewport(frameInfo.Get(), _hWnd, IID_PPV_ARGS(&_viewportOuter));
}
if(SUCCEEDED(hr))
{
hr = _manager->CreateViewport(frameInfo.Get(), _hWnd, IID_PPV_ARGS(&_viewportInner));
}
//
// Enable the desired configuration for each viewport.
//
DIRECTMANIPULATION_CONFIGURATION targetConfiguration = DIRECTMANIPULATION_CONFIGURATION_INTERACTION
| DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X
| DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y
| DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA
| DIRECTMANIPULATION_CONFIGURATION_RAILS_X
| DIRECTMANIPULATION_CONFIGURATION_RAILS_Y
| DIRECTMANIPULATION_CONFIGURATION_SCALING
| DIRECTMANIPULATION_CONFIGURATION_SCALING_INERTIA;
if(SUCCEEDED(hr))
{
hr = _viewportOuter->ActivateConfiguration(targetConfiguration);
}
if(SUCCEEDED(hr))
{
hr = _viewportInner->ActivateConfiguration(targetConfiguration);
}
//
// Create instances of CViewportEventHandler for each viewport and associate the instance
// with the corresponding viewport.
//
if(SUCCEEDED(hr))
{
_handlerOuter = Make<CViewportEventHandler>(_hWnd);
if(_handlerOuter == nullptr)
{
hr = E_OUTOFMEMORY;
}
}
if(SUCCEEDED(hr))
{
_handlerInner = Make<CViewportEventHandler>(_hWnd);
if(_handlerInner == nullptr)
{
hr = E_OUTOFMEMORY;
}
}
if(SUCCEEDED(hr))
{
hr = _viewportOuter->AddEventHandler(_hWnd, _handlerOuter.Get(), &_viewportOuterHandlerCookie);
}
if(SUCCEEDED(hr))
{
hr = _viewportInner->AddEventHandler(_hWnd, _handlerInner.Get(), &_viewportInnerHandlerCookie);
}
//
// Get the content instances for each viewport so it is available when performing hit testing and
// rendering the associated viewport's DirectComposition content.
//
ComPtr<IDirectManipulationPrimaryContent> primaryContentOuter;
if(SUCCEEDED(hr))
{
hr = _viewportOuter->GetPrimaryContent(IID_PPV_ARGS(&primaryContentOuter));
}
ComPtr<IDirectManipulationPrimaryContent> primaryContentInner;
if(SUCCEEDED(hr))
{
hr = _viewportInner->GetPrimaryContent(IID_PPV_ARGS(&primaryContentInner));
}
if(SUCCEEDED(hr))
{
hr = primaryContentOuter.As(&_contentOuter);
}
if(SUCCEEDED(hr))
{
hr = primaryContentInner.As(&_contentInner);
}
// Limit how far the viewports can be zoomed (scaled).
if(SUCCEEDED(hr))
{
hr = primaryContentOuter->SetZoomBoundaries(1.0f, 5.0f);
}
if(SUCCEEDED(hr))
{
hr = primaryContentInner->SetZoomBoundaries(0.5f, 5.0f);
}
// Activate DirectManipulation on our target window.
if(SUCCEEDED(hr))
{
hr = _manager->Activate(_hWnd);
}
return hr;
}
HRESULT CAppWindow::_InitializeDrawing()
{
HRESULT hr = S_OK;
hr = _dcompDevice->CreateTargetForHwnd(_hWnd, TRUE, &_dcompTarget);
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateVisual(&_dcompVisualOuterParent);
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateVisual(&_dcompVisualText);
}
//
// The DirectComposition visuals will be set up in a hierarchy with parent-child order
// _dcompVisualOuterParent->_dcompVisualOuterChild->_dcompVisualInnerParent->_dcompVisualInnerChild.
// Once IDirectManipulationDCompCompositor::AddVisual is called, DirectManipulation will lie between
// inner parent and child visuals and outer parent and child visuals.
//
if(SUCCEEDED(hr))
{
hr = _dcompTarget->SetRoot(_dcompVisualOuterParent.Get());
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateVisual(&_dcompVisualOuterChild);
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateVisual(&_dcompVisualInnerParent);
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateVisual(&_dcompVisualInnerChild);
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateVisual(&_dcompVisualButtonDown);
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateVisual(&_dcompVisualButtonUpCapturedByViewport);
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateVisual(&_dcompVisualButtonUpClicked);
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateVisual(&_dcompVisualButtonUpDefault);
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualOuterParent->AddVisual(_dcompVisualOuterChild.Get(), FALSE, nullptr);
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualOuterChild->AddVisual(_dcompVisualInnerParent.Get(), FALSE, nullptr);
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualInnerParent->AddVisual(_dcompVisualInnerChild.Get(), FALSE, nullptr);
}
if(SUCCEEDED(hr))
{
_dcompVisualButtonCurrent = _dcompVisualButtonUpDefault;
hr = _dcompVisualOuterParent->AddVisual(_dcompVisualButtonUpDefault.Get(), FALSE, nullptr);
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualOuterParent->AddVisual(_dcompVisualText.Get(), TRUE, _dcompVisualOuterChild.Get());
}
if(SUCCEEDED(hr))
{
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &_writeFactory);
}
if(SUCCEEDED(hr))
{
hr = _writeFactory->CreateTextFormat(L"Arial", nullptr, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &_statusTextFormat);
}
if(SUCCEEDED(hr))
{
hr = _writeFactory->CreateTextFormat(L"Arial", nullptr, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 25.0f, L"en-us", &_viewportTextFormat);
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateSurface(
TEXTBLOCK_WIDTH,
TEXTBLOCK_HEIGHT,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE_IGNORE,
&_textSurface);
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualText->SetContent(_textSurface.Get());
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualText->SetBitmapInterpolationMode(DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
}
//
// Load Circles.png in preparation for painting the content of the inner viewport.
//
ComPtr<IWICImagingFactory> factory;
if(SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory));
}
ComPtr<IWICBitmapDecoder> decoder;
if(SUCCEEDED(hr))
{
hr = factory->CreateDecoderFromFilename(L".\\Circles.png", nullptr, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &decoder);
}
ComPtr<IWICBitmapFrameDecode> source;
if(SUCCEEDED(hr))
{
hr = decoder->GetFrame(0, &source);
}
if(SUCCEEDED(hr))
{
hr = factory->CreateFormatConverter(&_bitmapConverter);
}
if(SUCCEEDED(hr))
{
hr = _bitmapConverter->Initialize(source.Get(), GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nullptr, 0.0f, WICBitmapPaletteTypeMedianCut);
}
UINT imageWidth = 0;
UINT imageHeight = 0;
if(SUCCEEDED(hr))
{
hr = _bitmapConverter->GetSize(&imageWidth, &imageHeight);
}
if(SUCCEEDED(hr))
{
// Populate the inner viewport and client rect based on the image size. The default
// size will be half the image width and height.
_viewportInnerRect.top = INNER_VIEWPORT_OFFSET;
_viewportInnerRect.left = INNER_VIEWPORT_OFFSET;
_viewportInnerRect.right = INNER_VIEWPORT_OFFSET + (imageWidth / 2);
_viewportInnerRect.bottom = INNER_VIEWPORT_OFFSET + (imageHeight / 2);
_contentInnerRect.top = 0;
_contentInnerRect.left = 0;
_contentInnerRect.right = imageWidth;
_contentInnerRect.bottom = imageHeight;
}
return hr;
}
HRESULT CAppWindow::_DrawOuterPrimaryContent()
{
HRESULT hr = S_OK;
UINT width = _contentOuterRect.right - _contentOuterRect.left;
UINT height = _contentOuterRect.bottom - _contentOuterRect.top;
ComPtr<IDCompositionSurface> outerSurface;
hr = _dcompDevice->CreateSurface(
width,
height,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE_IGNORE,
&outerSurface);
if(SUCCEEDED(hr))
{
hr = _dcompVisualOuterChild->SetContent(outerSurface.Get());
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualOuterChild->SetBitmapInterpolationMode(DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
}
// Surface begin draw
POINT point = {0, 0};
ComPtr<IDXGISurface1> dxSurface;
if(SUCCEEDED(hr))
{
hr = outerSurface->BeginDraw(nullptr, IID_PPV_ARGS(&dxSurface), &point);
}
ComPtr<ID2D1RenderTarget> renderTarget;
if(SUCCEEDED(hr))
{
D2D1_RENDER_TARGET_PROPERTIES targetProperties = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), 96, 96);
hr = _d2dFactory->CreateDxgiSurfaceRenderTarget(dxSurface.Get(), &targetProperties, &renderTarget);
}
// Render target begin draw
if(SUCCEEDED(hr))
{
renderTarget->BeginDraw();
}
ComPtr<ID2D1SolidColorBrush> purpleBrush;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Purple), &purpleBrush);
}
ComPtr<ID2D1SolidColorBrush> grayBrush;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Gray), &grayBrush);
}
if(SUCCEEDED(hr))
{
for(int x = 0; x < SQUARES_PER_SIDE; x++)
{
for(int y = 0; y < SQUARES_PER_SIDE; y++)
{
float fractionX = width / 8.0f;
float fractionY = height / 8.0f;
D2D1_RECT_F fillRect = D2D1::RectF((x * fractionX) + point.x, (y * fractionY) + point.y, ((x + 1) * fractionX) + point.x, ((y + 1) * fractionY) + point.y);
ComPtr<ID2D1SolidColorBrush> brush = nullptr;
if((x + y) % 2 == 1)
{
brush = grayBrush;
}
else
{
brush = purpleBrush;
}
renderTarget->FillRectangle(fillRect, brush.Get());
}
}
}
ComPtr<ID2D1SolidColorBrush> whiteBrush;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &whiteBrush);
}
WCHAR panText[] = L"Pan and Zoom Me!";
D2D1_RECT_F textRect = D2D1::RectF(OUTER_VIEWPORT_TEXT_MARGIN_X, OUTER_VIEWPORT_TEXT_MARGIN_Y, OUTER_VIEWPORT_TEXT_WIDTH + OUTER_VIEWPORT_TEXT_MARGIN_X, OUTER_VIEWPORT_TEXT_HEIGHT + OUTER_VIEWPORT_TEXT_MARGIN_Y);
renderTarget->DrawTextW(panText, ARRAYSIZE(panText), _viewportTextFormat.Get(), textRect, whiteBrush.Get());
// End Draw
if(SUCCEEDED(hr))
{
hr = renderTarget->EndDraw();
}
if(SUCCEEDED(hr))
{
hr = outerSurface->EndDraw();
}
// Associate the DirectComposition content we just rendered with DirectManipulation. This allows DirectManipulation
// to control these surfaces automatically, and off the main UI thread.
if(SUCCEEDED(hr))
{
hr = _compositor->AddContent(_contentOuter.Get(), _dcompDevice.Get(), _dcompVisualOuterParent.Get(), _dcompVisualOuterChild.Get());
}
if(SUCCEEDED(hr))
{
WCHAR buttonText[] = L"I'm a button! Press me!";
hr = _DrawButtonState(_dcompVisualButtonUpDefault, TRUE, buttonText, wcsnlen_s(buttonText, ARRAYSIZE(buttonText)));
}
if(SUCCEEDED(hr))
{
WCHAR buttonText[] = L"I was clicked.";
hr = _DrawButtonState(_dcompVisualButtonUpClicked, TRUE, buttonText, wcsnlen_s(buttonText, ARRAYSIZE(buttonText)));
}
if(SUCCEEDED(hr))
{
WCHAR buttonText[] = L"Input was captured.";
hr = _DrawButtonState(_dcompVisualButtonUpCapturedByViewport, TRUE, buttonText, wcsnlen_s(buttonText, ARRAYSIZE(buttonText)));
}
if(SUCCEEDED(hr))
{
WCHAR buttonText[] = L"Release or drag now.";
hr = _DrawButtonState(_dcompVisualButtonDown, FALSE, buttonText, wcsnlen_s(buttonText, ARRAYSIZE(buttonText)));
}
return hr;
}
HRESULT CAppWindow::_DrawInnerPrimaryContent()
{
HRESULT hr = S_OK;
ComPtr<IDCompositionSurface> innerSurface;
hr = _dcompDevice->CreateSurface(
_contentInnerRect.right - _contentInnerRect.left,
_contentInnerRect.bottom - _contentInnerRect.top,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE_IGNORE,
&innerSurface);
if(SUCCEEDED(hr))
{
hr = _dcompVisualInnerChild->SetContent(innerSurface.Get());
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualInnerChild->SetBitmapInterpolationMode(DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
}
// Surface begin draw
POINT point;
ComPtr<IDXGISurface1> dxSurface;
hr = innerSurface->BeginDraw(nullptr, IID_PPV_ARGS(&dxSurface), &point);
ComPtr<ID2D1RenderTarget> renderTarget;
if(SUCCEEDED(hr))
{
D2D1_RENDER_TARGET_PROPERTIES targetProperties = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), 96, 96);
hr = _d2dFactory->CreateDxgiSurfaceRenderTarget(dxSurface.Get(), &targetProperties, &renderTarget);
}
// Render target begin draw
if(SUCCEEDED(hr))
{
renderTarget->BeginDraw();
}
ComPtr<ID2D1Bitmap> bitmap;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateBitmapFromWicBitmap(_bitmapConverter.Get(), nullptr, &bitmap);
}
ComPtr<ID2D1BitmapBrush> bitmapBrush;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateBitmapBrush(bitmap.Get(), &bitmapBrush);
}
float contentX = static_cast<float>(_contentInnerRect.right - _contentInnerRect.left);
float contentY = static_cast<float>(_contentInnerRect.bottom - _contentInnerRect.top);
D2D1_RECT_F fillRect = D2D1::RectF(0.0f + point.x, 0.0f + point.y, contentX + point.x, contentY + point.y);
renderTarget->DrawBitmap(bitmap.Get(), fillRect);
//End Draw
if(SUCCEEDED(hr))
{
hr = renderTarget->EndDraw();
}
if(SUCCEEDED(hr))
{
hr = innerSurface->EndDraw();
}
// Associate the DirectComposition content we just rendered with DirectManipulation. This allows DirectManipulation
// to control these surfaces automatically, and off the main UI thread.
if(SUCCEEDED(hr))
{
hr = _compositor->AddContent(_contentInner.Get(), _dcompDevice.Get(), _dcompVisualInnerParent.Get(), _dcompVisualInnerChild.Get());
}
return hr;
}
HRESULT CAppWindow::_DrawViewportStatusText()
{
HRESULT hr = S_OK;
WCHAR statusTextInner[MAX_PATH] = {0};
WCHAR statusTextOuter[MAX_PATH] = {0};
WCHAR statusText[MAX_PATH] = {0};
hr = _handlerOuter.Get()->GetViewportStatus(statusTextOuter, ARRAYSIZE(statusTextOuter));
if(SUCCEEDED(hr))
{
hr = _handlerInner.Get()->GetViewportStatus(statusTextInner, ARRAYSIZE(statusTextInner));
}
if(SUCCEEDED(hr))
{
hr = StringCchPrintf(statusText, ARRAYSIZE(statusText), L"Outer viewport status: %s\r\nInner viewport status: %s", statusTextOuter, statusTextInner);
}
// Surface begin draw
POINT point = {0, 0};
ComPtr<IDXGISurface1> dxSurface;
if(SUCCEEDED(hr))
{
hr = _textSurface->BeginDraw(nullptr, IID_PPV_ARGS(&dxSurface), &point);
}
ComPtr<ID2D1RenderTarget> renderTarget;
if(SUCCEEDED(hr))
{
D2D1_RENDER_TARGET_PROPERTIES targetProperties = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), 96, 96);
hr = _d2dFactory->CreateDxgiSurfaceRenderTarget(dxSurface.Get(), &targetProperties, &renderTarget);
}
// Render target begin draw
if(SUCCEEDED(hr))
{
renderTarget->BeginDraw();
}
ComPtr<ID2D1SolidColorBrush> redBrush;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &redBrush);
}
ComPtr<ID2D1SolidColorBrush> blueBrush;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::AliceBlue), &blueBrush);
}
const D2D1_RECT_F fillRect = {0.0f + point.x, 0.0f + point.y, static_cast<float>(TEXTBLOCK_WIDTH) + point.x, static_cast<float>(TEXTBLOCK_HEIGHT) + point.y};
if(SUCCEEDED(hr))
{
renderTarget->FillRectangle(fillRect, blueBrush.Get());
}
if(SUCCEEDED(hr))
{
renderTarget->DrawTextW(statusText, wcsnlen_s(statusText, ARRAYSIZE(statusText)), _statusTextFormat.Get(), &fillRect, redBrush.Get());
}
//End Draw
if(SUCCEEDED(hr))
{
hr = renderTarget->EndDraw();
}
if(SUCCEEDED(hr))
{
hr = _textSurface->EndDraw();
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->Commit();
}
return hr;
}
HRESULT CAppWindow::_DrawButtonState(ComPtr<IDCompositionVisual2> buttonVisual, BOOL isUp, WCHAR* buttonText, size_t buttonTextLength)
{
HRESULT hr = S_OK;
ComPtr<IDCompositionSurface> buttonSurface;
hr = _dcompDevice->CreateSurface(
BUTTON_WIDTH,
BUTTON_HEIGHT,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE_IGNORE,
&buttonSurface);
if(SUCCEEDED(hr))
{
hr = buttonVisual->SetOffsetX(BUTTON_OFFSET_X);
}
if(SUCCEEDED(hr))
{
hr = buttonVisual->SetOffsetY(BUTTON_OFFSET_Y);
}
if(SUCCEEDED(hr))
{
hr = buttonVisual->SetContent(buttonSurface.Get());
}
if(SUCCEEDED(hr))
{
hr = buttonVisual->SetBitmapInterpolationMode(DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
}
// Surface begin draw
POINT point = {0, 0};
ComPtr<IDXGISurface1> dxSurface;
if(SUCCEEDED(hr))
{
hr = buttonSurface->BeginDraw(nullptr, IID_PPV_ARGS(&dxSurface), &point);
}
ComPtr<ID2D1RenderTarget> renderTarget;
if(SUCCEEDED(hr))
{
D2D1_RENDER_TARGET_PROPERTIES targetProperties = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), 96, 96);
hr = _d2dFactory->CreateDxgiSurfaceRenderTarget(dxSurface.Get(), &targetProperties, &renderTarget);
}
// Render target begin draw
if(SUCCEEDED(hr))
{
renderTarget->BeginDraw();
}
ComPtr<ID2D1SolidColorBrush> grayBrush;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Gray), &grayBrush);
}
ComPtr<ID2D1SolidColorBrush> lightGrayBrush;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::LightGray), &lightGrayBrush);
}
if(SUCCEEDED(hr))
{
D2D1_RECT_F fillBorderRect = D2D1::RectF(static_cast<float>(point.x), static_cast<float>(point.y), static_cast<float>(BUTTON_WIDTH + point.x), static_cast<float>(BUTTON_HEIGHT + point.y));
D2D1_RECT_F fillInnerRect = D2D1::RectF(point.x + BUTTON_BORDER_WIDTH, point.y + BUTTON_BORDER_WIDTH, BUTTON_WIDTH + point.x - BUTTON_BORDER_WIDTH, BUTTON_HEIGHT + point.y - BUTTON_BORDER_WIDTH);
if(isUp)
{
renderTarget->FillRectangle(fillBorderRect, grayBrush.Get());
renderTarget->FillRectangle(fillInnerRect, lightGrayBrush.Get());
}
else
{
renderTarget->FillRectangle(fillBorderRect, lightGrayBrush.Get());
renderTarget->FillRectangle(fillInnerRect, grayBrush.Get());
}
}
ComPtr<ID2D1SolidColorBrush> blackBrush;
if(SUCCEEDED(hr))
{
hr = renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &blackBrush);
}
D2D1_RECT_F textRect = D2D1::RectF(point.x + BUTTON_BORDER_WIDTH + BUTTON_TEXT_PADDING, point.y + BUTTON_BORDER_WIDTH + BUTTON_TEXT_PADDING, BUTTON_WIDTH + point.x - BUTTON_BORDER_WIDTH - BUTTON_TEXT_PADDING, + BUTTON_HEIGHT + point.y - BUTTON_BORDER_WIDTH - BUTTON_TEXT_PADDING);
renderTarget->DrawTextW(buttonText, buttonTextLength, _statusTextFormat.Get(), textRect, blackBrush.Get());
// End Draw
if(SUCCEEDED(hr))
{
hr = renderTarget->EndDraw();
}
if(SUCCEEDED(hr))
{
hr = buttonSurface->EndDraw();
}
return hr;
}
HRESULT CAppWindow::_ChangeButtonState(ComPtr<IDCompositionVisual2> buttonState)
{
HRESULT hr = S_OK;
hr = _dcompVisualOuterParent->RemoveVisual(_dcompVisualButtonCurrent.Get());
if(SUCCEEDED(hr))
{
_dcompVisualButtonCurrent = buttonState;
hr = _dcompVisualOuterParent->AddVisual(buttonState.Get(), FALSE, nullptr);
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->Commit();
}
return hr;
}
BOOL CAppWindow::_OuterViewportHasContact(UINT contactId)
{
std::list<UINT>::iterator contact = std::find(_activeContacts.begin(), _activeContacts.end(), contactId);
if(contact != _activeContacts.end())
{
return TRUE;
}
else
{
return FALSE;
}
}
HRESULT CAppWindow::_SizeDependentChanges()
{
HRESULT hr = S_OK;
// Set the outer viewport rect to the size of the client area.
::GetClientRect(_hWnd, &_viewportOuterRect);
if(SUCCEEDED(hr))
{
hr = _viewportOuter->SetViewportRect(&_viewportOuterRect);
}
if(SUCCEEDED(hr))
{
hr = _viewportInner->SetViewportRect(&_viewportInnerRect);
}
//
// Set the content size for each viewport's primary content object.
//
if(SUCCEEDED(hr))
{
hr = _contentOuter->SetContentRect(&_contentOuterRect);
}
if(SUCCEEDED(hr))
{
hr = _contentInner->SetContentRect(&_contentInnerRect);
}
ComPtr<IDCompositionRectangleClip> clipRect;
if(SUCCEEDED(hr))
{
hr = _dcompDevice->CreateRectangleClip(&clipRect);
}
if(SUCCEEDED(hr))
{
clipRect->SetLeft(static_cast<float>(_viewportOuterRect.left));
clipRect->SetTop(static_cast<float>(_viewportOuterRect.top));
clipRect->SetRight(static_cast<float>(_viewportOuterRect.right));
clipRect->SetBottom(static_cast<float>(_viewportOuterRect.bottom));
hr = _dcompVisualOuterParent->SetClip(clipRect.Get());
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualInnerParent->SetOffsetX(static_cast<float>(_viewportInnerRect.left));
}
if(SUCCEEDED(hr))
{
hr = _dcompVisualInnerParent->SetOffsetY(static_cast<float>(_viewportInnerRect.top));
}
if(SUCCEEDED(hr))
{
ComPtr<IDCompositionRectangleClip> clip;
_dcompDevice->CreateRectangleClip(&clip);
hr = clip->SetLeft(0.0f);
if(SUCCEEDED(hr))
{
hr = clip->SetTop(0.0f);
}
if(SUCCEEDED(hr))
{
hr = clip->SetRight(static_cast<float>(_viewportInnerRect.right - _viewportInnerRect.left));
}
if(SUCCEEDED(hr))
{
hr = clip->SetBottom(static_cast<float>(_viewportInnerRect.bottom - _viewportInnerRect.top));
}
hr = _dcompVisualInnerParent->SetClip(clip.Get());
}
//
// Render the content
//
if(SUCCEEDED(hr))
{
hr = _DrawOuterPrimaryContent();
}
if(SUCCEEDED(hr))
{
hr = _DrawInnerPrimaryContent();
}
if(SUCCEEDED(hr))
{
hr = _dcompDevice->Commit();
}
return hr;
}
HRESULT CAppWindow::_Initialize()
{
HRESULT hr = S_OK;
//
// Create the Direct3D, DirectComposition and Direct2D objects, then initialize our drawing code
//
hr = _InitializeDevices();
if(SUCCEEDED(hr))
{
hr = _InitializeManagerAndViewport();
}
if(SUCCEEDED(hr))
{
hr = _InitializeDrawing();
}
if(SUCCEEDED(hr))
{
hr = _viewportOuter->SetChaining(DIRECTMANIPULATION_MOTION_ALL);
}
if(SUCCEEDED(hr))
{
hr = _viewportInner->SetChaining(DIRECTMANIPULATION_MOTION_ALL);
}
if(SUCCEEDED(hr))
{
hr = _viewportOuter->Enable();
}
if(SUCCEEDED(hr))
{
hr = _viewportInner->Enable();
}
return hr;
}
}
You can’t perform that action at this time.