diff --git a/README.md b/README.md index 822ede9c4..5a5fd35ff 100644 --- a/README.md +++ b/README.md @@ -225,10 +225,11 @@ For additional Windows samples, see [Windows on GitHub](http://microsoft.github. Low latency input + Pen haptics Simple inking - Touch keyboard + Touch keyboard Touch keyboard text input diff --git a/Samples/BasicInput/README.md b/Samples/BasicInput/README.md index ddf14fb13..6ba485223 100644 --- a/Samples/BasicInput/README.md +++ b/Samples/BasicInput/README.md @@ -51,6 +51,11 @@ To obtain information about Microsoft Visual Studio and the tools for developing ## Related topics +### Samples + +* [ComplexInk](/Samples/ComplexInk) +* [Pen haptics](/Samples/PenHaptics) + ### Reference [PointerPoint](https://msdn.microsoft.com/library/windows/apps/windows.ui.input.pointerpoint.aspx) diff --git a/Samples/ComplexInk/README.md b/Samples/ComplexInk/README.md index e36db9777..f72ea65a4 100644 --- a/Samples/ComplexInk/README.md +++ b/Samples/ComplexInk/README.md @@ -56,11 +56,14 @@ To obtain information about Windows 10 development, go to the [Windows Dev Cente To obtain information about Microsoft Visual Studio and the tools for developing Windows apps, go to [Visual Studio](http://go.microsoft.com/fwlink/?LinkID=532422) ## Related topics --------------- [Getting started with apps](http://msdn.microsoft.com/library/windows/apps/) -## Reference +### Samples + +[Pen haptics](/Samples/PenHaptics) + +### Reference [Windows.UI.Input.Inking](http://msdn.microsoft.com/library/windows/apps/br208524) diff --git a/Samples/Geolocation/cppwinrt/Geolocation.vcxproj b/Samples/Geolocation/cppwinrt/Geolocation.vcxproj index 5f259f63c..d0be49bbf 100644 --- a/Samples/Geolocation/cppwinrt/Geolocation.vcxproj +++ b/Samples/Geolocation/cppwinrt/Geolocation.vcxproj @@ -14,7 +14,7 @@ true Windows Store 10.0 - 10.0.19041.0 + 10.0.22000.0 $(WindowsTargetPlatformVersion) diff --git a/Samples/Geolocation/cppwinrt/Scenario1_TrackPosition.cpp b/Samples/Geolocation/cppwinrt/Scenario1_TrackPosition.cpp index 50c7716aa..1952d3f4e 100644 --- a/Samples/Geolocation/cppwinrt/Scenario1_TrackPosition.cpp +++ b/Samples/Geolocation/cppwinrt/Scenario1_TrackPosition.cpp @@ -173,12 +173,14 @@ namespace winrt::SDKTemplate::implementation ScenarioOutput_Latitude().Text(L"No data"); ScenarioOutput_Longitude().Text(L"No data"); ScenarioOutput_Accuracy().Text(L"No data"); + ScenarioOutput_IsRemoteSource().Text(L"No data"); } else { ScenarioOutput_Latitude().Text(to_hstring(position.Coordinate().Point().Position().Latitude)); ScenarioOutput_Longitude().Text(to_hstring(position.Coordinate().Point().Position().Longitude)); ScenarioOutput_Accuracy().Text(to_hstring(position.Coordinate().Accuracy())); + ScenarioOutput_IsRemoteSource().Text(to_hstring(position.Coordinate().IsRemoteSource())); } } } diff --git a/Samples/Geolocation/cppwinrt/Scenario2_GetPosition.cpp b/Samples/Geolocation/cppwinrt/Scenario2_GetPosition.cpp index ff0fef887..b5cc3f369 100644 --- a/Samples/Geolocation/cppwinrt/Scenario2_GetPosition.cpp +++ b/Samples/Geolocation/cppwinrt/Scenario2_GetPosition.cpp @@ -118,6 +118,7 @@ namespace winrt::SDKTemplate::implementation ScenarioOutput_Longitude().Text(L"No data"); ScenarioOutput_Accuracy().Text(L"No data"); ScenarioOutput_Source().Text(L"No data"); + ScenarioOutput_IsRemoteSource().Text(L"No data"); ShowSatelliteData(false); } else @@ -126,6 +127,7 @@ namespace winrt::SDKTemplate::implementation ScenarioOutput_Longitude().Text(to_hstring(position.Coordinate().Point().Position().Longitude)); ScenarioOutput_Accuracy().Text(to_hstring(position.Coordinate().Accuracy())); ScenarioOutput_Source().Text(to_hstring(position.Coordinate().PositionSource())); + ScenarioOutput_IsRemoteSource().Text(to_hstring(position.Coordinate().IsRemoteSource())); if (position.Coordinate().PositionSource() == PositionSource::Satellite) { diff --git a/Samples/Geolocation/cppwinrt/Scenario6_GetLastVisit.cpp b/Samples/Geolocation/cppwinrt/Scenario6_GetLastVisit.cpp index 183e3d836..2ffd9ec26 100644 --- a/Samples/Geolocation/cppwinrt/Scenario6_GetLastVisit.cpp +++ b/Samples/Geolocation/cppwinrt/Scenario6_GetLastVisit.cpp @@ -73,6 +73,7 @@ namespace winrt::SDKTemplate::implementation ScenarioOutput_Accuracy().Text(L"No data"); ScenarioOutput_Timestamp().Text(L"No data"); ScenarioOutput_VisitStateChange().Text(L"No data"); + ScenarioOutput_IsRemoteSource().Text(L"No data"); } else { @@ -86,12 +87,14 @@ namespace winrt::SDKTemplate::implementation ScenarioOutput_Latitude().Text(L"No data"); ScenarioOutput_Longitude().Text(L"No data"); ScenarioOutput_Accuracy().Text(L"No data"); + ScenarioOutput_IsRemoteSource().Text(L"No data"); } else { ScenarioOutput_Latitude().Text(to_hstring(visit.Position().Coordinate().Point().Position().Latitude)); ScenarioOutput_Longitude().Text(to_hstring(visit.Position().Coordinate().Point().Position().Longitude)); ScenarioOutput_Accuracy().Text(to_hstring(visit.Position().Coordinate().Accuracy())); + ScenarioOutput_IsRemoteSource().Text(to_hstring(visit.Position().Coordinate().IsRemoteSource())); } } } diff --git a/Samples/Geolocation/cppwinrt/Scenario7_ForegroundVisits.cpp b/Samples/Geolocation/cppwinrt/Scenario7_ForegroundVisits.cpp index 9ab6ff0e4..6701ca870 100644 --- a/Samples/Geolocation/cppwinrt/Scenario7_ForegroundVisits.cpp +++ b/Samples/Geolocation/cppwinrt/Scenario7_ForegroundVisits.cpp @@ -117,12 +117,14 @@ namespace winrt::SDKTemplate::implementation ScenarioOutput_Latitude().Text(L"No data"); ScenarioOutput_Longitude().Text(L"No data"); ScenarioOutput_Accuracy().Text(L"No data"); + ScenarioOutput_IsRemoteSource().Text(L"No data"); } else { ScenarioOutput_Latitude().Text(to_hstring(visit.Position().Coordinate().Point().Position().Latitude)); ScenarioOutput_Longitude().Text(to_hstring(visit.Position().Coordinate().Point().Position().Longitude)); ScenarioOutput_Accuracy().Text(to_hstring(visit.Position().Coordinate().Accuracy())); + ScenarioOutput_IsRemoteSource().Text(to_hstring(visit.Position().Coordinate().IsRemoteSource())); } } diff --git a/Samples/Geolocation/cs/Geolocation.csproj b/Samples/Geolocation/cs/Geolocation.csproj index 8759f826d..767f6dc16 100644 --- a/Samples/Geolocation/cs/Geolocation.csproj +++ b/Samples/Geolocation/cs/Geolocation.csproj @@ -14,8 +14,8 @@ Geolocation en-US UAP - 10.0.19041.0 - 10.0.19041.0 + 10.0.22000.0 + $(TargetPlatformVersion) 14 true 512 diff --git a/Samples/Geolocation/cs/Package.appxmanifest b/Samples/Geolocation/cs/Package.appxmanifest index 3985381b4..097763c0a 100644 --- a/Samples/Geolocation/cs/Package.appxmanifest +++ b/Samples/Geolocation/cs/Package.appxmanifest @@ -8,7 +8,7 @@ Assets\StoreLogo-sdk.png - + diff --git a/Samples/Geolocation/cs/Scenario1_TrackPosition.xaml.cs b/Samples/Geolocation/cs/Scenario1_TrackPosition.xaml.cs index 26e5af79d..490f59ef6 100644 --- a/Samples/Geolocation/cs/Scenario1_TrackPosition.xaml.cs +++ b/Samples/Geolocation/cs/Scenario1_TrackPosition.xaml.cs @@ -197,12 +197,14 @@ private void UpdateLocationData(Geoposition position) ScenarioOutput_Latitude.Text = "No data"; ScenarioOutput_Longitude.Text = "No data"; ScenarioOutput_Accuracy.Text = "No data"; + ScenarioOutput_IsRemoteSource.Text = "No data"; } else { ScenarioOutput_Latitude.Text = position.Coordinate.Point.Position.Latitude.ToString(); ScenarioOutput_Longitude.Text = position.Coordinate.Point.Position.Longitude.ToString(); ScenarioOutput_Accuracy.Text = position.Coordinate.Accuracy.ToString(); + ScenarioOutput_IsRemoteSource.Text = position.Coordinate.IsRemoteSource.ToString(); } } } diff --git a/Samples/Geolocation/cs/Scenario2_GetPosition.xaml.cs b/Samples/Geolocation/cs/Scenario2_GetPosition.xaml.cs index 210e766c9..777ed7fb0 100644 --- a/Samples/Geolocation/cs/Scenario2_GetPosition.xaml.cs +++ b/Samples/Geolocation/cs/Scenario2_GetPosition.xaml.cs @@ -136,6 +136,7 @@ private void UpdateLocationData(Geoposition position) ScenarioOutput_Longitude.Text = "No data"; ScenarioOutput_Accuracy.Text = "No data"; ScenarioOutput_Source.Text = "No data"; + ScenarioOutput_IsRemoteSource.Text = "No data"; ShowSatelliteData(false); } else @@ -144,6 +145,7 @@ private void UpdateLocationData(Geoposition position) ScenarioOutput_Longitude.Text = position.Coordinate.Point.Position.Longitude.ToString(); ScenarioOutput_Accuracy.Text = position.Coordinate.Accuracy.ToString(); ScenarioOutput_Source.Text = position.Coordinate.PositionSource.ToString(); + ScenarioOutput_IsRemoteSource.Text = position.Coordinate.IsRemoteSource.ToString(); if (position.Coordinate.PositionSource == PositionSource.Satellite) { diff --git a/Samples/Geolocation/cs/Scenario6_GetLastVisit.xaml.cs b/Samples/Geolocation/cs/Scenario6_GetLastVisit.xaml.cs index 06af6bd46..a79e7bad9 100644 --- a/Samples/Geolocation/cs/Scenario6_GetLastVisit.xaml.cs +++ b/Samples/Geolocation/cs/Scenario6_GetLastVisit.xaml.cs @@ -95,12 +95,14 @@ private void UpdateLastVisit(Geovisit visit) ScenarioOutput_Latitude.Text = "No data"; ScenarioOutput_Longitude.Text = "No data"; ScenarioOutput_Accuracy.Text = "No data"; + ScenarioOutput_IsRemoteSource.Text = "No data"; } else { ScenarioOutput_Latitude.Text = visit.Position.Coordinate.Point.Position.Latitude.ToString(); ScenarioOutput_Longitude.Text = visit.Position.Coordinate.Point.Position.Longitude.ToString(); ScenarioOutput_Accuracy.Text = visit.Position.Coordinate.Accuracy.ToString(); + ScenarioOutput_IsRemoteSource.Text = visit.Position.Coordinate.IsRemoteSource.ToString(); } } } diff --git a/Samples/Geolocation/cs/Scenario7_ForegroundVisits.xaml.cs b/Samples/Geolocation/cs/Scenario7_ForegroundVisits.xaml.cs index 12a09a2a2..c6ec2d988 100644 --- a/Samples/Geolocation/cs/Scenario7_ForegroundVisits.xaml.cs +++ b/Samples/Geolocation/cs/Scenario7_ForegroundVisits.xaml.cs @@ -137,12 +137,14 @@ private void UpdateVisitData(Geovisit visit) ScenarioOutput_Latitude.Text = "No data"; ScenarioOutput_Longitude.Text = "No data"; ScenarioOutput_Accuracy.Text = "No data"; + ScenarioOutput_IsRemoteSource.Text = "No data"; } else { ScenarioOutput_Latitude.Text = visit.Position.Coordinate.Point.Position.Latitude.ToString(); ScenarioOutput_Longitude.Text = visit.Position.Coordinate.Point.Position.Longitude.ToString(); ScenarioOutput_Accuracy.Text = visit.Position.Coordinate.Accuracy.ToString(); + ScenarioOutput_IsRemoteSource.Text = visit.Position.Coordinate.IsRemoteSource.ToString(); } } } diff --git a/Samples/Geolocation/shared/Scenario1_TrackPosition.xaml b/Samples/Geolocation/shared/Scenario1_TrackPosition.xaml index 8f5d71bbe..25ed9d66b 100644 --- a/Samples/Geolocation/shared/Scenario1_TrackPosition.xaml +++ b/Samples/Geolocation/shared/Scenario1_TrackPosition.xaml @@ -35,15 +35,18 @@ + - - - - - - - - + + + + + + + + + + Application is not able to get location data. Go to diff --git a/Samples/Geolocation/shared/Scenario2_GetPosition.xaml b/Samples/Geolocation/shared/Scenario2_GetPosition.xaml index 3f0945dc2..fac3c3c86 100644 --- a/Samples/Geolocation/shared/Scenario2_GetPosition.xaml +++ b/Samples/Geolocation/shared/Scenario2_GetPosition.xaml @@ -46,19 +46,22 @@ + - - - - - - - - + + + + + + + + + + diff --git a/Samples/Geolocation/shared/Scenario6_GetLastVisit.xaml b/Samples/Geolocation/shared/Scenario6_GetLastVisit.xaml index 0aeab384b..036ab0a8b 100644 --- a/Samples/Geolocation/shared/Scenario6_GetLastVisit.xaml +++ b/Samples/Geolocation/shared/Scenario6_GetLastVisit.xaml @@ -39,17 +39,20 @@ + - - - - - - - - - - + + + + + + + + + + + + Application is not able to get location data. Go to diff --git a/Samples/Geolocation/shared/Scenario7_ForegroundVisits.xaml b/Samples/Geolocation/shared/Scenario7_ForegroundVisits.xaml index c773b4c02..7b04e5187 100644 --- a/Samples/Geolocation/shared/Scenario7_ForegroundVisits.xaml +++ b/Samples/Geolocation/shared/Scenario7_ForegroundVisits.xaml @@ -36,17 +36,20 @@ + - - - - - + + + + + + + Application is not able to get location data. Go to diff --git a/Samples/PenHaptics/README.md b/Samples/PenHaptics/README.md new file mode 100644 index 000000000..aa67180a6 --- /dev/null +++ b/Samples/PenHaptics/README.md @@ -0,0 +1,75 @@ +--- +page_type: sample +languages: +- csharp +products: +- windows +- windows-uwp +urlFragment: PenHaptics +extendedZipContent: +- path: SharedContent + target: SharedContent +- path: LICENSE + target: LICENSE +description: "Shows how to use pen haptics in Universal Windows apps." +--- + + + +# Pen haptics sample + +This sample shows how to use Windows 11 pen haptics API to trigger haptic feedback on a pen that supports haptics. + +Specifically, this sample shows how to: + +- **Get SimpleHapticsController from pen input:** this sample shows how to go from pointer ID to PenDevice and then to SimpleHapticsController. This requires haptics support from both the pen and a compliant machine that supports the particular pen. +- **Check pen haptics capabilities:** SimpleHapticsController has properties for pen hardware capabilities, such as IsIntensitySupported, IsPlayCountSupported, SupportedFeedback, etc. +- **Start and stop haptic feedback:** start and stop feedback using variations of SendHapticFeedback and StopFeedback API +- **Trigger both inking and interaction haptic feedback:** the code shows how to trigger inking feedback for inking scenarios and interaction feedback for user interactions + +**Note** The Windows universal samples require Visual Studio to build and Windows 11 to execute. + +To obtain information about Windows 11 development, go to the [Windows Dev Center](http://go.microsoft.com/fwlink/?LinkID=532421) + +To obtain information about Microsoft Visual Studio and the tools for developing Windows apps, go to [Visual Studio](http://go.microsoft.com/fwlink/?LinkID=532422) + +## Related topics + +### Samples + +[BasicInput](/Samples/BasicInput) + +[ComplexInk](/Samples/ComplexInk) + +### Reference + +[PenDevice](https://docs.microsoft.com/en-us/uwp/api/windows.devices.input.pendevice) + +[SimpleHapticsController](https://docs.microsoft.com/en-us/uwp/api/windows.devices.haptics.simplehapticscontroller) + +[Windows pen and ink interactions](https://docs.microsoft.com/en-us/windows/apps/design/input/pen-and-stylus-interactions) + +## System requirements + +**Client:** Windows 11 + +## Build the sample + +1. If you download the samples ZIP, be sure to unzip the entire archive, not just the folder with the sample you want to build. +2. Start Microsoft Visual Studio and select **File** \> **Open** \> **Project/Solution**. +3. Starting in the folder where you unzipped the samples, go to the Samples subfolder, then the subfolder for this specific sample, then the subfolder for your preferred language (C++, C#, or JavaScript). Double-click the Visual Studio Solution (.sln) file. +4. Press Ctrl+Shift+B, or select **Build** \> **Build Solution**. + +## Run the sample + +The next steps depend on whether you just want to deploy the sample or you want to both deploy and run it. + +### Deploying the sample + +- Select Build > Deploy Solution. + +### Deploying and running the sample + +- To debug the sample and then run it, press F5 or select Debug > Start Debugging. To run the sample without debugging, press Ctrl+F5 or selectDebug > Start Without Debugging. diff --git a/Samples/PenHaptics/cppwinrt/Package.appxmanifest b/Samples/PenHaptics/cppwinrt/Package.appxmanifest new file mode 100644 index 000000000..3040ae525 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Package.appxmanifest @@ -0,0 +1,39 @@ + + + + + + PenHaptics C++/WinRT Sample + Microsoft Corporation + Assets\storelogo-sdk.png + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/PenHaptics/cppwinrt/PenHaptics.sln b/Samples/PenHaptics/cppwinrt/PenHaptics.sln new file mode 100644 index 000000000..62c02cd83 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/PenHaptics.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31624.102 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PenHaptics", "PenHaptics.vcxproj", "{2E3ED2B8-4564-4318-85F1-68BD8F29D560}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Debug|ARM.ActiveCfg = Debug|ARM + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Debug|ARM.Build.0 = Debug|ARM + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Debug|ARM.Deploy.0 = Debug|ARM + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Debug|x64.ActiveCfg = Debug|x64 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Debug|x64.Build.0 = Debug|x64 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Debug|x64.Deploy.0 = Debug|x64 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Debug|x86.ActiveCfg = Debug|Win32 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Debug|x86.Build.0 = Debug|Win32 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Debug|x86.Deploy.0 = Debug|Win32 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Release|ARM.ActiveCfg = Release|ARM + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Release|ARM.Build.0 = Release|ARM + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Release|ARM.Deploy.0 = Release|ARM + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Release|x64.ActiveCfg = Release|x64 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Release|x64.Build.0 = Release|x64 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Release|x64.Deploy.0 = Release|x64 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Release|x86.ActiveCfg = Release|Win32 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Release|x86.Build.0 = Release|Win32 + {2E3ED2B8-4564-4318-85F1-68BD8F29D560}.Release|x86.Deploy.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72760E03-10FF-4586-BB6A-D40E8FF82EF8} + EndGlobalSection +EndGlobal diff --git a/Samples/PenHaptics/cppwinrt/PenHaptics.vcxproj b/Samples/PenHaptics/cppwinrt/PenHaptics.vcxproj new file mode 100644 index 000000000..d8c7f7c9e --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/PenHaptics.vcxproj @@ -0,0 +1,211 @@ + + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), LICENSE))\SharedContent + + + true + {2E3ED2B8-4564-4318-85F1-68BD8F29D560} + PenHaptics + SDKTemplate + en-US + 15.0 + true + Windows Store + 10.0 + 10.0.22000.0 + $(WindowsTargetPlatformVersion) + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + Application + Unicode + + + true + true + + + false + true + false + + + + + + + + $(VC_IncludePath);$(UniversalCRT_IncludePath);$(WindowsSDK_IncludePath);$(SharedContentDir)\cppwinrt + true + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + 4453;28204 + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + + + $(SharedContentDir)\xaml\App.xaml + + + $(SharedContentDir)\xaml\MainPage.xaml + + + + ..\shared\Scenario1_InkCanvasTactileFeedback.xaml + + + ..\shared\Scenario2_SupportedTactileFeedback.xaml + + + ..\shared\Scenario3_InkingFeedback.xaml + + + ..\shared\Scenario4_InteractionFeedback.xaml + + + ..\shared\Scenario5_InkingAndInteractionFeedback.xaml + + + + + + Designer + + + Designer + + + + + + + + Styles\Styles.xaml + + + + + $(SharedContentDir)\xaml\App.xaml + + + $(SharedContentDir)\xaml\MainPage.xaml + + + SampleConfiguration.h + + + ..\shared\Scenario1_InkCanvasTactileFeedback.xaml + + + ..\shared\Scenario2_SupportedTactileFeedback.xaml + + + ..\shared\Scenario3_InkingFeedback.xaml + + + ..\shared\Scenario4_InteractionFeedback.xaml + + + ..\shared\Scenario5_InkingAndInteractionFeedback.xaml + + + Create + pch.h + + + Project.idl + + + + + $(SharedContentDir)\xaml\MainPage.xaml + + + + + + Designer + + + + + Assets\microsoft-sdk.png + + + Assets\smallTile-sdk.png + + + Assets\splash-sdk.png + + + Assets\squareTile-sdk.png + + + Assets\storeLogo-sdk.png + + + Assets\tile-sdk.png + + + Assets\windows-sdk.png + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/Samples/PenHaptics/cppwinrt/PenHaptics.vcxproj.filters b/Samples/PenHaptics/cppwinrt/PenHaptics.vcxproj.filters new file mode 100644 index 000000000..74aafa7d1 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/PenHaptics.vcxproj.filters @@ -0,0 +1,62 @@ + + + + + 4416d50a-7676-4d0a-9b2c-91ff70c6047f + bmp;fbx;gif;jpg;jpeg;tga;tiff;tif;png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + + + + + + + \ No newline at end of file diff --git a/Samples/PenHaptics/cppwinrt/Project.idl b/Samples/PenHaptics/cppwinrt/Project.idl new file mode 100644 index 000000000..dec756859 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Project.idl @@ -0,0 +1,45 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +namespace SDKTemplate +{ + [default_interface] + runtimeclass Scenario1_InkCanvasTactileFeedback : Windows.UI.Xaml.Controls.Page + { + Scenario1_InkCanvasTactileFeedback(); + + Windows.UI.Xaml.Controls.InkCanvas appInkCanvas{ get; }; + } + + [default_interface] + runtimeclass Scenario2_SupportedTactileFeedback : Windows.UI.Xaml.Controls.Page + { + Scenario2_SupportedTactileFeedback(); + } + + [default_interface] + runtimeclass Scenario3_InkingFeedback : Windows.UI.Xaml.Controls.Page + { + Scenario3_InkingFeedback(); + } + + [default_interface] + runtimeclass Scenario4_InteractionFeedback : Windows.UI.Xaml.Controls.Page + { + Scenario4_InteractionFeedback(); + } + + [default_interface] + runtimeclass Scenario5_InkingAndInteractionFeedback : Windows.UI.Xaml.Controls.Page + { + Scenario5_InkingAndInteractionFeedback(); + } +} diff --git a/Samples/PenHaptics/cppwinrt/SampleConfiguration.cpp b/Samples/PenHaptics/cppwinrt/SampleConfiguration.cpp new file mode 100644 index 000000000..ad46c1193 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/SampleConfiguration.cpp @@ -0,0 +1,68 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include +#include "MainPage.h" +#include "SampleConfiguration.h" + +using namespace winrt; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Windows::Devices::Haptics; +using namespace winrt::SDKTemplate; + +hstring implementation::MainPage::FEATURE_NAME() +{ + return L"PenHaptics C++/WinRT Sample"; +} + +IVector implementation::MainPage::scenariosInner = winrt::single_threaded_observable_vector( +{ + Scenario{ L"Ink Canvas Tactile Feedback", xaml_typename() }, + Scenario{ L"Query Tactile Feedback Support", xaml_typename() }, + Scenario{ L"Inking Feedback", xaml_typename() }, + Scenario{ L"Interaction Feedback", xaml_typename() }, + Scenario{ L"Inking and Interaction Feedback", xaml_typename() }, +}); + +std::map const& SDKTemplate::WaveformNamesMap() +{ + static const std::map map + { + { L"BrushContinuous", KnownSimpleHapticsControllerWaveforms::BrushContinuous() }, + { L"BuzzContinuous", KnownSimpleHapticsControllerWaveforms::BuzzContinuous() }, + { L"ChiselMarkerContinuous", KnownSimpleHapticsControllerWaveforms::ChiselMarkerContinuous() }, + { L"Click", KnownSimpleHapticsControllerWaveforms::Click() }, + { L"EraserContinuous", KnownSimpleHapticsControllerWaveforms::EraserContinuous() }, + { L"Error", KnownSimpleHapticsControllerWaveforms::Error() }, + { L"GalaxyPenContinuous", KnownSimpleHapticsControllerWaveforms::GalaxyPenContinuous() }, + { L"Hover", KnownSimpleHapticsControllerWaveforms::Hover() }, + { L"InkContinuous", KnownSimpleHapticsControllerWaveforms::InkContinuous() }, + { L"MarkerContinuous", KnownSimpleHapticsControllerWaveforms::MarkerContinuous() }, + { L"PencilContinuous", KnownSimpleHapticsControllerWaveforms::PencilContinuous() }, + { L"Release", KnownSimpleHapticsControllerWaveforms::Release() }, + { L"RumbleContinuous", KnownSimpleHapticsControllerWaveforms::RumbleContinuous() }, + { L"Success", KnownSimpleHapticsControllerWaveforms::Success() }, + }; + return map; +} + +SimpleHapticsControllerFeedback SDKTemplate::FindSupportedFeedback(SimpleHapticsController const& hapticsController, uint16_t waveform) +{ + for (auto&& feedback : hapticsController.SupportedFeedback()) + { + if (feedback.Waveform() == waveform) + { + return feedback; + } + } + return nullptr; +} diff --git a/Samples/PenHaptics/cppwinrt/SampleConfiguration.h b/Samples/PenHaptics/cppwinrt/SampleConfiguration.h new file mode 100644 index 000000000..f67cbe98d --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/SampleConfiguration.h @@ -0,0 +1,19 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once +#include "pch.h" + +namespace winrt::SDKTemplate +{ + std::map const& WaveformNamesMap(); + Windows::Devices::Haptics::SimpleHapticsControllerFeedback FindSupportedFeedback(Windows::Devices::Haptics::SimpleHapticsController const& hapticsController, uint16_t waveform); +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario1_InkCanvasTactileFeedback.cpp b/Samples/PenHaptics/cppwinrt/Scenario1_InkCanvasTactileFeedback.cpp new file mode 100644 index 000000000..4e229346a --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario1_InkCanvasTactileFeedback.cpp @@ -0,0 +1,32 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "Scenario1_InkCanvasTactileFeedback.h" +#include "Scenario1_InkCanvasTactileFeedback.g.cpp" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::UI::Xaml; + +namespace winrt::SDKTemplate::implementation +{ + Scenario1_InkCanvasTactileFeedback::Scenario1_InkCanvasTactileFeedback() + { + InitializeComponent(); + } + + void Scenario1_InkCanvasTactileFeedback::OnSizeChanged(IInspectable const&, SizeChangedEventArgs const&) + { + outputGrid().Width(RootGrid().ActualWidth()); + outputGrid().Height(RootGrid().ActualHeight() / 2); + } +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario1_InkCanvasTactileFeedback.h b/Samples/PenHaptics/cppwinrt/Scenario1_InkCanvasTactileFeedback.h new file mode 100644 index 000000000..32fbda6b2 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario1_InkCanvasTactileFeedback.h @@ -0,0 +1,32 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario1_InkCanvasTactileFeedback.g.h" +#include "MainPage.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario1_InkCanvasTactileFeedback : Scenario1_InkCanvasTactileFeedbackT + { + Scenario1_InkCanvasTactileFeedback(); + + void OnSizeChanged(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::SizeChangedEventArgs const& e); + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario1_InkCanvasTactileFeedback : Scenario1_InkCanvasTactileFeedbackT + { + }; +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario2_SupportedTactileFeedback.cpp b/Samples/PenHaptics/cppwinrt/Scenario2_SupportedTactileFeedback.cpp new file mode 100644 index 000000000..bc2b253d4 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario2_SupportedTactileFeedback.cpp @@ -0,0 +1,113 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "SampleConfiguration.h" +#include "Scenario2_SupportedTactileFeedback.h" +#include "Scenario2_SupportedTactileFeedback.g.cpp" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Devices::Haptics; +using namespace Windows::Devices::Input; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Input; + +// This sample shows how to query the pen currently providing input to the system to see +// if it supports tactile feedback and, if so, what features it supports +namespace winrt::SDKTemplate::implementation +{ + Scenario2_SupportedTactileFeedback::Scenario2_SupportedTactileFeedback() + { + InitializeComponent(); + } + + // The PointerEnter event will get fired as soon as Pointer input is received. + // This event handler implementation will query the device providing input to see if it's a pen and + // then check to see the pen supports tactile feedback and, if so, what features it supports + void Scenario2_SupportedTactileFeedback::Pointer_Entered(IInspectable const&, PointerRoutedEventArgs const& e) + { + // If the current Pointer device is not a pen, exit + if (e.Pointer().PointerDeviceType() != PointerDeviceType::Pen) + { + supportedFeatures().Text(L""); + supportedFeedback().Text(L""); + return; + } + + // Attempt to retrieve the PenDevice from the current PointerId + penDevice = PenDevice::GetFromPointerId(e.Pointer().PointerId()); + + // If a PenDevice cannot be retrieved based on the PointerId, it does not support + // advanced pen features, such as tactile feedback + if (penDevice == nullptr) + { + statusText().Text(L"Advanced pen features not supported"); + supportedFeatures().Text(L""); + supportedFeedback().Text(L""); + return; + } + + // Check to see if the current PenDevice supports tactile feedback by seeing if it + // has a SimpleHapticsController + hapticsController = penDevice.SimpleHapticsController(); + if (hapticsController == nullptr) + { + statusText().Text(L"This pen does not provide tactile feedback"); + return; + } + + // Check which tactile feedback features are supported + std::wstring message = L"Supported Haptics Features:\n"; + if (hapticsController.IsIntensitySupported()) + { + message.append(L"Intensity\n"); + } + if (hapticsController.IsPlayCountSupported()) + { + message.append(L"PlayCount\n"); + } + if (hapticsController.IsPlayDurationSupported()) + { + message.append(L"PlayDuration\n"); + } + if (hapticsController.IsReplayPauseIntervalSupported()) + { + message.append(L"ReplayPauseInterval\n"); + } + supportedFeatures().Text(message); + + // Check which feedback waveforms are supported + message = L"Supported Feedback:\n"; + for (SimpleHapticsControllerFeedback feedback : hapticsController.SupportedFeedback()) + { + uint16_t waveform = feedback.Waveform(); + for (auto [name, value] : WaveformNamesMap()) + { + if (waveform == value) + { + message.append(name); + message.append(L"\n"); + break; + } + } + } + supportedFeedback().Text(message); + statusText().Text(L""); + } + + // Clear the current penDevice and hapticsController on PointerExit + void Scenario2_SupportedTactileFeedback::Pointer_Exited(IInspectable const&, PointerRoutedEventArgs const&) + { + penDevice = nullptr; + hapticsController = nullptr; + } +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario2_SupportedTactileFeedback.h b/Samples/PenHaptics/cppwinrt/Scenario2_SupportedTactileFeedback.h new file mode 100644 index 000000000..1fbde1a44 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario2_SupportedTactileFeedback.h @@ -0,0 +1,36 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario2_SupportedTactileFeedback.g.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario2_SupportedTactileFeedback : Scenario2_SupportedTactileFeedbackT + { + Scenario2_SupportedTactileFeedback(); + + void Pointer_Entered(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + void Pointer_Exited(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + + private: + Windows::Devices::Haptics::SimpleHapticsController hapticsController{ nullptr }; + Windows::Devices::Input::PenDevice penDevice{ nullptr }; + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario2_SupportedTactileFeedback : Scenario2_SupportedTactileFeedbackT + { + }; +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario3_InkingFeedback.cpp b/Samples/PenHaptics/cppwinrt/Scenario3_InkingFeedback.cpp new file mode 100644 index 000000000..10ae43f2c --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario3_InkingFeedback.cpp @@ -0,0 +1,131 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "SampleConfiguration.h" +#include "Scenario3_InkingFeedback.h" +#include "Scenario3_InkingFeedback.g.cpp" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Devices::Haptics; +using namespace Windows::Devices::Input; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Input; + + +// This sample shows how to play inking feedback tactile signals, how to +// adjust the intensity of the tactile signal, and how to fall back to a default +// waveform if the desired waveform is not supported by the current PenDevice +namespace winrt::SDKTemplate::implementation +{ + Scenario3_InkingFeedback::Scenario3_InkingFeedback() + { + InitializeComponent(); + } + + // The PointerEnter event will get fired as soon as Pointer input is received. + // This event handler implementation will query the device providing input to see if it's a pen and + // then check to see the pen supports tactile feedback and, if so, configure the PenDevice + // to send the appropriate tactile signal based on what is selected in the dropdown. + void Scenario3_InkingFeedback::HapticCanvas_Entered(IInspectable const&, PointerRoutedEventArgs const& e) + { + // If the current Pointer device is not a pen, exit + if (e.Pointer().PointerDeviceType() != PointerDeviceType::Pen) + { + return; + } + + // Attempt to retrieve the PenDevice from the current PointerId + penDevice = PenDevice::GetFromPointerId(e.Pointer().PointerId()); + + // If a PenDevice cannot be retrieved based on the PointerId, it does not support + // advanced pen features, such as tactile feedback + if (penDevice == nullptr) + { + statusText().Text(L"Advanced pen features not supported"); + return; + } + + // Check to see if the current PenDevice supports tactile feedback by seeing if it + // has a SimpleHapticsController + hapticsController = penDevice.SimpleHapticsController(); + if (hapticsController == nullptr) + { + statusText().Text(L"This pen does not provide tactile feedback"); + return; + } + + // Get feedback based on the user's selected waveform and what the haptic controller supports + std::wstring message; + currentFeedback = GetSelectedFeedbackOrFallback(message); + + // Send the current feedback to the PenDevice's SimpleHapticsController. + // Once sent, inking tactile feedback will be triggered as soon as the pen tip touches + // the screen and will be stopped once the pen tip is lifted from the screen. + // Also, check to see if the current PenDevice's SimpleHapticsController supports + // setting the intensity value of the tactile feedback. If so, set it based + // on the slider. If not, send the waveform without custom intensity. + if (hapticsController.IsIntensitySupported()) + { + hapticsController.SendHapticFeedback(currentFeedback, intensitySlider().Value() / 100.0); + message.append(L"\nIntensity set to " + to_hstring(intensitySlider().Value()) + L"%"); + } + else + { + hapticsController.SendHapticFeedback(currentFeedback); + message.append(L"\nSetting intensity is not supported by this pen"); + } + statusText().Text(message); + } + + // Stop sending the tactile feedback and clear the current penDevice and hapticsController on PointerExit. + // Stopping the feedback is important as it clears the tactile signal from the PenDevice's + // SimpleHapticsController, ensuring that it has a clean state once it next enters range. + void Scenario3_InkingFeedback::HapticCanvas_Exited(IInspectable const&, PointerRoutedEventArgs const&) + { + penDevice = nullptr; + statusText().Text(L""); + if (hapticsController != nullptr) + { + hapticsController.StopFeedback(); + hapticsController = nullptr; + currentFeedback = nullptr; + } + } + + // Get feedback which matches the currently selected waveform in the dropdown. + // To get the feedback, the SimpleHapticsController's SupportedFeedback list must be traversed + // to see if a matching feedback is present. + // This is important because InkContinuous is the only Inking waveform required to be + // supported by every pen with haptics. + // If no matching feedback is found in the SupportedFeedback list, this method returns + // InkContinuous as a guaranteed fallback. + SimpleHapticsControllerFeedback Scenario3_InkingFeedback::GetSelectedFeedbackOrFallback(std::wstring& message) + { + // Look up the waveform the user selected. + hstring name = waveformComboBox().SelectionBoxItem().as(); + uint16_t waveform = WaveformNamesMap().at(name); + + // See if the haptics controller supports the selected waveform. + SimpleHapticsControllerFeedback feedback = FindSupportedFeedback(hapticsController, waveform); + if (feedback != nullptr) + { + message = L"Waveform set to " + name; + return feedback; + } + + // It does not. Use InkContinuous as a fallback. + message = name + L" is not supported by this pen, so falling back to InkContinuous"; + return FindSupportedFeedback(hapticsController, KnownSimpleHapticsControllerWaveforms::InkContinuous()); + } +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario3_InkingFeedback.h b/Samples/PenHaptics/cppwinrt/Scenario3_InkingFeedback.h new file mode 100644 index 000000000..e1375f64d --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario3_InkingFeedback.h @@ -0,0 +1,41 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario3_InkingFeedback.g.h" +#include "MainPage.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario3_InkingFeedback : Scenario3_InkingFeedbackT + { + Scenario3_InkingFeedback(); + + void HapticCanvas_Entered(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + void HapticCanvas_Exited(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + + private: + Windows::Devices::Haptics::SimpleHapticsController hapticsController{ nullptr }; + Windows::Devices::Input::PenDevice penDevice{ nullptr }; + Windows::Devices::Haptics::SimpleHapticsControllerFeedback currentFeedback{ nullptr }; + + Windows::Devices::Haptics::SimpleHapticsControllerFeedback GetSelectedFeedbackOrFallback(std::wstring& message); + + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario3_InkingFeedback : Scenario3_InkingFeedbackT + { + }; +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario4_InteractionFeedback.cpp b/Samples/PenHaptics/cppwinrt/Scenario4_InteractionFeedback.cpp new file mode 100644 index 000000000..b7b8563be --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario4_InteractionFeedback.cpp @@ -0,0 +1,138 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "SampleConfiguration.h" +#include "Scenario4_InteractionFeedback.h" +#include "Scenario4_InteractionFeedback.g.cpp" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Devices::Haptics; +using namespace Windows::Devices::Input; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Input; + + +// This sample shows how to play Interaction feedback tactile signals, how to +// adjust the intensity of the tactile signal, and how to fall back to a default +// waveform if the desired waveform is not supported by the current PenDevice +namespace winrt::SDKTemplate::implementation +{ + Scenario4_InteractionFeedback::Scenario4_InteractionFeedback() + { + InitializeComponent(); + } + + // The PointerEnter event will get fired as soon as Pointer input is received. + // This event handler implementation will query the device providing input to see if it's a pen and + // then check to see the pen supports tactile feedback + void Scenario4_InteractionFeedback::MainGrid_Entered(IInspectable const&, PointerRoutedEventArgs const& e) + { + // If the current Pointer device is not a pen, exit + if (e.Pointer().PointerDeviceType() != PointerDeviceType::Pen) + { + return; + } + + // Attempt to retrieve the PenDevice from the current PointerId + penDevice = PenDevice::GetFromPointerId(e.Pointer().PointerId()); + + // If a PenDevice cannot be retrieved based on the PointerId, it does not support + // advanced pen features, such as tactile feedback + if (penDevice == nullptr) + { + statusText().Text(L"Advanced pen features not supported"); + return; + } + + // Check to see if the current PenDevice supports tactile feedback by seeing if it + // has a SimpleHapticsController + hapticsController = penDevice.SimpleHapticsController(); + if (hapticsController == nullptr) + { + statusText().Text(L"This pen does not provide tactile feedback"); + return; + } + } + + // Stop sending the tactile feedback and clear the current penDevice and hapticsController on PointerExit. + // Stopping the feedback is important as it clears the tactile signal from the PenDevice's + // SimpleHapticsController, ensuring that it has a clean state once it next enters range. + void Scenario4_InteractionFeedback::MainGrid_Exited(IInspectable const&, PointerRoutedEventArgs const&) + { + penDevice = nullptr; + statusText().Text(L""); + if (hapticsController != nullptr) + { + hapticsController.StopFeedback(); + hapticsController = nullptr; + currentFeedback = nullptr; + } + } + + // Send the selected waveform upon the button being clicked + void Scenario4_InteractionFeedback::SendFeedback_Clicked(IInspectable const&, RoutedEventArgs const&) + { + if (hapticsController == nullptr) + { + return; + } + + // Get the current feedback based on what is selected in the dropdown + std::wstring message; + currentFeedback = GetSelectedFeedbackOrFallback(message); + + // Send the current feedback to the PenDevice's SimpleHapticsController. + // Once sent, unlike inking feedback, the feedback will be played immediately. + // Also, check to see if the current PenDevice's SimpleHapticsController supports + // setting the intensity value of the tactile feedback. If so, set it based + // on the slider. If not, send the waveform without custom intensity. + if (hapticsController.IsIntensitySupported()) + { + hapticsController.SendHapticFeedback(currentFeedback, intensitySlider().Value() / 100); + message.append(L"\nIntensity set to " + to_hstring(intensitySlider().Value()) + L"%"); + } + else + { + hapticsController.SendHapticFeedback(currentFeedback); + message.append(L"\nSetting intensity is not supported by this pen"); + } + statusText().Text(message); + } + + // Get the currently selected waveform in the dropdown. + // To get the waveform, the SimpleHapticsController's SupportedFeedback list must be traversed + // to see if the desired waveform is present. + // This is important because Click is the only Interaction waveform required to be + // supported by every pen with haptics. + // If the desired waveform is not in the SupportedFeedback list, this method returns + // Click as a guaranteed fallback. + SimpleHapticsControllerFeedback Scenario4_InteractionFeedback::GetSelectedFeedbackOrFallback(std::wstring& message) + { + // Look up the waveform the user selected. + hstring name = waveformComboBox().SelectionBoxItem().as(); + uint16_t waveform = WaveformNamesMap().at(name); + + // See if the haptics controller supports the selected waveform. + SimpleHapticsControllerFeedback feedback = FindSupportedFeedback(hapticsController, waveform); + if (feedback != nullptr) + { + message = L"Waveform set to " + name; + return feedback; + } + + // It does not. Use Click as a fallback. + message = name + L" is not supported by this pen, so falling back to Click"; + return FindSupportedFeedback(hapticsController, KnownSimpleHapticsControllerWaveforms::Click()); + } +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario4_InteractionFeedback.h b/Samples/PenHaptics/cppwinrt/Scenario4_InteractionFeedback.h new file mode 100644 index 000000000..8e9b19858 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario4_InteractionFeedback.h @@ -0,0 +1,42 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario4_InteractionFeedback.g.h" +#include "MainPage.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario4_InteractionFeedback : Scenario4_InteractionFeedbackT + { + Scenario4_InteractionFeedback(); + + void MainGrid_Entered(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + void MainGrid_Exited(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + void SendFeedback_Clicked(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const& e); + + private: + Windows::Devices::Haptics::SimpleHapticsController hapticsController{ nullptr }; + Windows::Devices::Input::PenDevice penDevice{ nullptr }; + Windows::Devices::Haptics::SimpleHapticsControllerFeedback currentFeedback{ nullptr }; + + Windows::Devices::Haptics::SimpleHapticsControllerFeedback GetSelectedFeedbackOrFallback(std::wstring& message); + + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario4_InteractionFeedback : Scenario4_InteractionFeedbackT + { + }; +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario5_InkingAndInteractionFeedback.cpp b/Samples/PenHaptics/cppwinrt/Scenario5_InkingAndInteractionFeedback.cpp new file mode 100644 index 000000000..10549f87a --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario5_InkingAndInteractionFeedback.cpp @@ -0,0 +1,300 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "pch.h" +#include "SampleConfiguration.h" +#include "Scenario5_InkingAndInteractionFeedback.h" +#include "Scenario5_InkingAndInteractionFeedback.g.cpp" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Devices::Haptics; +using namespace Windows::Devices::Input; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Input; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Shapes; + + +// This sample shows how to use Inking and Interaction tactile signals in +// conjunction with each other. +// Namely, this sample plays the InkContinuous waveform when dragging a movable +// rectangle and then plays the Click waveform when the rectangle is in range +// of a snap gridline and, finally, if the movable rectangle snaps to a gridline +// upon release, it plays the Click waveform one more time. +namespace winrt::SDKTemplate::implementation +{ + // Set the initial configuration of the canvas and the rectangles + Scenario5_InkingAndInteractionFeedback::Scenario5_InkingAndInteractionFeedback() + { + InitializeComponent(); + InitializeGridLines(); + InitializeManipulationTransforms(); + } + + // Draw gridlines based on the gridSize + void Scenario5_InkingAndInteractionFeedback::InitializeGridLines() + { + SolidColorBrush darkGray(Colors::DarkGray()); + double width = hapticCanvas().Width(); + double height = hapticCanvas().Height(); + for (int i = 0; i <= width; i += gridSize) + { + Line currLine; + currLine.Stroke(darkGray); + currLine.X1(i); + currLine.X2(i); + currLine.Y1(0); + currLine.Y2(height); + hapticCanvas().Children().Append(currLine); + } + for (int i = 0; i <= height; i += gridSize) + { + Line currLine; + currLine.Stroke(darkGray); + currLine.X1(0); + currLine.X2(width); + currLine.Y1(i); + currLine.Y2(i); + hapticCanvas().Children().Append(currLine); + } + } + + // Configure the Manipulation transforms that are used to drag the rectangle around + void Scenario5_InkingAndInteractionFeedback::InitializeManipulationTransforms() + { + previousTransform = MatrixTransform(); + previousTransform.Matrix(MatrixHelper::Identity()); + deltaTransform = CompositeTransform(); + topLeft = Point(75, 75); + deltaTransform.TranslateX(topLeft.X); + deltaTransform.TranslateY(topLeft.Y); + + transforms = TransformGroup(); + transforms.Children().ReplaceAll({ previousTransform, deltaTransform }); + + // Set the render transform on the rect + movableRect().RenderTransform(transforms); + } + + + // The PointerEnter event will get fired as soon as Pointer input is received in the movableRect. + // This event handler implementation will query the device providing input to see if it's a pen and + // then check to see the pen supports tactile feedback and, if so, configure the PenDevice + // to send the InkContinuous tactile signal. + void Scenario5_InkingAndInteractionFeedback::MovableRect_Entered(IInspectable const&, PointerRoutedEventArgs const& e) + { + // If the current Pointer device is not a pen, exit + if (e.Pointer().PointerDeviceType() != PointerDeviceType::Pen) + { + return; + } + + // Attempt to retrieve the PenDevice from the current PointerId + penDevice = PenDevice::GetFromPointerId(e.Pointer().PointerId()); + + // If a PenDevice cannot be retrieved based on the PointerId, it does not support + // advanced pen features, such as tactile feedback + if (penDevice == nullptr) + { + statusText().Text(L"Advanced pen features not supported"); + return; + } + + // Check to see if the current PenDevice supports tactile feedback by seeing if it + // has a SimpleHapticsController + hapticsController = penDevice.SimpleHapticsController(); + if (hapticsController == nullptr) + { + statusText().Text(L"This pen does not provide tactile feedback"); + return; + } + + // Send the InkContinuous waveform to the PenDevice's SimpleHapticsController. + // Once sent, inking continuous tactile feedback will be triggered as soon as the pen tip touches + // the screen and will be stopped once the pen tip is lifted from the screen. + // Also, check to see if the current PenDevice's SimpleHapticsController supports + // setting the intensity value of the tactile feedback. If so, set it to 0.75. + // If not, send the waveform with the default intensity. + SimpleHapticsControllerFeedback feedback = FindSupportedFeedback(hapticsController, KnownSimpleHapticsControllerWaveforms::InkContinuous()); + if (feedback != nullptr) + { + if (hapticsController.IsIntensitySupported()) + { + hapticsController.SendHapticFeedback(feedback, 0.75); + } + else + { + hapticsController.SendHapticFeedback(feedback); + } + } + } + + // Stop sending the tactile feedback and clear the current penDevice and hapticsController on PointerExit. + // Stopping the feedback is important as it clears the tactile signal from the PenDevice's + // SimpleHapticsController, ensuring that it has a clean state once it next enters range. + void Scenario5_InkingAndInteractionFeedback::MovableRect_Exited(IInspectable const&, PointerRoutedEventArgs const&) + { + penDevice = nullptr; + statusText().Text(L""); + if (hapticsController != nullptr) + { + hapticsController.StopFeedback(); + hapticsController = nullptr; + } + } + + // Change the color of the movableRect once a manipulation is started. This coincides with the pen + // touching down inside the rectangle, so this color change will happen at the same time that the + // InkContinuous waveform begins playing. + void Scenario5_InkingAndInteractionFeedback::MovableRect_ManipulationStarted(IInspectable const&, ManipulationStartedRoutedEventArgs const&) + { + movableRect().Style(MovableRectangleDragged()); + } + + // Update the position of the movableRect and update the previewRect as needed + void Scenario5_InkingAndInteractionFeedback::MovableRect_ManipulationDelta(IInspectable const&, ManipulationDeltaRoutedEventArgs const& e) + { + previousTransform.Matrix(transforms.Value()); + + // Look at the Delta property of the ManipulationDeltaRoutedEventArgs to retrieve the X and Y changes + Point translation = e.Delta().Translation; + deltaTransform.TranslateX(translation.X); + deltaTransform.TranslateY(translation.Y); + topLeft.X += translation.X; + topLeft.Y += translation.Y; + UpdatePreviewRect(); + } + + // Update the visibilty or position of the previewRect based on the location + // of the movableRect. + // If the previewRect is snapped to a given gridline for the first time, + // play the Click tactile signal. + void Scenario5_InkingAndInteractionFeedback::UpdatePreviewRect() + { + if (IsInSnapRange()) + { + previewRect().Visibility(Visibility::Visible); + Point newPreviewTopLeft = GetSnappedPoint(); + + // Check to see if this is the first time that the previewRect is in range of a given gridline + // and send the Click tactile signal if so + bool sendHaptics = true; + if ((previewTopLeft.X == newPreviewTopLeft.X && previewTopLeft.Y == newPreviewTopLeft.Y) || + (previewTopLeft.X == newPreviewTopLeft.X && newPreviewTopLeft.Y == topLeft.Y) || + (previewTopLeft.Y == newPreviewTopLeft.Y && newPreviewTopLeft.X == topLeft.X)) + { + sendHaptics = false; + } + if (previewTopLeft.X != newPreviewTopLeft.X || previewTopLeft.Y != newPreviewTopLeft.Y) + { + previewTopLeft = newPreviewTopLeft; + previewRect().Translation({ previewTopLeft.X, previewTopLeft.Y, 0 }); + if (hapticsController != nullptr && sendHaptics) + { + SimpleHapticsControllerFeedback feedback = FindSupportedFeedback(hapticsController, KnownSimpleHapticsControllerWaveforms::Click()); + if (feedback != nullptr) + { + hapticsController.SendHapticFeedback(feedback); + } + } + } + } + else + { + previewRect().Visibility(Visibility::Collapsed); + previewTopLeft = Point(-1, -1); + } + } + + // When the manipulation is over, reset the color of the movableRect and + // snap it to a gridline if appropriate + void Scenario5_InkingAndInteractionFeedback::MovableRect_ManipulationCompleted(IInspectable const&, ManipulationCompletedRoutedEventArgs const&) + { + movableRect().Style(MovableRectangle()); + if (IsInSnapRange()) + { + SnapMovableRectToGridLines(); + } + } + + // Updates the position of the movableRect to snap to the appropriate gridline(s). + // In addition, play the Click tactile signal to provide additional feedback to the user that it has snapped. + void Scenario5_InkingAndInteractionFeedback::SnapMovableRectToGridLines() + { + topLeft = GetSnappedPoint(); + previousTransform = MatrixTransform(); + previousTransform.Matrix(MatrixHelper::Identity()); + deltaTransform.TranslateX(topLeft.X); + deltaTransform.TranslateY(topLeft.Y); + + transforms = TransformGroup(); + transforms.Children().ReplaceAll({ previousTransform, deltaTransform }); + + movableRect().RenderTransform(transforms); + + if (hapticsController != nullptr) + { + SimpleHapticsControllerFeedback feedback = FindSupportedFeedback(hapticsController, KnownSimpleHapticsControllerWaveforms::Click()); + if (feedback != nullptr) + { + hapticsController.SendHapticFeedback(feedback); + } + } + } + + // Retrieve the point corresponding to the top-left corner of where the movableRect would snap to + // if released. + Point Scenario5_InkingAndInteractionFeedback::GetSnappedPoint() + { + Point snappedPoint; + float remainderX = (float)fmod(topLeft.X, gridSize); + if (remainderX <= snapDistance) + { + snappedPoint.X = topLeft.X - remainderX; + } + else if (remainderX >= gridSize - snapDistance) + { + snappedPoint.X = topLeft.X + gridSize - remainderX; + } + else + { + snappedPoint.X = topLeft.X; + } + float remainderY = (float)fmod(topLeft.Y, gridSize); + if (remainderY <= snapDistance) + { + snappedPoint.Y = topLeft.Y - remainderY; + } + else if (remainderY >= gridSize - snapDistance) + { + snappedPoint.Y = topLeft.Y + gridSize - remainderY; + } + else + { + snappedPoint.Y = topLeft.Y; + } + return snappedPoint; + } + + // Check to see if the movableRect is currently within snapping range of a gridline + bool Scenario5_InkingAndInteractionFeedback::IsInSnapRange() + { + float remainderX = (float)fmod(topLeft.X, gridSize); + float remainderY = (float)fmod(topLeft.Y, gridSize); + return remainderX <= snapDistance || + remainderX >= gridSize - snapDistance || + remainderY <= snapDistance || + remainderY >= gridSize - snapDistance; + } +} diff --git a/Samples/PenHaptics/cppwinrt/Scenario5_InkingAndInteractionFeedback.h b/Samples/PenHaptics/cppwinrt/Scenario5_InkingAndInteractionFeedback.h new file mode 100644 index 000000000..6ec46220c --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/Scenario5_InkingAndInteractionFeedback.h @@ -0,0 +1,54 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario5_InkingAndInteractionFeedback.g.h" +#include "MainPage.h" + +namespace winrt::SDKTemplate::implementation +{ + struct Scenario5_InkingAndInteractionFeedback : Scenario5_InkingAndInteractionFeedbackT + { + Scenario5_InkingAndInteractionFeedback(); + + void MovableRect_Entered(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + void MovableRect_Exited(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); + void MovableRect_ManipulationStarted(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::ManipulationStartedRoutedEventArgs const&); + void MovableRect_ManipulationDelta(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs const& e); + void MovableRect_ManipulationCompleted(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::ManipulationCompletedRoutedEventArgs const&); + + private: + const int gridSize = 100; + const double snapDistance = 20.0; + Windows::Foundation::Point topLeft; + Windows::Foundation::Point previewTopLeft{ -1, -1 }; + Windows::UI::Xaml::Media::TransformGroup transforms{ nullptr }; + Windows::UI::Xaml::Media::MatrixTransform previousTransform{ nullptr }; + Windows::UI::Xaml::Media::CompositeTransform deltaTransform{ nullptr }; + Windows::Devices::Input::PenDevice penDevice{ nullptr }; + Windows::Devices::Haptics::SimpleHapticsController hapticsController{ nullptr }; + + void InitializeGridLines(); + void InitializeManipulationTransforms(); + void UpdatePreviewRect(); + void SnapMovableRectToGridLines(); + Windows::Foundation::Point GetSnappedPoint(); + bool IsInSnapRange(); + }; +} + +namespace winrt::SDKTemplate::factory_implementation +{ + struct Scenario5_InkingAndInteractionFeedback : Scenario5_InkingAndInteractionFeedbackT + { + }; +} diff --git a/Samples/PenHaptics/cppwinrt/packages.config b/Samples/PenHaptics/cppwinrt/packages.config new file mode 100644 index 000000000..a125ab51d --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Samples/PenHaptics/cppwinrt/pch.cpp b/Samples/PenHaptics/cppwinrt/pch.cpp new file mode 100644 index 000000000..01484ff5a --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/pch.cpp @@ -0,0 +1,6 @@ +// +// pch.cpp +// Include the standard header and generate the precompiled header. +// + +#include "pch.h" diff --git a/Samples/PenHaptics/cppwinrt/pch.h b/Samples/PenHaptics/cppwinrt/pch.h new file mode 100644 index 000000000..766a18c10 --- /dev/null +++ b/Samples/PenHaptics/cppwinrt/pch.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "winrt/Windows.Foundation.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Foundation.Numerics.h" +#include "winrt/Windows.ApplicationModel.Activation.h" +#include "winrt/Windows.Devices.Haptics.h" +#include "winrt/Windows.Devices.Input.h" +#include "winrt/Windows.System.h" +#include "winrt/Windows.UI.h" +#include "winrt/Windows.UI.Core.h" +#include "winrt/Windows.UI.Xaml.h" +#include "winrt/Windows.UI.Xaml.Automation.Peers.h" +#include "winrt/Windows.UI.Xaml.Controls.h" +#include "winrt/Windows.UI.Xaml.Controls.Primitives.h" +#include "winrt/Windows.UI.Xaml.Documents.h" +#include "winrt/Windows.UI.Xaml.Input.h" +#include "winrt/Windows.UI.Xaml.Interop.h" +#include "winrt/Windows.UI.Xaml.Markup.h" +#include "winrt/Windows.UI.Xaml.Media.h" +#include "winrt/Windows.UI.Xaml.Navigation.h" +#include "winrt/Windows.UI.Xaml.Shapes.h" +#include +#include diff --git a/Samples/PenHaptics/cs/Package.appxmanifest b/Samples/PenHaptics/cs/Package.appxmanifest new file mode 100644 index 000000000..dc95ecafe --- /dev/null +++ b/Samples/PenHaptics/cs/Package.appxmanifest @@ -0,0 +1,49 @@ + + + + + + + + + + PenHaptics C# Sample + Microsoft Corporation + Assets\StoreLogo-sdk.png + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/PenHaptics/cs/PenHaptics.csproj b/Samples/PenHaptics/cs/PenHaptics.csproj new file mode 100644 index 000000000..fe46ae10a --- /dev/null +++ b/Samples/PenHaptics/cs/PenHaptics.csproj @@ -0,0 +1,208 @@ + + + + + Debug + x86 + {8876C997-7930-4C1F-ABBB-5356318D6DEB} + AppContainerExe + Properties + SDKTemplate + PenHaptics + en-US + UAP + 10.0.22000.0 + $(TargetPlatformVersion) + 15 + true + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM + false + prompt + true + + + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM + false + prompt + true + true + + + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x64 + false + prompt + true + + + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x64 + false + prompt + true + true + + + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x86 + false + prompt + true + + + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x86 + false + prompt + true + true + + + + App.xaml.cs + App.xaml + + + MainPage.xaml.cs + MainPage.xaml + + + Properties\AssemblyInfo.cs + + + Scenario5_InkingAndInteractionFeedback.xaml + + + Scenario3_InkingFeedback.xaml + + + Scenario4_InteractionFeedback.xaml + + + + Scenario1_InkCanvasTactileFeedback.xaml + + + Scenario2_SupportedTactileFeedback.xaml + + + + + Designer + + + + + App.xaml + MSBuild:Compile + Designer + + + MainPage.xaml + MSBuild:Compile + Designer + + + Scenario5_InkingAndInteractionFeedback.xaml + Designer + MSBuild:Compile + + + Scenario3_InkingFeedback.xaml + Designer + MSBuild:Compile + + + Scenario1_InkCanvasTactileFeedback.xaml + MSBuild:Compile + Designer + + + Scenario4_InteractionFeedback.xaml + Designer + MSBuild:Compile + + + Scenario2_SupportedTactileFeedback.xaml + MSBuild:Compile + Designer + + + Styles\Styles.xaml + MSBuild:Compile + Designer + + + + + Properties\Default.rd.xml + + + Assets\microsoft-sdk.png + + + Assets\smallTile-sdk.png + + + Assets\splash-sdk.png + + + Assets\squareTile-sdk.png + + + Assets\storeLogo-sdk.png + + + Assets\tile-sdk.png + + + Assets\windows-sdk.png + + + + + 5.0.0 + + + + 15.0 + + + + \ No newline at end of file diff --git a/Samples/PenHaptics/cs/PenHaptics.sln b/Samples/PenHaptics/cs/PenHaptics.sln new file mode 100644 index 000000000..3afae38ba --- /dev/null +++ b/Samples/PenHaptics/cs/PenHaptics.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31624.102 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PenHaptics", "PenHaptics.csproj", "{8876C997-7930-4C1F-ABBB-5356318D6DEB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Debug|ARM.ActiveCfg = Debug|ARM + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Debug|ARM.Build.0 = Debug|ARM + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Debug|ARM.Deploy.0 = Debug|ARM + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Debug|x64.ActiveCfg = Debug|x64 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Debug|x64.Build.0 = Debug|x64 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Debug|x64.Deploy.0 = Debug|x64 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Debug|x86.ActiveCfg = Debug|x86 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Debug|x86.Build.0 = Debug|x86 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Debug|x86.Deploy.0 = Debug|x86 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Release|ARM.ActiveCfg = Release|ARM + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Release|ARM.Build.0 = Release|ARM + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Release|ARM.Deploy.0 = Release|ARM + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Release|x64.ActiveCfg = Release|x64 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Release|x64.Build.0 = Release|x64 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Release|x64.Deploy.0 = Release|x64 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Release|x86.ActiveCfg = Release|x86 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Release|x86.Build.0 = Release|x86 + {8876C997-7930-4C1F-ABBB-5356318D6DEB}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5AB4407F-C89C-4320-99B2-D8D5E48096DB} + EndGlobalSection +EndGlobal diff --git a/Samples/PenHaptics/cs/SampleConfiguration.cs b/Samples/PenHaptics/cs/SampleConfiguration.cs new file mode 100644 index 000000000..abeb9fc1e --- /dev/null +++ b/Samples/PenHaptics/cs/SampleConfiguration.cs @@ -0,0 +1,71 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +using System; +using System.Collections.Generic; +using Windows.Devices.Haptics; +using Windows.UI.Xaml.Controls; + +namespace SDKTemplate +{ + public partial class MainPage : Page + { + public const string FEATURE_NAME = "PenHaptics C# Sample"; + + List scenarios = new List + { + new Scenario() { Title="Ink Canvas Tactile Feedback", ClassType=typeof(Scenario1_InkCanvasTactileFeedback)}, + new Scenario() { Title="Query Tactile Feedback Support", ClassType=typeof(Scenario2_SupportedTactileFeedback)}, + new Scenario() { Title="Inking Feedback", ClassType=typeof(Scenario3_InkingFeedback)}, + new Scenario() { Title="Interaction Feedback", ClassType=typeof(Scenario4_InteractionFeedback)}, + new Scenario() { Title="Inking and Interaction Feedback", ClassType=typeof(Scenario5_InkingAndInteractionFeedback)}, + }; + + // Mapping between waveform names and waveform values. + public static IReadOnlyDictionary WaveformNamesMap { get; } = new Dictionary() + { + ["BrushContinuous"] = KnownSimpleHapticsControllerWaveforms.BrushContinuous, + ["BuzzContinuous"] = KnownSimpleHapticsControllerWaveforms.BuzzContinuous, + ["ChiselMarkerContinuous"] = KnownSimpleHapticsControllerWaveforms.ChiselMarkerContinuous, + ["Click"] = KnownSimpleHapticsControllerWaveforms.Click, + ["EraserContinuous"] = KnownSimpleHapticsControllerWaveforms.EraserContinuous, + ["Error"] = KnownSimpleHapticsControllerWaveforms.Error, + ["GalaxyPenContinuous"] = KnownSimpleHapticsControllerWaveforms.GalaxyPenContinuous, + ["Hover"] = KnownSimpleHapticsControllerWaveforms.Hover, + ["InkContinuous"] = KnownSimpleHapticsControllerWaveforms.InkContinuous, + ["MarkerContinuous"] = KnownSimpleHapticsControllerWaveforms.MarkerContinuous, + ["PencilContinuous"] = KnownSimpleHapticsControllerWaveforms.PencilContinuous, + ["Release"] = KnownSimpleHapticsControllerWaveforms.Release, + ["RumbleContinuous"] = KnownSimpleHapticsControllerWaveforms.RumbleContinuous, + ["Success"] = KnownSimpleHapticsControllerWaveforms.Success, + }; + + // Returns the first SimpleHapticsControllerFeedback supported by the provided SimpleHapticsController + // which supports the specified waveform. + public static SimpleHapticsControllerFeedback FindSupportedFeedback(SimpleHapticsController hapticsController, ushort waveform) + { + foreach (SimpleHapticsControllerFeedback feedback in hapticsController.SupportedFeedback) + { + if (feedback.Waveform == waveform) + { + return feedback; + } + } + return null; + } + } + + public class Scenario + { + public string Title { get; set; } + public Type ClassType { get; set; } + } +} diff --git a/Samples/PenHaptics/cs/Scenario1_InkCanvasTactileFeedback.xaml.cs b/Samples/PenHaptics/cs/Scenario1_InkCanvasTactileFeedback.xaml.cs new file mode 100644 index 000000000..50bfbca9d --- /dev/null +++ b/Samples/PenHaptics/cs/Scenario1_InkCanvasTactileFeedback.xaml.cs @@ -0,0 +1,34 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +// This sample shows how the default InkCanvas supports tactile signals. +// Simply begin inking in the InkCanvas with a supported pen and tactile signals +// will automatically be played for each of the different brush types. +namespace SDKTemplate +{ + public sealed partial class Scenario1_InkCanvasTactileFeedback : Page + { + + public Scenario1_InkCanvasTactileFeedback() + { + InitializeComponent(); + } + + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + outputGrid.Width = RootGrid.ActualWidth; + outputGrid.Height = RootGrid.ActualHeight / 2; + } + } +} diff --git a/Samples/PenHaptics/cs/Scenario2_SupportedTactileFeedback.xaml.cs b/Samples/PenHaptics/cs/Scenario2_SupportedTactileFeedback.xaml.cs new file mode 100644 index 000000000..426275b7d --- /dev/null +++ b/Samples/PenHaptics/cs/Scenario2_SupportedTactileFeedback.xaml.cs @@ -0,0 +1,110 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +using System.Collections.Generic; +using Windows.Devices.Haptics; +using Windows.Devices.Input; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; + +// This sample shows how to query the pen currently providing input to the system to see +// if it supports tactile feedback and, if so, what features it supports +namespace SDKTemplate +{ + public sealed partial class Scenario2_SupportedTactileFeedback : Page + { + SimpleHapticsController hapticsController; + PenDevice penDevice; + + public Scenario2_SupportedTactileFeedback() + { + InitializeComponent(); + } + + // The PointerEnter event will get fired as soon as Pointer input is received. + // This event handler implementation will query the device providing input to see if it's a pen and + // then check to see the pen supports tactile feedback and, if so, what features it supports + private void Pointer_Entered(object sender, PointerRoutedEventArgs e) + { + // If the current Pointer device is not a pen, exit + if (e.Pointer.PointerDeviceType != PointerDeviceType.Pen) + { + supportedFeatures.Text = ""; + supportedFeedback.Text = ""; + return; + } + + // Attempt to retrieve the PenDevice from the current PointerId + penDevice = PenDevice.GetFromPointerId(e.Pointer.PointerId); + + // If a PenDevice cannot be retrieved based on the PointerId, it does not support + // advanced pen features, such as tactile feedback + if (penDevice == null) + { + statusText.Text = "Advanced pen features not supported"; + supportedFeatures.Text = ""; + supportedFeedback.Text = ""; + return; + } + + // Check to see if the current PenDevice supports tactile feedback by seeing if it + // has a SimpleHapticsController + hapticsController = penDevice.SimpleHapticsController; + if (hapticsController == null) + { + statusText.Text = "This pen does not provide tactile feedback"; + return; + } + + // Check which tactile feedback features are supported + supportedFeatures.Text = "Supported Haptics Features:\n"; + if (hapticsController.IsIntensitySupported) + { + supportedFeatures.Text += "Intensity\n"; + } + if (hapticsController.IsPlayCountSupported) + { + supportedFeatures.Text += "PlayCount\n"; + } + if (hapticsController.IsPlayDurationSupported) + { + supportedFeatures.Text += "PlayDuration\n"; + } + if (hapticsController.IsReplayPauseIntervalSupported) + { + supportedFeatures.Text += "ReplayPauseInterval\n"; + } + + // Check which feedback waveforms are supported + supportedFeedback.Text = "Supported Feedback:\n"; + foreach (SimpleHapticsControllerFeedback feedback in hapticsController.SupportedFeedback) + { + ushort waveform = feedback.Waveform; + foreach (KeyValuePair entry in MainPage.WaveformNamesMap) + { + if (entry.Value == waveform) + { + supportedFeedback.Text += entry.Key + "\n"; + break; + } + } + } + statusText.Text = ""; + } + + // Clear the current penDevice and hapticsController on PointerExit + private void Pointer_Exited(object sender, PointerRoutedEventArgs e) + { + penDevice = null; + hapticsController = null; + } + } +} diff --git a/Samples/PenHaptics/cs/Scenario3_InkingFeedback.xaml.cs b/Samples/PenHaptics/cs/Scenario3_InkingFeedback.xaml.cs new file mode 100644 index 000000000..4cb0209c9 --- /dev/null +++ b/Samples/PenHaptics/cs/Scenario3_InkingFeedback.xaml.cs @@ -0,0 +1,128 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +using Windows.Devices.Haptics; +using Windows.Devices.Input; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; + +// This sample shows how to play inking feedback tactile signals, how to +// adjust the intensity of the tactile signal, and how to fall back to a default +// waveform if the desired waveform is not supported by the current PenDevice +namespace SDKTemplate +{ + public sealed partial class Scenario3_InkingFeedback : Page + { + SimpleHapticsController hapticsController; + PenDevice penDevice; + SimpleHapticsControllerFeedback currentFeedback; + + public Scenario3_InkingFeedback() + { + InitializeComponent(); + } + + // The PointerEnter event will get fired as soon as Pointer input is received. + // This event handler implementation will query the device providing input to see if it's a pen and + // then check to see the pen supports tactile feedback and, if so, configure the PenDevice + // to send the appropriate tactile signal based on what is selected in the dropdown. + private void HapticCanvas_Entered(object sender, PointerRoutedEventArgs e) + { + // If the current Pointer device is not a pen, exit + if (e.Pointer.PointerDeviceType != PointerDeviceType.Pen) + { + return; + } + + // Attempt to retrieve the PenDevice from the current PointerId + penDevice = PenDevice.GetFromPointerId(e.Pointer.PointerId); + + // If a PenDevice cannot be retrieved based on the PointerId, it does not support + // advanced pen features, such as tactile feedback + if (penDevice == null) + { + statusText.Text = "Advanced pen features not supported"; + return; + } + + // Check to see if the current PenDevice supports tactile feedback by seeing if it + // has a SimpleHapticsController + hapticsController = penDevice.SimpleHapticsController; + if (hapticsController == null) + { + statusText.Text = "This pen does not provide tactile feedback"; + return; + } + + // Get feedback based on the user's selected waveform + currentFeedback = GetSelectedFeedbackOrFallback(out string message); + + // Send the current feedback to the PenDevice's SimpleHapticsController. + // Once sent, inking tactile feedback will be triggered as soon as the pen tip touches + // the screen and will be stopped once the pen tip is lifted from the screen. + // Also, check to see if the current PenDevice's SimpleHapticsController supports + // setting the intensity value of the tactile feedback. If so, set it based + // on the slider. If not, send the waveform without custom intensity. + if (hapticsController.IsIntensitySupported) + { + hapticsController.SendHapticFeedback(currentFeedback, intensitySlider.Value / 100); + message += "\nIntensity set to " + intensitySlider.Value + "%"; + } + else + { + hapticsController.SendHapticFeedback(currentFeedback); + message += "\nSetting intensity is not supported by this pen"; + } + statusText.Text = message; + } + + // Stop sending the tactile feedback and clear the current penDevice and hapticsController on PointerExit. + // Stopping the feedback is important as it clears the tactile signal from the PenDevice's + // SimpleHapticsController, ensuring that it has a clean state once it next enters range. + private void HapticCanvas_Exited(object sender, PointerRoutedEventArgs e) + { + penDevice = null; + statusText.Text = ""; + if (hapticsController != null) + { + hapticsController.StopFeedback(); + hapticsController = null; + currentFeedback = null; + } + } + + // Get feedback which matches the currently selected waveform in the dropdown. + // To get the feedback, the SimpleHapticsController's SupportedFeedback list must be traversed + // to see if a matching feedback is present. + // This is important because InkContinuous is the only Inking waveform required to be + // supported by every pen with haptics. + // If no matching feedback is found in the SupportedFeedback list, this method returns + // InkContinuous as a guaranteed fallback. + private SimpleHapticsControllerFeedback GetSelectedFeedbackOrFallback(out string message) + { + // Look up the waveform the user selected. + string name = (string)waveformComboBox.SelectionBoxItem; + ushort waveform = MainPage.WaveformNamesMap[name]; + + // See if the haptics controller supports the selected waveform. + SimpleHapticsControllerFeedback feedback = MainPage.FindSupportedFeedback(hapticsController, waveform); + if (feedback != null) + { + message = "Waveform set to " + name; + return feedback; + } + + // It does not. Use InkContinuous as a fallback. + message = name + " is not supported by this pen, so falling back to InkContinuous"; + return MainPage.FindSupportedFeedback(hapticsController, KnownSimpleHapticsControllerWaveforms.InkContinuous); + } + } +} diff --git a/Samples/PenHaptics/cs/Scenario4_InteractionFeedback.xaml.cs b/Samples/PenHaptics/cs/Scenario4_InteractionFeedback.xaml.cs new file mode 100644 index 000000000..eb8fc7dee --- /dev/null +++ b/Samples/PenHaptics/cs/Scenario4_InteractionFeedback.xaml.cs @@ -0,0 +1,136 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +using Windows.Devices.Haptics; +using Windows.Devices.Input; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; + +// This sample shows how to play Interaction feedback tactile signals, how to +// adjust the intensity of the tactile signal, and how to fall back to a default +// waveform if the desired waveform is not supported by the current PenDevice +namespace SDKTemplate +{ + public sealed partial class Scenario4_InteractionFeedback : Page + { + SimpleHapticsController hapticsController; + PenDevice penDevice; + SimpleHapticsControllerFeedback currentFeedback; + + public Scenario4_InteractionFeedback() + { + InitializeComponent(); + } + + // The PointerEnter event will get fired as soon as Pointer input is received. + // This event handler implementation will query the device providing input to see if it's a pen and + // then check to see the pen supports tactile feedback + private void MainGrid_Entered(object sender, PointerRoutedEventArgs e) + { + // If the current Pointer device is not a pen, exit + if (e.Pointer.PointerDeviceType != PointerDeviceType.Pen) + { + return; + } + + // Attempt to retrieve the PenDevice from the current PointerId + penDevice = PenDevice.GetFromPointerId(e.Pointer.PointerId); + + // If a PenDevice cannot be retrieved based on the PointerId, it does not support + // advanced pen features, such as tactile feedback + if (penDevice == null) + { + statusText.Text = "Advanced pen features not supported"; + return; + } + + // Check to see if the current PenDevice supports tactile feedback by seeing if it + // has a SimpleHapticsController + hapticsController = penDevice.SimpleHapticsController; + if (hapticsController == null) + { + statusText.Text = "This pen does not provide tactile feedback"; + return; + } + } + + // Stop sending the tactile feedback and clear the current penDevice and hapticsController on PointerExit. + // Stopping the feedback is important as it clears the tactile signal from the PenDevice's + // SimpleHapticsController, ensuring that it has a clean state once it next enters range. + private void MainGrid_Exited(object sender, PointerRoutedEventArgs e) + { + penDevice = null; + statusText.Text = ""; + if (hapticsController != null) + { + hapticsController.StopFeedback(); + hapticsController = null; + currentFeedback = null; + } + } + + // Send haptic feedback that matches the selected waveform upon the button being clicked + private void SendFeedback_Clicked(object sender, RoutedEventArgs e) + { + if (hapticsController == null) + { + return; + } + + // Get feedback based on the user's selected waveform + currentFeedback = GetSelectedFeedbackOrFallback(out string message); + + // Send the current feedback to the PenDevice's SimpleHapticsController. + // Once sent, unlike inking feedback, this feedback will be played immediately. + // Also, check to see if the current PenDevice's SimpleHapticsController supports + // setting the intensity value of the tactile feedback. If so, set it based + // on the slider. If not, send the feedback without custom intensity. + if (hapticsController.IsIntensitySupported) + { + hapticsController.SendHapticFeedback(currentFeedback, intensitySlider.Value / 100); + message += "\nIntensity set to " + intensitySlider.Value + "%"; + } + else + { + hapticsController.SendHapticFeedback(currentFeedback); + message += "\nSetting intensity is not supported by this pen"; + } + statusText.Text = message; + } + + // Get feedback which matches the currently selected waveform in the dropdown. + // To get the feedback, the SimpleHapticsController's SupportedFeedback list must be traversed + // to see if a matching feedback is present. + // This is important because Click is the only Interaction waveform required to be + // supported by every pen with haptics. + // If no matching feedback is found in the SupportedFeedback list, this method returns + // Click as a guaranteed fallback. + private SimpleHapticsControllerFeedback GetSelectedFeedbackOrFallback(out string message) + { + // Look up the waveform the user selected. + string name = (string)waveformComboBox.SelectionBoxItem; + ushort waveform = MainPage.WaveformNamesMap[name]; + + // See if the haptics controller supports the selected waveform. + SimpleHapticsControllerFeedback feedback = MainPage.FindSupportedFeedback(hapticsController, waveform); + if (feedback != null) + { + message = "Waveform set to " + name; + return feedback; + } + + // It does not. Use Click as a fallback. + message = name + " is not supported by this pen, so falling back to Click"; + return MainPage.FindSupportedFeedback(hapticsController, KnownSimpleHapticsControllerWaveforms.Click); + } + } +} \ No newline at end of file diff --git a/Samples/PenHaptics/cs/Scenario5_InkingAndInteractionFeedback.xaml.cs b/Samples/PenHaptics/cs/Scenario5_InkingAndInteractionFeedback.xaml.cs new file mode 100644 index 000000000..469c0bf33 --- /dev/null +++ b/Samples/PenHaptics/cs/Scenario5_InkingAndInteractionFeedback.xaml.cs @@ -0,0 +1,305 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + + +using System.Numerics; +using Windows.Devices.Haptics; +using Windows.Devices.Input; +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Shapes; + +// This sample shows how to use Inking and Interaction tactile signals in +// conjunction with each other. +// Namely, this sample plays the InkContinuous waveform when dragging a movable +// rectangle and then plays the Click waveform when the rectangle is in range +// of a snap gridline and, finally, if the movable rectangle snaps to a gridline +// upon release, it plays the Click waveform one more time. +namespace SDKTemplate +{ + public sealed partial class Scenario5_InkingAndInteractionFeedback : Page + { + private readonly int gridSize = 100; + readonly double snapDistance = 20; + private Point topLeft; + private Point previewTopLeft = new Point(-1, -1); + private TransformGroup transforms; + private MatrixTransform previousTransform; + private CompositeTransform deltaTransform; + PenDevice penDevice; + SimpleHapticsController hapticsController; + + // Set the initial configuration of the canvas and the rectangles + public Scenario5_InkingAndInteractionFeedback() + { + InitializeComponent(); + InitializeGridLines(); + InitializeManipulationTransforms(); + } + + // Draw gridlines based on the gridSize + private void InitializeGridLines() + { + SolidColorBrush darkGray = new SolidColorBrush(Colors.DarkGray); + double width = hapticCanvas.Width; + double height = hapticCanvas.Height; + for (int i = 0; i <= width; i += gridSize) + { + Line currLine = new Line() { + Stroke = darkGray, + X1 = i, + X2 = i, + Y1 = 0, + Y2 = height, + }; + hapticCanvas.Children.Add(currLine); + } + for (int i = 0; i <= height; i += gridSize) + { + Line currLine = new Line() { + Stroke = darkGray, + X1 = 0, + X2 = width, + Y1 = i, + Y2 = i, + }; + hapticCanvas.Children.Add(currLine); + } + } + + // Configure the Manipulation transforms that are used to drag the rectangle around + private void InitializeManipulationTransforms() + { + previousTransform = new MatrixTransform() { Matrix = Matrix.Identity }; + topLeft = new Point(75, 75); + deltaTransform = new CompositeTransform() { TranslateX = topLeft.X, TranslateY = topLeft.Y }; + + transforms = new TransformGroup() { Children = { previousTransform, deltaTransform } }; + + // Set the render transform on the rect + movableRect.RenderTransform = transforms; + } + + // The PointerEnter event will get fired as soon as Pointer input is received in the movableRect. + // This event handler implementation will query the device providing input to see if it's a pen and + // then check to see the pen supports tactile feedback and, if so, configure the PenDevice + // to send the InkContinuous tactile signal. + private void MovableRect_Entered(object sender, PointerRoutedEventArgs e) + { + // If the current Pointer device is not a pen, exit + if (e.Pointer.PointerDeviceType != PointerDeviceType.Pen) + { + return; + } + + // Attempt to retrieve the PenDevice from the current PointerId + penDevice = PenDevice.GetFromPointerId(e.Pointer.PointerId); + + // If a PenDevice cannot be retrieved based on the PointerId, it does not support + // advanced pen features, such as tactile feedback + if (penDevice == null) + { + statusText.Text = "Advanced pen features not supported"; + return; + } + + // Check to see if the current PenDevice supports tactile feedback by seeing if it + // has a SimpleHapticsController + hapticsController = penDevice.SimpleHapticsController; + if (hapticsController == null) + { + statusText.Text = "This pen does not provide tactile feedback"; + return; + } + + // Send the InkContinuous waveform to the PenDevice's SimpleHapticsController. + // Once sent, inking continuous tactile feedback will be triggered as soon as the pen tip touches + // the screen and will be stopped once the pen tip is lifted from the screen. + // Also, check to see if the current PenDevice's SimpleHapticsController supports + // setting the intensity value of the tactile feedback. If so, set it to 0.75. + // If not, send the waveform with the default intensity. + foreach (var waveform in hapticsController.SupportedFeedback) + { + if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.InkContinuous) + { + if (hapticsController.IsIntensitySupported) + { + hapticsController.SendHapticFeedback(waveform, 0.75); + } + else + { + hapticsController.SendHapticFeedback(waveform); + } + } + } + + } + + // Stop sending the tactile feedback and clear the current penDevice and hapticsController on PointerExit. + // Stopping the feedback is important as it clears the tactile signal from the PenDevice's + // SimpleHapticsController, ensuring that it has a clean state once it next enters range. + private void MovableRect_Exited(object sender, PointerRoutedEventArgs e) + { + penDevice = null; + statusText.Text = ""; + if (hapticsController != null) + { + hapticsController.StopFeedback(); + hapticsController = null; + } + } + + // Change the color of the movableRect once a manipulation is started. This coincides with the pen + // touching down inside the rectangle, so this color change will happen at the same time that the + // InkContinuous waveform begins playing. + private void MovableRect_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) + { + movableRect.Style = MovableRectangleDragged; + } + + // Update the position of the movableRect and update the previewRect as needed + private void MovableRect_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) + { + previousTransform.Matrix = transforms.Value; + + // Look at the Delta property of the ManipulationDeltaRoutedEventArgs to retrieve the X and Y changes + deltaTransform.TranslateX = e.Delta.Translation.X; + deltaTransform.TranslateY = e.Delta.Translation.Y; + topLeft.X += e.Delta.Translation.X; + topLeft.Y += e.Delta.Translation.Y; + UpdatePreviewRect(); + } + + // Update the visibilty or position of the previewRect based on the location + // of the movableRect. + // If the previewRect is snapped to a given gridline for the first time, + // play the Click tactile signal. + private void UpdatePreviewRect() + { + if (IsInSnapRange()) + { + previewRect.Visibility = Visibility.Visible; + Point newPreviewTopLeft = GetSnappedPoint(); + + // Check to see if this is the first time that the previewRect is in range of a given gridline + // and send the Click tactile signal if so + bool sendHaptics = true; + if ((previewTopLeft.X == newPreviewTopLeft.X && previewTopLeft.Y == newPreviewTopLeft.Y) || + (previewTopLeft.X == newPreviewTopLeft.X && newPreviewTopLeft.Y == topLeft.Y) || + (previewTopLeft.Y == newPreviewTopLeft.Y && newPreviewTopLeft.X == topLeft.X)) + { + sendHaptics = false; + } + if (previewTopLeft.X != newPreviewTopLeft.X || previewTopLeft.Y != newPreviewTopLeft.Y) + { + previewTopLeft = newPreviewTopLeft; + previewRect.Translation = new Vector3((float)previewTopLeft.X, (float)previewTopLeft.Y, 0); + if (hapticsController != null && sendHaptics) + { + foreach (var waveform in hapticsController.SupportedFeedback) + { + if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Click) + { + hapticsController.SendHapticFeedback(waveform); + } + } + } + } + } + else + { + previewRect.Visibility = Visibility.Collapsed; + previewTopLeft = new Point(-1, -1); + } + } + + // When the manipulation is over, reset the color of the movableRect and + // snap it to a gridline if appropriate + private void MovableRect_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) + { + movableRect.Style = MovableRectangle; + if (IsInSnapRange()) + { + SnapMovableRectToGridLines(); + } + } + + // Updates the position of the movableRect to snap to the appropriate gridline(s). + // In addition, play the Click tactile signal to provide additional feedback to the user that it has snapped. + private void SnapMovableRectToGridLines() + { + topLeft = GetSnappedPoint(); + previousTransform = new MatrixTransform() { Matrix = Matrix.Identity }; + deltaTransform.TranslateX = topLeft.X; + deltaTransform.TranslateY = topLeft.Y; + + transforms = new TransformGroup() { Children = { previousTransform, deltaTransform } }; + + movableRect.RenderTransform = transforms; + + if (hapticsController != null) + { + foreach (var waveform in hapticsController.SupportedFeedback) + { + if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Click) + { + hapticsController.SendHapticFeedback(waveform); + } + } + } + } + + // Retrieve the point corresponding to the top-left corner of where the movableRect would snap to + // if released. + private Point GetSnappedPoint() + { + Point snappedPoint; + if (topLeft.X % gridSize <= snapDistance) + { + snappedPoint.X = topLeft.X + topLeft.X % gridSize * -1; + } + else if (topLeft.X % gridSize >= gridSize - snapDistance) + { + snappedPoint.X = topLeft.X + gridSize - (topLeft.X % gridSize); + } + else + { + snappedPoint.X = topLeft.X; + } + if (topLeft.Y % gridSize <= snapDistance) + { + snappedPoint.Y = topLeft.Y + topLeft.Y % gridSize * -1; + } + else if (topLeft.Y % gridSize >= gridSize - snapDistance) + { + snappedPoint.Y = topLeft.Y + gridSize - (topLeft.Y % gridSize); + } + else + { + snappedPoint.Y = topLeft.Y; + } + return snappedPoint; + } + + // Check to see if the movableRect is currently within snapping range of a gridline + private bool IsInSnapRange() + { + return topLeft.X % gridSize <= snapDistance || + topLeft.X % gridSize >= gridSize - snapDistance || + topLeft.Y % gridSize <= snapDistance || + topLeft.Y % gridSize >= gridSize - snapDistance; + } + } +} diff --git a/Samples/PenHaptics/shared/Scenario1_InkCanvasTactileFeedback.xaml b/Samples/PenHaptics/shared/Scenario1_InkCanvasTactileFeedback.xaml new file mode 100644 index 000000000..c9ac5be6f --- /dev/null +++ b/Samples/PenHaptics/shared/Scenario1_InkCanvasTactileFeedback.xaml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + This scenario shows how the default InkCanvas provides tactile feedback via the pen. + Simply use the pen on the canvas to feel the tactile feedback. + Change the pen type between marker, pencil, and highlighter to feel the different types of feedback. + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/PenHaptics/shared/Scenario2_SupportedTactileFeedback.xaml b/Samples/PenHaptics/shared/Scenario2_SupportedTactileFeedback.xaml new file mode 100644 index 000000000..ec5dd231a --- /dev/null +++ b/Samples/PenHaptics/shared/Scenario2_SupportedTactileFeedback.xaml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + This scenario shows how to query whether the current pen is capable of providing tactile feedback and, if so, what types of feedback it can provide. + + + + + + + + + + + diff --git a/Samples/PenHaptics/shared/Scenario3_InkingFeedback.xaml b/Samples/PenHaptics/shared/Scenario3_InkingFeedback.xaml new file mode 100644 index 000000000..ac9d7be74 --- /dev/null +++ b/Samples/PenHaptics/shared/Scenario3_InkingFeedback.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + This scenario shows how to start and stop the different inking haptic signals, as well as how to apply fallback logic in the event that a desired signal is not supported by the current pen. In addition, this shows how to alter the intensity of the haptic signal. + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/PenHaptics/shared/Scenario4_InteractionFeedback.xaml b/Samples/PenHaptics/shared/Scenario4_InteractionFeedback.xaml new file mode 100644 index 000000000..e04072018 --- /dev/null +++ b/Samples/PenHaptics/shared/Scenario4_InteractionFeedback.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + This scenario shows how to send the different interaction haptic signals, as well as how to apply fallback logic in the event that a desired signal is not supported by the current pen. In addition, this shows how to alter the intensity of the haptic signal. + + + + + + + + + + + + + + + + + + + +