// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "Application.h" #include Application *Application::_application = nullptr; // Main window procedure LRESULT CALLBACK Application::WindowProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam) { LRESULT result = 0; switch (msg) { case WM_POINTERUP: { result = _application->OnPointerUp(lparam); break; } case WM_CLOSE: { result = _application->OnClose(); break; } case WM_DESTROY: { result = _application->OnDestroy(); break; } default: { result = DefWindowProc(hWnd, msg, wparam, lparam); break; } } return result; } // Provides the entry point to the application and defines client size Application::Application(HINSTANCE hInstance) : _hInstance(hInstance), _hWnd(NULL), _hWndClientWidth(512), _hWndClientHeight(512) { _application = this; } Application::~Application() { _application = nullptr; } // Runs the application int Application::Run() { int result = 0; if (SUCCEEDED(BeforeEnteringMessageLoop())) { result = EnterMessageLoop(); } AfterLeavingMessageLoop(); return result; } // Initialization for the application window, devices and // DComp resources before entering the message loop HRESULT Application::BeforeEnteringMessageLoop() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { hr = CreateMainWindow(); } if (SUCCEEDED(hr)) { hr = CreateD3DDevice(); } if (SUCCEEDED(hr)) { hr = CreateD2DDevice(); } if (SUCCEEDED(hr)) { hr = CreateDWriteFactory(); } if (SUCCEEDED(hr)) { hr = CreateDCompDevice(); } if (SUCCEEDED(hr)) { hr = CreateDCompVisualTree(); } if (SUCCEEDED(hr)) { hr = ShowMainWindow(); } if (SUCCEEDED(hr)) { hr = EnableMouseAsPointerDevice(); } return hr; } // Message loop int Application::EnterMessageLoop() { MSG message = { 0 }; while (GetMessage(&message, NULL, 0, 0)) { TranslateMessage(&message); DispatchMessage(&message); } return static_cast(message.wParam); } // Destroys the application window, devices and DComp resources void Application::AfterLeavingMessageLoop() { DestroyDCompVisualTree(); DestroyDCompDevice(); DestroyDWriteFactory(); DestroyD2DDevice(); DestroyD3DDevice(); DestroyMainWindow(); } // Creates the application window HRESULT Application::CreateMainWindow() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { WNDCLASSEX wcex = { 0 }; wcex.cbSize = sizeof (wcex); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = Application::WindowProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = _hInstance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = static_cast(GetStockObject(WHITE_BRUSH)); wcex.lpszMenuName = NULL; wcex.lpszClassName = L"MainWindowClass"; wcex.hIconSm = NULL; hr = (RegisterClassEx(&wcex) == 0) ? E_FAIL : S_OK; ShowMessageBoxIfFailed(hr, L"RegisterClassEx"); } if (SUCCEEDED(hr)) { RECT windowRect = { 0, 0, _hWndClientWidth, _hWndClientHeight }; hr = (AdjustWindowRect(&windowRect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE) == FALSE) ? E_FAIL : S_OK; ShowMessageBoxIfFailed(hr, L"AdjustWindowRect"); if (SUCCEEDED(hr)) { _hWnd = CreateWindowExW( 0, L"MainWindowClass", L"DirectComposition", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, _hInstance, nullptr); hr = (_hWnd == NULL) ? E_FAIL : S_OK; ShowMessageBoxIfFailed(hr, L"CreateWindowExW"); } } return hr; } // Shows the application window HRESULT Application::ShowMainWindow() { HRESULT hr = (_hWnd == NULL) ? E_UNEXPECTED : S_OK; if (SUCCEEDED(hr)) { ShowWindow(_hWnd, SW_SHOW); UpdateWindow(_hWnd); } return hr; } // Destroys the application window void Application::DestroyMainWindow() { if (_hWnd != NULL) { DestroyWindow(_hWnd); _hWnd = NULL; } } // Enables mouse pointer device for hit-testing HRESULT Application::EnableMouseAsPointerDevice() { HRESULT hr = S_OK; if (!IsMouseInPointerEnabled()) { hr = (EnableMouseInPointer(TRUE) == FALSE) ? E_FAIL : S_OK; } return hr; } HRESULT Application::CreateD3DDevice() { HRESULT hr = (_d3dDevice != nullptr) ? E_UNEXPECTED : S_OK;; ShowMessageBoxIfFailed(hr, L"(_d3dDevice != nullptr)"); if (SUCCEEDED(hr)) { D3D_DRIVER_TYPE driverTypes [] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP }; 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_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 }; D3D_FEATURE_LEVEL featureLevelSupported; for (int i = 0; i < sizeof(driverTypes) / sizeof(driverTypes[0]); ++i) { Microsoft::WRL::ComPtr d3dDevice; hr = ::D3D11CreateDevice( nullptr, driverTypes[i], NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featureLevels, sizeof(featureLevels) / sizeof(featureLevels[0]), D3D11_SDK_VERSION, &d3dDevice, &featureLevelSupported, nullptr); ShowMessageBoxIfFailed(hr, L"::D3D11CreateDevice"); if (SUCCEEDED(hr)) { _d3dDevice = d3dDevice.Detach(); break; } } } return hr; } void Application::DestroyD3DDevice() { _d3dDevice = nullptr; } HRESULT Application::CreateD2DDevice() { HRESULT hr = ((_d3dDevice == nullptr) || (_d2dDevice != nullptr)) ? E_UNEXPECTED : S_OK; ShowMessageBoxIfFailed(hr, L"((_d3dDevice == nullptr) || (_d2dDevice != nullptr))"); if (SUCCEEDED(hr)) { Microsoft::WRL::ComPtr dxgiDevice; hr = _d3dDevice.As(&dxgiDevice); ShowMessageBoxIfFailed(hr, L"_d3dDevice->QueryInterface"); if (SUCCEEDED(hr)) { hr = ::D2D1CreateDevice(dxgiDevice.Get(), nullptr, &_d2dDevice); ShowMessageBoxIfFailed(hr, L"::D2D1CreateDevice"); } } return hr; } void Application::DestroyD2DDevice() { _d2dDevice = nullptr; } /*----Begin calling DirectComposition APIs----*/ HRESULT Application::CreateDWriteFactory() { HRESULT hr = (_dwriteFactory != nullptr) ? E_UNEXPECTED : S_OK; ShowMessageBoxIfFailed(hr, L"(_dwriteFactory != nullptr)"); if (SUCCEEDED(hr)) { hr = ::DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory) , reinterpret_cast(_dwriteFactory.GetAddressOf())); ShowMessageBoxIfFailed(hr, L"::DWriteCreateFactory"); } return hr; } void Application::DestroyDWriteFactory() { _dwriteFactory = nullptr; } // Initializing DirectComposition device using a D2D device object // Pointing to a D2D device object is required for D2D batching HRESULT Application::CreateDCompDevice() { HRESULT hr = ((_d2dDevice == nullptr) || (_dcompDevice != nullptr)) ? E_UNEXPECTED : S_OK; ShowMessageBoxIfFailed(hr, L"((_d2dDevice == nullptr) || (_dcompDevice != nullptr))"); if (SUCCEEDED(hr)) { hr = ::DCompositionCreateDevice2(_d2dDevice.Get(), __uuidof(IDCompositionDesktopDevice) , reinterpret_cast(_dcompDevice.GetAddressOf())); ShowMessageBoxIfFailed(hr, L"::DCompositionCreateDevice2"); } return hr; } void Application::DestroyDCompDevice() { _dcompDevice = nullptr; } // Calling functions to build DirectComposition visual tree, creating surfaces and setting visual properties HRESULT Application::CreateDCompVisualTree() { HRESULT hr = ((_hWnd == NULL) || (_dcompDevice == nullptr)) ? E_UNEXPECTED : S_OK; if (SUCCEEDED(hr)) { hr = CreateDCompTarget(); } if (SUCCEEDED(hr)) { hr = CreateDCompRootVisual(); } if (SUCCEEDED(hr)) { hr = BindDCompRootVisualToTarget(); } if (SUCCEEDED(hr)) { hr = CreateDCompTextVisual(); } if (SUCCEEDED(hr)) { hr = CreateDCompTextSurface(); } if (SUCCEEDED(hr)) { hr = DrawDCompTextSurface(); } if (SUCCEEDED(hr)) { hr = BindDCompTextSurfaceToVisual(); } if (SUCCEEDED(hr)) { hr = SetOffsetOnDCompTextVisual(); } if (SUCCEEDED(hr)) { hr = AddDCompTextVisualToRootVisual(); } if (SUCCEEDED(hr)) { hr = CreateDCompTileVisuals(); } if (SUCCEEDED(hr)) { hr = CreateDCompTileSurfaces(); } if (SUCCEEDED(hr)) { hr = DrawDCompTileSurfaces(); } if (SUCCEEDED(hr)) { hr = BindDCompTileSurfacesToVisuals(); } if (SUCCEEDED(hr)) { hr = SetOffsetOnDCompTileVisuals(); } if (SUCCEEDED(hr)) { hr = SetBackfaceVisibilityOnDCompTileVisuals(); } if (SUCCEEDED(hr)) { hr = AddDCompTileVisualsToRootVisual(); } if (SUCCEEDED(hr)) { hr = CommitDCompDevice(); } return hr; } void Application::DestroyDCompVisualTree() { DestroyDCompTileSurfaces(); DestroyDCompTileVisuals(); DestroyDCompTextSurface(); DestroyDCompTextVisual(); DestroyDCompRootVisual(); DestroyDCompTarget(); } // Creating DirectComposition Target which is associated to an HWND HRESULT Application::CreateDCompTarget() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { hr = _dcompDevice->CreateTargetForHwnd(_hWnd, TRUE, &_dcompTarget); ShowMessageBoxIfFailed(hr, L"_dcompDevice->CreateTargetForHwnd"); } return hr; } void Application::DestroyDCompTarget() { _dcompTarget = nullptr; } /*----Creating visuals----*/ HRESULT Application::CreateDCompRootVisual() { HRESULT hr = ((_dcompDevice == nullptr) || (_dcompRootVisual != nullptr)) ? E_UNEXPECTED : S_OK; if (SUCCEEDED(hr)) { hr = _dcompDevice->CreateVisual(&_dcompRootVisual); ShowMessageBoxIfFailed(hr, L"_dcompDevice->CreateVisual"); } return hr; } void Application::DestroyDCompRootVisual() { _dcompRootVisual = nullptr; } // Creating visual which contains sample description HRESULT Application::CreateDCompTextVisual() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { hr = _dcompDevice->CreateVisual(&_dcompTextVisual); ShowMessageBoxIfFailed(hr, L"_dcompDevice->CreateVisual"); } return hr; } void Application::DestroyDCompTextVisual() { _dcompTextVisual = nullptr; } // Creating surface for DCompTextVisual HRESULT Application::CreateDCompTextSurface() { HRESULT hr = S_OK; UINT dcompTextSurfaceWidth = 7 * _hWndClientWidth / 8; UINT dcompTextSurfaceHeight = 2 * _hWndClientHeight / 8; if (SUCCEEDED(hr)) { hr = _dcompDevice->CreateSurface(dcompTextSurfaceWidth, dcompTextSurfaceHeight, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ALPHA_MODE_IGNORE, &_dcompTextSurface); ShowMessageBoxIfFailed(hr, L"_dcompDevice->CreateSurface"); } return hr; } void Application::DestroyDCompTextSurface() { _dcompTextSurface = nullptr; } // Creating tile visuals HRESULT Application::CreateDCompTileVisuals() { HRESULT hr = S_OK; for (int i = 0; SUCCEEDED(hr) && i < 4; ++i) { hr = _dcompDevice->CreateVisual(&_dcompTileVisuals[i]); ShowMessageBoxIfFailed(hr, L"_dcompDevice->CreateVisual"); } return hr; } void Application::DestroyDCompTileVisuals() { for (int i = 0; i < 4; ++i) { _dcompTileVisuals[i] = nullptr; } } // Creating surfaces for the DComp tile visuals HRESULT Application::CreateDCompTileSurfaces() { HRESULT hr = S_OK; UINT dcompTileSurfaceWidth = 2 * _hWndClientWidth / 8; UINT dcompTileSurfaceHeight = 2 * _hWndClientHeight / 8; for (int i = 0; SUCCEEDED(hr) && i < 4; ++i) { hr = _dcompDevice->CreateSurface(dcompTileSurfaceWidth, dcompTileSurfaceHeight, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ALPHA_MODE_IGNORE, &_dcompTileSurfaces[i]); ShowMessageBoxIfFailed(hr, L"dcompTileSurfaceFactory->CreateSurface"); } return hr; } void Application::DestroyDCompTileSurfaces() { for (int i = 0; i < 4; ++i) { _dcompTileSurfaces[i] = nullptr; } } /*----End of creating visuals----*/ /*----Creating surfaces----*/ // Defining text properties and drawing text into the DComp text surface HRESULT Application::DrawDCompTextSurface() { HRESULT hr = S_OK; UINT dcompTextSurfaceWidth = 7 * _hWndClientWidth / 8; UINT dcompTextSurfaceHeight = 2 * _hWndClientHeight / 8; if (SUCCEEDED(hr)) { Microsoft::WRL::ComPtr d2dDeviceContext; POINT updateOffset = { 0 }; hr = _dcompTextSurface->BeginDraw(nullptr, __uuidof(ID2D1DeviceContext) , reinterpret_cast(d2dDeviceContext.GetAddressOf()), &updateOffset); ShowMessageBoxIfFailed(hr, L"_dcompTextSurface->BeginDraw"); if (SUCCEEDED(hr)) { Microsoft::WRL::ComPtr d2dBackgroundBrush; if (SUCCEEDED(hr)) { hr = d2dDeviceContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &d2dBackgroundBrush); ShowMessageBoxIfFailed(hr, L"d2dDeviceContext->CreateSolidColorBrush"); } Microsoft::WRL::ComPtr d2dForegroundBrush; if (SUCCEEDED(hr)) { hr = d2dDeviceContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &d2dForegroundBrush); ShowMessageBoxIfFailed(hr, L"d2dDeviceContext->CreateSolidColorBrush"); } Microsoft::WRL::ComPtr dwriteTextFormat; if (SUCCEEDED(hr)) { hr = _dwriteFactory->CreateTextFormat(L"Verdana", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &dwriteTextFormat); ShowMessageBoxIfFailed(hr, L"dwriteFactory->CreateTextFormat"); } if (SUCCEEDED(hr)) { hr = dwriteTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_JUSTIFIED); ShowMessageBoxIfFailed(hr, L"dwriteTextFormat->SetTextAlignment"); } if (SUCCEEDED(hr)) { hr = dwriteTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR); ShowMessageBoxIfFailed(hr, L"dwriteTextFormat->SetParagraphAlignment"); } if (SUCCEEDED(hr)) { const wchar_t dcompTileSurfaceText [] = L"This sample demonstrates how to use DirectComposition to apply backface visibility and utilize performance optmization feature known as D2D Batching. Tiles 2 & 3 show backface visible while 1 & 4 show the backface hidden. \n\n" L"Step 1. Tap or click any tile below \n" L"Step 2. Reset by selecting background \n"; D2D1_RECT_F dcompTextSurfaceRect = D2D1::RectF( updateOffset.x + 0.0f * dcompTextSurfaceWidth, updateOffset.y + 0.0f * dcompTextSurfaceHeight, updateOffset.x + 1.0f * dcompTextSurfaceWidth, updateOffset.y + 1.0f * dcompTextSurfaceHeight ); d2dDeviceContext->FillRectangle( dcompTextSurfaceRect, d2dBackgroundBrush.Get()); d2dDeviceContext->DrawText( dcompTileSurfaceText, wcslen(dcompTileSurfaceText), dwriteTextFormat.Get(), &dcompTextSurfaceRect, d2dForegroundBrush.Get()); ShowMessageBoxIfFailed(hr, L"d2dDeviceContext->EndDraw"); } } if (SUCCEEDED(hr)) { hr = _dcompTextSurface->EndDraw(); ShowMessageBoxIfFailed(hr, L"_dcompTextSurface->EndDraw"); } } return hr; } // Drawing tile content into tile surfaces using D2D batching HRESULT Application::DrawDCompTileSurfaces() { HRESULT hr = S_OK; // Defining tile colors D2D1_COLOR_F dcompTileSurfaceBackgroundColors [] = { D2D1::ColorF(D2D1::ColorF::LightBlue), D2D1::ColorF(D2D1::ColorF::BlueViolet), D2D1::ColorF(D2D1::ColorF::Blue), D2D1::ColorF(D2D1::ColorF::Cyan) }; D2D1_COLOR_F dcompTileSurfaceForegroundColors [] = { D2D1::ColorF(D2D1::ColorF::Black), D2D1::ColorF(D2D1::ColorF::Black), D2D1::ColorF(D2D1::ColorF::Black), D2D1::ColorF(D2D1::ColorF::Black), }; // Defining tile content const wchar_t *dcompTileSurfaceTexts [] = { L"1", L"2", L"3", L"4" }; UINT dcompTileSurfaceWidth = 2 * _hWndClientWidth / 8; UINT dcompTileSurfaceHeight = 2 * _hWndClientHeight / 8; // Drawing content for all tile surfaces for (int i = 0; SUCCEEDED(hr) && i < 4; ++i) { Microsoft::WRL::ComPtr d2dDeviceContext; POINT updateOffset = { 0 }; // Begin draw is using a D2D device context for D2D batching hr = _dcompTileSurfaces[i]->BeginDraw(nullptr, __uuidof(ID2D1DeviceContext) , reinterpret_cast(d2dDeviceContext.GetAddressOf()), &updateOffset); ShowMessageBoxIfFailed(hr, L"pDCompSurface->BeginDraw"); if (SUCCEEDED(hr)) { Microsoft::WRL::ComPtr d2dBackgroundBrush; if (SUCCEEDED(hr)) { hr = d2dDeviceContext->CreateSolidColorBrush(dcompTileSurfaceBackgroundColors[i], &d2dBackgroundBrush); ShowMessageBoxIfFailed(hr, L"d2dDeviceContext->CreateSolidColorBrush"); } Microsoft::WRL::ComPtr d2dForegroundBrush; if (SUCCEEDED(hr)) { hr = d2dDeviceContext->CreateSolidColorBrush(dcompTileSurfaceForegroundColors[i], &d2dForegroundBrush); ShowMessageBoxIfFailed(hr, L"d2dDeviceContext->CreateSolidColorBrush"); } Microsoft::WRL::ComPtr dwriteTextFormat; if (SUCCEEDED(hr)) { hr = _dwriteFactory->CreateTextFormat(L"Verdana", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 72.0f, L"en-us", &dwriteTextFormat); ShowMessageBoxIfFailed(hr, L"dwriteFactory->CreateTextFormat"); } if (SUCCEEDED(hr)) { hr = dwriteTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); ShowMessageBoxIfFailed(hr, L"dwriteTextFormat->SetTextAlignment"); } if (SUCCEEDED(hr)) { hr = dwriteTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); ShowMessageBoxIfFailed(hr, L"dwriteTextFormat->SetParagraphAlignment"); } if (SUCCEEDED(hr)) { D2D1_RECT_F dcompTileSurfaceRect = D2D1::RectF( updateOffset.x + 0.0f * dcompTileSurfaceWidth, updateOffset.y + 0.0f * dcompTileSurfaceHeight, updateOffset.x + 1.0f * dcompTileSurfaceWidth, updateOffset.y + 1.0f * dcompTileSurfaceHeight ); d2dDeviceContext->FillRectangle( dcompTileSurfaceRect, d2dBackgroundBrush.Get()); d2dDeviceContext->DrawText( dcompTileSurfaceTexts[i], wcslen(dcompTileSurfaceTexts[i]), dwriteTextFormat.Get(), &dcompTileSurfaceRect, d2dForegroundBrush.Get()); ShowMessageBoxIfFailed(hr, L"d2dDeviceContext->EndDraw"); } } if (SUCCEEDED(hr)) { hr = _dcompTileSurfaces[i]->EndDraw(); ShowMessageBoxIfFailed(hr, L"pDCompSurface->EndDraw"); } } return hr; } /*---- End of creating surfaces ----*/ /*---- Binding surfaces to visuals and root visual to target ----*/ HRESULT Application::BindDCompRootVisualToTarget() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { hr = _dcompTarget->SetRoot(_dcompRootVisual.Get()); ShowMessageBoxIfFailed(hr, L"_dcompTarget->SetRoot"); } return hr; } HRESULT Application::BindDCompTextSurfaceToVisual() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { hr = _dcompTextVisual->SetContent(_dcompTextSurface.Get()); ShowMessageBoxIfFailed(hr, L"_dcompTextVisual->SetContent"); } return hr; } HRESULT Application::BindDCompTileSurfacesToVisuals() { HRESULT hr = S_OK; for (int i = 0; SUCCEEDED(hr) && i < 4; ++i) { hr = _dcompTileVisuals[i]->SetContent(_dcompTileSurfaces[i].Get()); ShowMessageBoxIfFailed(hr, L"_dcompTileVisuals[i]->SetContent"); } return hr; } /*---- End of binding visuals ----*/ /*---- Setting offsets to visuals ----*/ HRESULT Application::SetOffsetOnDCompTextVisual() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { hr = _dcompTextVisual->SetOffsetX(0.5f * _hWndClientWidth / 8.0f); ShowMessageBoxIfFailed(hr, L"_dcompTextVisual->SetOffsetX"); } if (SUCCEEDED(hr)) { hr = _dcompTextVisual->SetOffsetY(0.25f * _hWndClientHeight / 8.0f); ShowMessageBoxIfFailed(hr, L"_dcompTextVisual->SetOffsetY"); } return hr; } HRESULT Application::SetOffsetOnDCompTileVisuals() { HRESULT hr = S_OK; float dcompVisualOffsetX [] = { 1.0f * _hWndClientWidth / 8.0f, 5.0f * _hWndClientWidth / 8.0f, 1.0f * _hWndClientWidth / 8.0f, 5.0f * _hWndClientWidth / 8.0f }; for (int i = 0; SUCCEEDED(hr) && i < 4; ++i) { hr = _dcompTileVisuals[i]->SetOffsetX(dcompVisualOffsetX[i]); ShowMessageBoxIfFailed(hr, L"_dcompTileVisuals[i]->SetOffsetX"); } float dcompVisualOffsetY [] = { 2.0f * _hWndClientWidth / 8.0f, 2.0f * _hWndClientWidth / 8.0f, 5.0f * _hWndClientWidth / 8.0f, 5.0f * _hWndClientWidth / 8.0f }; for (int i = 0; SUCCEEDED(hr) && i < 4; ++i) { hr = _dcompTileVisuals[i]->SetOffsetY(dcompVisualOffsetY[i]); ShowMessageBoxIfFailed(hr, L"_dcompTileVisuals[i]->SetOffsetY"); } return hr; } /*---- End of setting offsets ----*/ // Setting backface visibility visible or hidden to visuals HRESULT Application::SetBackfaceVisibilityOnDCompTileVisuals() { HRESULT hr = S_OK; DCOMPOSITION_BACKFACE_VISIBILITY dcompVisualBackfaceVisibility [] = { DCOMPOSITION_BACKFACE_VISIBILITY_HIDDEN, DCOMPOSITION_BACKFACE_VISIBILITY_VISIBLE, DCOMPOSITION_BACKFACE_VISIBILITY_VISIBLE, DCOMPOSITION_BACKFACE_VISIBILITY_HIDDEN }; for (int i = 0; SUCCEEDED(hr) && i < 4; ++i) { hr = _dcompTileVisuals[i]->SetBackFaceVisibility(dcompVisualBackfaceVisibility[i]); } return hr; } /*---- Begin binding visuals to root visual to create visual tree ----*/ HRESULT Application::AddDCompTextVisualToRootVisual() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { hr = _dcompRootVisual->AddVisual(_dcompTextVisual.Get(), TRUE, nullptr); ShowMessageBoxIfFailed(hr, L"_dcompRootVisual->AddVisual"); } return hr; } HRESULT Application::AddDCompTileVisualsToRootVisual() { HRESULT hr = S_OK; for (int i = 0; SUCCEEDED(hr) && i < 4; ++i) { hr = _dcompRootVisual->AddVisual(_dcompTileVisuals[i].Get(), TRUE, (i == 0) ? _dcompTextVisual.Get() : _dcompTileVisuals[i - 1].Get()); ShowMessageBoxIfFailed(hr, L"_dcompRootVisual->AddVisual"); } return hr; } /*---- End of binding viusals ----*/ // Commiting the DComp device HRESULT Application::CommitDCompDevice() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { hr = _dcompDevice->Commit(); ShowMessageBoxIfFailed(hr, L"_dcompDevice->Commit"); } return hr; } // Hit-testing visual LRESULT Application::OnPointerUp(LPARAM lparam) { POINT p = { GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; ::ScreenToClient(_hWnd, &p); int visualIndex = -1; if (HasAnyVisualBeenHit(p.x, p.y, &visualIndex)) { ShowMessageBoxIfFailed(FlipVisual(visualIndex), L"FlipVisual(visualIndex)"); } else { ShowMessageBoxIfFailed(ResetVisuals(), L"ResetVisual"); } return 0; } // Identify which tile visual has been hit-tested bool Application::HasAnyVisualBeenHit(int x, int y, int *pVisualIndex) { bool br = (pVisualIndex != nullptr); int visualIndex = -1; if (br) { int x0[4] = { 1 * _hWndClientWidth / 8, 5 * _hWndClientWidth / 8, 1 * _hWndClientWidth / 8, 5 * _hWndClientWidth / 8, }; int x1[4] = { 3 * _hWndClientWidth / 8, 7 * _hWndClientWidth / 8, 3 * _hWndClientWidth / 8, 7 * _hWndClientWidth / 8, }; int y0[4] = { 2 * _hWndClientWidth / 8, 2 * _hWndClientWidth / 8, 5 * _hWndClientWidth / 8, 5 * _hWndClientWidth / 8, }; int y1[4] = { 4 * _hWndClientWidth / 8, 4 * _hWndClientWidth / 8, 7 * _hWndClientWidth / 8, 7 * _hWndClientWidth / 8, }; for (int i = 0; i < 4; ++i) { if ((x0[i] <= x) && (x <= x1[i]) && (y0[i] <= y) && (y <= y1[i])) { visualIndex = i; break; } } br = (visualIndex != -1); *pVisualIndex = visualIndex; } return br; } // Apply a 3D flip for hit-tested tile visual HRESULT Application::FlipVisual(int visualIndex) { HRESULT hr = S_OK; Microsoft::WRL::ComPtr dcompAngleAnimation; if (SUCCEEDED(hr)) { hr = _dcompDevice->CreateAnimation(&dcompAngleAnimation); } if (SUCCEEDED(hr)) { hr = dcompAngleAnimation->AddCubic(0.0, 0.0f, 180.0f, 0.0f, 0.0f); } if (SUCCEEDED(hr)) { hr = dcompAngleAnimation->End(1.0, 180.0f); } Microsoft::WRL::ComPtr dcompFlipTransform; if (SUCCEEDED(hr)) { hr = _dcompDevice->CreateRotateTransform3D(&dcompFlipTransform); } if (SUCCEEDED(hr)) { hr = dcompFlipTransform->SetCenterX(1.0f * _hWndClientWidth / 8.0f); } if (SUCCEEDED(hr)) { hr = dcompFlipTransform->SetCenterY(1.0f * _hWndClientHeight / 8.0f); } if (SUCCEEDED(hr)) { hr = dcompFlipTransform->SetCenterZ(0.0f); } if (SUCCEEDED(hr)) { hr = dcompFlipTransform->SetAxisX(0.0f); } if (SUCCEEDED(hr)) { hr = dcompFlipTransform->SetAxisY(1.0f); } if (SUCCEEDED(hr)) { hr = dcompFlipTransform->SetAxisZ(0.0f); } if (SUCCEEDED(hr)) { hr = dcompFlipTransform->SetAngle(dcompAngleAnimation.Get()); } if (SUCCEEDED(hr)) { hr = _dcompTileVisuals[visualIndex]->SetEffect(dcompFlipTransform.Get()); } if (SUCCEEDED(hr)) { hr = _dcompDevice->Commit(); } return hr; } // Reset all visuals when a hit-test is detected outside of tile visuals HRESULT Application::ResetVisuals() { HRESULT hr = S_OK; for (int i = 0; SUCCEEDED(hr) && i < 4; ++i) { hr = _dcompTileVisuals[i]->SetEffect(nullptr); } if (SUCCEEDED(hr)) { hr = _dcompDevice->Commit(); } return hr; } LRESULT Application::OnClose() { if (_hWnd != NULL) { DestroyWindow(_hWnd); _hWnd = NULL; } return 0; } LRESULT Application::OnDestroy() { PostQuitMessage(0); return 0; } void Application::ShowMessageBoxIfFailed(HRESULT hr, const wchar_t *pMessage) { if (FAILED(hr)) { ::MessageBox(NULL, pMessage, L"Failed", MB_OK); } }