Converted GazeInteraction project for C++ to C#.
This commit is contained in:
Родитель
30452cf0bb
Коммит
910de59d76
|
@ -157,8 +157,8 @@
|
|||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.UWP.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.vcxproj">
|
||||
<Project>{a5e98964-45b1-442d-a07a-298a3221d81e}</Project>
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.csproj">
|
||||
<Project>{5bf75694-798a-43a0-8150-415de195359c}</Project>
|
||||
<Name>Microsoft.Toolkit.Uwp.Input.GazeInteraction</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GazeInputTest")]
|
||||
|
@ -20,13 +20,13 @@ using System.Runtime.InteropServices;
|
|||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
//[assembly: AssemblyVersion("1.0.0.0")]
|
||||
//[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
// [assembly: AssemblyVersion("1.0.0.0")]
|
||||
// [assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|
|
@ -0,0 +1,36 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class ComboBoxItemGazeTargetItem : GazeTargetItem
|
||||
{
|
||||
internal ComboBoxItemGazeTargetItem(UIElement element)
|
||||
: base(element)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void Invoke()
|
||||
{
|
||||
var peer = FrameworkElementAutomationPeer.FromElement(TargetElement);
|
||||
var comboBoxItemAutomationPeer = peer as ComboBoxItemAutomationPeer;
|
||||
var comboBoxItem = (ComboBoxItem)comboBoxItemAutomationPeer.Owner;
|
||||
|
||||
AutomationPeer ancestor = comboBoxItemAutomationPeer;
|
||||
var comboBoxAutomationPeer = ancestor as ComboBoxAutomationPeer;
|
||||
while (comboBoxAutomationPeer == null)
|
||||
{
|
||||
ancestor = ancestor.Navigate(AutomationNavigationDirection.Parent) as AutomationPeer;
|
||||
comboBoxAutomationPeer = ancestor as ComboBoxAutomationPeer;
|
||||
}
|
||||
|
||||
comboBoxItem.IsSelected = true;
|
||||
comboBoxAutomationPeer.Collapse();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// This parameter is passed to the GazeElement::Invoked event and allows
|
||||
/// the application to prevent default invocation when the user dwells on a control
|
||||
/// </summary>
|
||||
public class DwellInvokedRoutedEventArgs : RoutedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the application handled the event. If this parameter is set to true, the library prevents invoking the control when the user dwells on a control
|
||||
/// </summary>
|
||||
public bool Handled { get; set; }
|
||||
|
||||
internal DwellInvokedRoutedEventArgs()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This parameter is passed to the GazeElement::Invoked event and allows
|
||||
/// the application to prevent default invocation when the user dwells on a control
|
||||
/// </summary>
|
||||
public ref class DwellInvokedRoutedEventArgs : public RoutedEventArgs
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The application should set this value to true to prevent invoking the control when the user dwells on a control
|
||||
/// </summary>
|
||||
property bool Handled;
|
||||
|
||||
internal:
|
||||
|
||||
DwellInvokedRoutedEventArgs()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,36 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// This parameter is passed to the GazeElement.DwellProgressFeedback event. The event is fired to inform the application of the user's progress towards completing dwelling on a control
|
||||
/// </summary>
|
||||
public class DwellProgressEventArgs : RoutedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an enum that reflects the current state of dwell progress
|
||||
/// </summary>
|
||||
public DwellProgressState State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value between 0 and 1 that reflects the fraction of progress towards completing dwell
|
||||
/// </summary>
|
||||
public double Progress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the application handled the event. If this parameter is set to true, the library suppresses default animation for dwell feedback on the control
|
||||
/// </summary>
|
||||
public bool Handled { get; set; }
|
||||
|
||||
internal DwellProgressEventArgs(DwellProgressState state, TimeSpan elapsedDuration, TimeSpan triggerDuration)
|
||||
{
|
||||
State = state;
|
||||
Progress = ((double)elapsedDuration.Ticks) / triggerDuration.Ticks;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DwellProgressState.h"
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This parameter is passed to the GazeElement.DwellProgressFeedback event. The event is fired to inform the application of the user's progress towards completing dwelling on a control
|
||||
/// </summary>
|
||||
public ref class DwellProgressEventArgs : public RoutedEventArgs
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// An enum that reflects the current state of dwell progress
|
||||
/// </summary>
|
||||
property DwellProgressState State { DwellProgressState get() { return _state; }}
|
||||
|
||||
/// <summary>
|
||||
/// A value between 0 and 1 that reflects the fraction of progress towards completing dwell
|
||||
/// </summary>
|
||||
property double Progress { double get() { return _progress; }}
|
||||
|
||||
/// <summary>
|
||||
/// A parameter for the application to set to true if it handles the event. If this parameter is set to true, the library suppresses default animation for dwell feedback on the control
|
||||
/// </summary>
|
||||
property bool Handled;
|
||||
|
||||
internal:
|
||||
DwellProgressEventArgs(DwellProgressState state, TimeSpan elapsedDuration, TimeSpan triggerDuration)
|
||||
{
|
||||
_state = state;
|
||||
_progress = ((double)elapsedDuration.Duration) / triggerDuration.Duration;
|
||||
}
|
||||
private:
|
||||
DwellProgressState _state;
|
||||
double _progress;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,32 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum that reflects the current state of progress towards dwell when a user is focused on a control
|
||||
/// </summary>
|
||||
public enum DwellProgressState
|
||||
{
|
||||
/// <summary>
|
||||
/// User is not looking at the control
|
||||
/// </summary>
|
||||
Idle,
|
||||
|
||||
/// <summary>
|
||||
/// Gaze has entered control but we're not yet showing progress.
|
||||
/// </summary>
|
||||
Fixating,
|
||||
|
||||
/// <summary>
|
||||
/// User is continuing to focus on a control with an intent to dwell and invoke
|
||||
/// </summary>
|
||||
Progressing,
|
||||
|
||||
/// <summary>
|
||||
/// User has completed dwelling on a control
|
||||
/// </summary>
|
||||
Complete
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// An enum that reflects the current state of progress towards dwell when a user is focused on a control
|
||||
/// </summary>
|
||||
public enum class DwellProgressState
|
||||
{
|
||||
/// <summary>
|
||||
/// User is not looking at the control
|
||||
/// </summary>
|
||||
Idle,
|
||||
|
||||
/// <summary>
|
||||
/// Gaze has entered control but we're not yet showing progress.
|
||||
/// </summary>
|
||||
Fixating,
|
||||
|
||||
/// <summary>
|
||||
/// User is continuing to focus on a control with an intent to dwell and invoke
|
||||
/// </summary>
|
||||
Progressing,
|
||||
|
||||
/// <summary>
|
||||
/// User has completed dwelling on a control
|
||||
/// </summary>
|
||||
Complete
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,35 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class ExpandCollapsePatternGazeTargetItem : GazeTargetItem
|
||||
{
|
||||
internal ExpandCollapsePatternGazeTargetItem(UIElement element)
|
||||
: base(element)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void Invoke()
|
||||
{
|
||||
var peer = FrameworkElementAutomationPeer.FromElement(TargetElement);
|
||||
var provider = peer.GetPattern(PatternInterface.ExpandCollapse) as IExpandCollapseProvider;
|
||||
switch (provider.ExpandCollapseState)
|
||||
{
|
||||
case ExpandCollapseState.Collapsed:
|
||||
provider.Expand();
|
||||
break;
|
||||
|
||||
case ExpandCollapseState.Expanded:
|
||||
provider.Collapse();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeCursor.h"
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
GazeCursor::GazeCursor()
|
||||
{
|
||||
_gazePopup = ref new Popup();
|
||||
_gazePopup->IsHitTestVisible = false;
|
||||
|
||||
auto gazeCursor = ref new Shapes::Ellipse();
|
||||
gazeCursor->Fill = ref new SolidColorBrush(Colors::IndianRed);
|
||||
gazeCursor->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
|
||||
gazeCursor->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
|
||||
gazeCursor->Width = 2 * CursorRadius;
|
||||
gazeCursor->Height = 2 * CursorRadius;
|
||||
gazeCursor->Margin = Thickness(-CursorRadius, -CursorRadius, 0, 0);
|
||||
gazeCursor->IsHitTestVisible = false;
|
||||
|
||||
_gazePopup->Child = gazeCursor;
|
||||
}
|
||||
|
||||
void GazeCursor::CursorRadius::set(int value)
|
||||
{
|
||||
_cursorRadius = value;
|
||||
auto gazeCursor = CursorElement;
|
||||
if (gazeCursor != nullptr)
|
||||
{
|
||||
gazeCursor->Width = 2 * _cursorRadius;
|
||||
gazeCursor->Height = 2 * _cursorRadius;
|
||||
gazeCursor->Margin = Thickness(-_cursorRadius, -_cursorRadius, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GazeCursor::IsCursorVisible::set(bool value)
|
||||
{
|
||||
_isCursorVisible = value;
|
||||
SetVisibility();
|
||||
}
|
||||
|
||||
void GazeCursor::IsGazeEntered::set(bool value)
|
||||
{
|
||||
_isGazeEntered = value;
|
||||
SetVisibility();
|
||||
}
|
||||
|
||||
void GazeCursor::LoadSettings(ValueSet^ settings)
|
||||
{
|
||||
if (settings->HasKey("GazeCursor.CursorRadius"))
|
||||
{
|
||||
CursorRadius = (int)(settings->Lookup("GazeCursor.CursorRadius"));
|
||||
}
|
||||
if (settings->HasKey("GazeCursor.CursorVisibility"))
|
||||
{
|
||||
IsCursorVisible = (bool)(settings->Lookup("GazeCursor.CursorVisibility"));
|
||||
}
|
||||
}
|
||||
|
||||
void GazeCursor::SetVisibility()
|
||||
{
|
||||
auto isOpen = _isCursorVisible && _isGazeEntered;
|
||||
if (_gazePopup->IsOpen != isOpen)
|
||||
{
|
||||
_gazePopup->IsOpen = isOpen;
|
||||
}
|
||||
else if (isOpen)
|
||||
{
|
||||
auto topmost = VisualTreeHelper::GetOpenPopups(Window::Current)->First()->Current;
|
||||
if (_gazePopup != topmost)
|
||||
{
|
||||
_gazePopup->IsOpen = false;
|
||||
_gazePopup->IsOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,163 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Linq;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class GazeCursor
|
||||
{
|
||||
private const int DEFAULT_CURSOR_RADIUS = 5;
|
||||
private const bool DEFAULT_CURSOR_VISIBILITY = true;
|
||||
|
||||
public void LoadSettings(ValueSet settings)
|
||||
{
|
||||
if (settings.ContainsKey("GazeCursor.CursorRadius"))
|
||||
{
|
||||
CursorRadius = (int)settings["GazeCursor.CursorRadius"];
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("GazeCursor.CursorVisibility"))
|
||||
{
|
||||
IsCursorVisible = (bool)settings["GazeCursor.CursorVisibility"];
|
||||
}
|
||||
}
|
||||
|
||||
public int CursorRadius
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cursorRadius;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_cursorRadius = value;
|
||||
var gazeCursor = CursorElement;
|
||||
if (gazeCursor != null)
|
||||
{
|
||||
gazeCursor.Width = 2 * _cursorRadius;
|
||||
gazeCursor.Height = 2 * _cursorRadius;
|
||||
gazeCursor.Margin = new Thickness(-_cursorRadius, -_cursorRadius, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCursorVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isCursorVisible;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_isCursorVisible = value;
|
||||
SetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsGazeEntered
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isGazeEntered;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_isGazeEntered = value;
|
||||
SetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
public Point Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cursorPosition;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_cursorPosition = value;
|
||||
_gazePopup.HorizontalOffset = value.X;
|
||||
_gazePopup.VerticalOffset = value.Y;
|
||||
SetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
public UIElement PopupChild
|
||||
{
|
||||
get
|
||||
{
|
||||
return _gazePopup.Child;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_gazePopup.Child = value;
|
||||
}
|
||||
}
|
||||
|
||||
public FrameworkElement CursorElement
|
||||
{
|
||||
get
|
||||
{
|
||||
return _gazePopup.Child as FrameworkElement;
|
||||
}
|
||||
}
|
||||
|
||||
internal GazeCursor()
|
||||
{
|
||||
_gazePopup = new Popup
|
||||
{
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
|
||||
var gazeCursor = new Windows.UI.Xaml.Shapes.Ellipse
|
||||
{
|
||||
Fill = new SolidColorBrush(Colors.IndianRed),
|
||||
VerticalAlignment = VerticalAlignment.Top,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
Width = 2 * CursorRadius,
|
||||
Height = 2 * CursorRadius,
|
||||
Margin = new Thickness(-CursorRadius, -CursorRadius, 0, 0),
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
|
||||
_gazePopup.Child = gazeCursor;
|
||||
}
|
||||
|
||||
private void SetVisibility()
|
||||
{
|
||||
var isOpen = _isCursorVisible && _isGazeEntered;
|
||||
if (_gazePopup.IsOpen != isOpen)
|
||||
{
|
||||
_gazePopup.IsOpen = isOpen;
|
||||
}
|
||||
else if (isOpen)
|
||||
{
|
||||
var topmost = VisualTreeHelper.GetOpenPopups(Window.Current).First();
|
||||
if (_gazePopup != topmost)
|
||||
{
|
||||
_gazePopup.IsOpen = false;
|
||||
_gazePopup.IsOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Popup _gazePopup;
|
||||
private Point _cursorPosition = default;
|
||||
private int _cursorRadius = DEFAULT_CURSOR_RADIUS;
|
||||
private bool _isCursorVisible = DEFAULT_CURSOR_VISIBILITY;
|
||||
private bool _isGazeEntered;
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::UI;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
private ref class GazeCursor sealed
|
||||
{
|
||||
private:
|
||||
const int DEFAULT_CURSOR_RADIUS = 5;
|
||||
const bool DEFAULT_CURSOR_VISIBILITY = true;
|
||||
|
||||
public:
|
||||
void LoadSettings(ValueSet^ settings);
|
||||
property int CursorRadius
|
||||
{
|
||||
int get() { return _cursorRadius; }
|
||||
void set(int value);
|
||||
}
|
||||
|
||||
property bool IsCursorVisible
|
||||
{
|
||||
bool get() { return _isCursorVisible; }
|
||||
void set(bool value);
|
||||
}
|
||||
|
||||
property bool IsGazeEntered
|
||||
{
|
||||
bool get() { return _isGazeEntered; }
|
||||
void set(bool value);
|
||||
}
|
||||
|
||||
property Point Position
|
||||
{
|
||||
Point get()
|
||||
{
|
||||
return _cursorPosition;
|
||||
}
|
||||
|
||||
void set(Point value)
|
||||
{
|
||||
_cursorPosition = value;
|
||||
_gazePopup->HorizontalOffset = value.X;
|
||||
_gazePopup->VerticalOffset = value.Y;
|
||||
SetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
property UIElement^ PopupChild
|
||||
{
|
||||
UIElement^ get()
|
||||
{
|
||||
return _gazePopup->Child;
|
||||
}
|
||||
void set(UIElement^ value)
|
||||
{
|
||||
_gazePopup->Child = value;
|
||||
}
|
||||
}
|
||||
|
||||
property FrameworkElement^ CursorElement
|
||||
{
|
||||
FrameworkElement^ get()
|
||||
{
|
||||
return dynamic_cast<FrameworkElement^>(_gazePopup->Child);
|
||||
}
|
||||
}
|
||||
|
||||
internal:
|
||||
GazeCursor();
|
||||
|
||||
private:
|
||||
void SetVisibility();
|
||||
|
||||
Popup^ _gazePopup;
|
||||
Point _cursorPosition = {};
|
||||
int _cursorRadius = DEFAULT_CURSOR_RADIUS;
|
||||
bool _isCursorVisible = DEFAULT_CURSOR_VISIBILITY;
|
||||
bool _isGazeEntered;
|
||||
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,47 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Surrogate object attached to controls allowing subscription to per-control gaze events.
|
||||
/// </summary>
|
||||
public class GazeElement : DependencyObject
|
||||
{
|
||||
/// <summary>
|
||||
/// This event is fired when the state of the user's gaze on a control has changed
|
||||
/// </summary>
|
||||
public event EventHandler<StateChangedEventArgs> StateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// This event is fired when the user completed dwelling on a control and the control is about to be invoked by default. This event is fired to give the application an opportunity to prevent default invocation
|
||||
/// </summary>
|
||||
public event EventHandler<DwellInvokedRoutedEventArgs> Invoked;
|
||||
|
||||
/// <summary>
|
||||
/// This event is fired to inform the application of the progress towards dwell
|
||||
/// </summary>
|
||||
public event EventHandler<DwellProgressEventArgs> DwellProgressFeedback;
|
||||
|
||||
internal void RaiseStateChanged(object sender, StateChangedEventArgs args)
|
||||
{
|
||||
StateChanged?.Invoke(sender, args);
|
||||
}
|
||||
|
||||
internal void RaiseInvoked(object sender, DwellInvokedRoutedEventArgs args)
|
||||
{
|
||||
Invoked?.Invoke(sender, args);
|
||||
}
|
||||
|
||||
internal bool RaiseProgressFeedback(object sender, DwellProgressState state, TimeSpan elapsedTime, TimeSpan triggerTime)
|
||||
{
|
||||
var args = new DwellProgressEventArgs(state, elapsedTime, triggerTime);
|
||||
DwellProgressFeedback?.Invoke(sender, args);
|
||||
return args.Handled;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DwellInvokedRoutedEventArgs.h"
|
||||
#include "DwellProgressEventArgs.h"
|
||||
#include "StateChangedEventArgs.h"
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// Surrogate object attached to controls allowing subscription to per-control gaze events.
|
||||
/// </summary>
|
||||
public ref class GazeElement sealed : public DependencyObject
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// This event is fired when the state of the user's gaze on a control has changed
|
||||
/// </summary>
|
||||
event EventHandler<StateChangedEventArgs^>^ StateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// This event is fired when the user completed dwelling on a control and the control is about to be invoked by default. This event is fired to give the application an opportunity to prevent default invocation
|
||||
/// </summary>
|
||||
event EventHandler<DwellInvokedRoutedEventArgs^>^ Invoked;
|
||||
|
||||
/// <summary>
|
||||
/// This event is fired to inform the application of the progress towards dwell
|
||||
/// </summary>
|
||||
event EventHandler<DwellProgressEventArgs^>^ DwellProgressFeedback;
|
||||
|
||||
internal:
|
||||
|
||||
void RaiseStateChanged(Object^ sender, StateChangedEventArgs^ args) { StateChanged(sender, args); }
|
||||
|
||||
void RaiseInvoked(Object^ sender, DwellInvokedRoutedEventArgs^ args)
|
||||
{
|
||||
Invoked(sender, args);
|
||||
}
|
||||
|
||||
bool RaiseProgressFeedback(Object^ sender, DwellProgressState state, TimeSpan elapsedTime, TimeSpan triggerTime)
|
||||
{
|
||||
auto args = ref new DwellProgressEventArgs(state, elapsedTime, triggerTime);
|
||||
DwellProgressFeedback(sender, args);
|
||||
return args->Handled;
|
||||
}
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -1,5 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeEventArgs.h"
|
|
@ -0,0 +1,37 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// EventArgs used to send Gaze events. See <see cref="GazePointer.GazeEvent"/>
|
||||
/// </summary>
|
||||
public class GazeEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the application handled the event. If this parameter is set to true, the library prevents handling the events on that particular gaze event.
|
||||
/// </summary>
|
||||
public bool Handled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the location of the Gaze event
|
||||
/// </summary>
|
||||
public Point Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp of the gaze event
|
||||
/// </summary>
|
||||
public TimeSpan Timestamp { get; set; }
|
||||
|
||||
internal void Set(Point location, TimeSpan timestamp)
|
||||
{
|
||||
Handled = false;
|
||||
Location = location;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
public ref struct GazeEventArgs sealed
|
||||
{
|
||||
property bool Handled;
|
||||
property Point Location;
|
||||
property TimeSpan Timestamp;
|
||||
|
||||
GazeEventArgs()
|
||||
{
|
||||
}
|
||||
|
||||
void Set(Point location, TimeSpan timestamp)
|
||||
{
|
||||
Handled = false;
|
||||
Location = location;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -1,43 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeFeedbackPopupFactory.h"
|
||||
#include "GazeInput.h"
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
Popup^ GazeFeedbackPopupFactory::Get()
|
||||
{
|
||||
Popup^ popup;
|
||||
::Windows::UI::Xaml::Shapes::Rectangle^ rectangle;
|
||||
|
||||
if (s_cache->Size != 0)
|
||||
{
|
||||
popup = s_cache->GetAt(0);
|
||||
s_cache->RemoveAt(0);
|
||||
|
||||
rectangle = safe_cast<::Windows::UI::Xaml::Shapes::Rectangle^>(popup->Child);
|
||||
}
|
||||
else
|
||||
{
|
||||
popup = ref new Popup();
|
||||
|
||||
rectangle = ref new ::Windows::UI::Xaml::Shapes::Rectangle();
|
||||
rectangle->IsHitTestVisible = false;
|
||||
|
||||
popup->Child = rectangle;
|
||||
}
|
||||
|
||||
rectangle->StrokeThickness = GazeInput::DwellStrokeThickness;
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
void GazeFeedbackPopupFactory::Return(Popup^ popup)
|
||||
{
|
||||
popup->IsOpen = false;
|
||||
s_cache->Append(popup);
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,49 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class GazeFeedbackPopupFactory
|
||||
{
|
||||
private readonly List<Popup> _cache = new List<Popup>();
|
||||
|
||||
public Popup Get()
|
||||
{
|
||||
Popup popup;
|
||||
Windows.UI.Xaml.Shapes.Rectangle rectangle;
|
||||
|
||||
if (_cache.Count != 0)
|
||||
{
|
||||
popup = _cache[0];
|
||||
_cache.RemoveAt(0);
|
||||
|
||||
rectangle = popup.Child as Windows.UI.Xaml.Shapes.Rectangle;
|
||||
}
|
||||
else
|
||||
{
|
||||
popup = new Popup();
|
||||
|
||||
rectangle = new Windows.UI.Xaml.Shapes.Rectangle
|
||||
{
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
|
||||
popup.Child = rectangle;
|
||||
}
|
||||
|
||||
rectangle.StrokeThickness = GazeInput.DwellStrokeThickness;
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
public void Return(Popup popup)
|
||||
{
|
||||
popup.IsOpen = false;
|
||||
_cache.Add(popup);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
using namespace Platform::Collections;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
private ref class GazeFeedbackPopupFactory
|
||||
{
|
||||
private:
|
||||
|
||||
Vector<Popup^>^ s_cache = ref new Vector<Popup^>();
|
||||
|
||||
public:
|
||||
|
||||
Popup^ Get();
|
||||
|
||||
void Return(Popup^ popup);
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,35 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// This struct encapsulates the location and timestamp associated with the user's gaze
|
||||
/// and is used as an input and output parameter for the IGazeFilter.Update method
|
||||
/// </summary>
|
||||
internal struct GazeFilterArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current point in the gaze stream
|
||||
/// </summary>
|
||||
public Point Location => _location;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp associated with the current point
|
||||
/// </summary>
|
||||
public TimeSpan Timestamp => _timestamp;
|
||||
|
||||
internal GazeFilterArgs(Point location, TimeSpan timestamp)
|
||||
{
|
||||
_location = location;
|
||||
_timestamp = timestamp;
|
||||
}
|
||||
|
||||
private Point _location;
|
||||
private TimeSpan _timestamp;
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeHidParsers.h"
|
||||
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
namespace GazeHidParsers {
|
||||
static HidNumericControlDescription ^ GetGazeUsageFromCollectionId(
|
||||
GazeDevicePreview ^ gazeDevice,
|
||||
uint16 childUsageId,
|
||||
uint16 parentUsageId)
|
||||
{
|
||||
IVectorView<HidNumericControlDescription ^> ^ numericControls = gazeDevice->GetNumericControlDescriptions(
|
||||
(USHORT)GazeHidUsages::UsagePage_EyeHeadTracker, childUsageId);
|
||||
|
||||
for (unsigned int i = 0; i < numericControls->Size; i++)
|
||||
{
|
||||
auto parentCollections = numericControls->GetAt(i)->ParentCollections;
|
||||
if (parentCollections->Size > 0 &&
|
||||
parentCollections->GetAt(0)->UsagePage == (USHORT)GazeHidUsages::UsagePage_EyeHeadTracker &&
|
||||
parentCollections->GetAt(0)->UsageId == parentUsageId)
|
||||
{
|
||||
return numericControls->GetAt(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#pragma region GazeHidPositionParser
|
||||
GazeHidPositionParser::GazeHidPositionParser(GazeDevicePreview ^ gazeDevice, uint16 usage)
|
||||
{
|
||||
_usage = usage;
|
||||
|
||||
// Find all the position usages from the device's
|
||||
// descriptor and store them for easy access
|
||||
_X = GetGazeUsageFromCollectionId(gazeDevice, (USHORT)GazeHidUsages::Usage_PositionX, _usage);
|
||||
_Y = GetGazeUsageFromCollectionId(gazeDevice, (USHORT)GazeHidUsages::Usage_PositionY, _usage);
|
||||
_Z = GetGazeUsageFromCollectionId(gazeDevice, (USHORT)GazeHidUsages::Usage_PositionZ, _usage);
|
||||
}
|
||||
|
||||
GazeHidPosition^ GazeHidPositionParser::GetPosition(HidInputReport ^ report)
|
||||
{
|
||||
GazeHidPosition^ result = nullptr;
|
||||
|
||||
if (_X != nullptr &&
|
||||
_Y != nullptr &&
|
||||
_Z != nullptr &&
|
||||
_usage != 0x0000)
|
||||
{
|
||||
auto descX = report->GetNumericControlByDescription(_X);
|
||||
auto descY = report->GetNumericControlByDescription(_Y);
|
||||
auto descZ = report->GetNumericControlByDescription(_Z);
|
||||
|
||||
auto controlDescX = descX->ControlDescription;
|
||||
auto controlDescY = descY->ControlDescription;
|
||||
auto controlDescZ = descZ->ControlDescription;
|
||||
|
||||
if ((controlDescX->LogicalMaximum < descX->ScaledValue || controlDescX->LogicalMinimum > descX->ScaledValue) ||
|
||||
(controlDescY->LogicalMaximum < descY->ScaledValue || controlDescY->LogicalMinimum > descY->ScaledValue) ||
|
||||
(controlDescZ->LogicalMaximum < descZ->ScaledValue || controlDescZ->LogicalMinimum > descZ->ScaledValue))
|
||||
{
|
||||
// One of the values is outside of the valid range.
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ref new GazeHidPosition();
|
||||
result->X = descX->ScaledValue;
|
||||
result->Y = descY->ScaledValue;
|
||||
result->Z = descZ->ScaledValue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#pragma endregion GazeHidPositionParser
|
||||
|
||||
#pragma region GazeHidRotationParser
|
||||
GazeHidRotationParser::GazeHidRotationParser(GazeDevicePreview ^ gazeDevice, uint16 usage)
|
||||
{
|
||||
_usage = usage;
|
||||
|
||||
// Find all the rotation usages from the device's
|
||||
// descriptor and store them for easy access
|
||||
_X = GetGazeUsageFromCollectionId(gazeDevice, (USHORT)GazeHidUsages::Usage_RotationX, _usage);
|
||||
_Y = GetGazeUsageFromCollectionId(gazeDevice, (USHORT)GazeHidUsages::Usage_RotationY, _usage);
|
||||
_Z = GetGazeUsageFromCollectionId(gazeDevice, (USHORT)GazeHidUsages::Usage_RotationZ, _usage);
|
||||
}
|
||||
|
||||
GazeHidPosition^ GazeHidRotationParser::GetRotation(HidInputReport ^ report)
|
||||
{
|
||||
GazeHidPosition^ result = nullptr;
|
||||
|
||||
if (_X != nullptr &&
|
||||
_Y != nullptr &&
|
||||
_Z != nullptr &&
|
||||
_usage != 0x0000)
|
||||
{
|
||||
auto descX = report->GetNumericControlByDescription(_X);
|
||||
auto descY = report->GetNumericControlByDescription(_Y);
|
||||
auto descZ = report->GetNumericControlByDescription(_Z);
|
||||
|
||||
auto controlDescX = descX->ControlDescription;
|
||||
auto controlDescY = descY->ControlDescription;
|
||||
auto controlDescZ = descZ->ControlDescription;
|
||||
|
||||
if ((controlDescX->LogicalMaximum < descX->ScaledValue || controlDescX->LogicalMinimum > descX->ScaledValue) ||
|
||||
(controlDescY->LogicalMaximum < descY->ScaledValue || controlDescY->LogicalMinimum > descY->ScaledValue) ||
|
||||
(controlDescZ->LogicalMaximum < descZ->ScaledValue || controlDescZ->LogicalMinimum > descZ->ScaledValue))
|
||||
{
|
||||
// One of the values is outside of the valid range.
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ref new GazeHidPosition();
|
||||
result->X = descX->ScaledValue;
|
||||
result->Y = descY->ScaledValue;
|
||||
result->Z = descZ->ScaledValue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#pragma endregion GazeHidRotationParser
|
||||
|
||||
#pragma region GazeHidPositionsParser
|
||||
GazeHidPositionsParser::GazeHidPositionsParser(GazeDevicePreview ^ gazeDevice)
|
||||
{
|
||||
_leftEyePositionParser = ref new GazeHidPositionParser(gazeDevice, (USHORT)GazeHidUsages::Usage_LeftEyePosition);
|
||||
_rightEyePositionParser = ref new GazeHidPositionParser(gazeDevice, (USHORT)GazeHidUsages::Usage_RightEyePosition);
|
||||
_headPositionParser = ref new GazeHidPositionParser(gazeDevice, (USHORT)GazeHidUsages::Usage_HeadPosition);
|
||||
_headRotationParser = ref new GazeHidRotationParser(gazeDevice, (USHORT)GazeHidUsages::Usage_HeadDirectionPoint);
|
||||
}
|
||||
|
||||
GazeHidPositions ^ GazeHidPositionsParser::GetGazeHidPositions(HidInputReport ^ report)
|
||||
{
|
||||
auto retval = ref new GazeHidPositions();
|
||||
|
||||
retval->LeftEyePosition = _leftEyePositionParser->GetPosition(report);
|
||||
retval->RightEyePosition = _rightEyePositionParser->GetPosition(report);
|
||||
retval->HeadPosition = _headPositionParser->GetPosition(report);
|
||||
retval->HeadRotation = _headRotationParser->GetRotation(report);
|
||||
|
||||
return retval;
|
||||
}
|
||||
#pragma endregion GazeHidPositionsParser
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -1,74 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GazeHidUsages.h"
|
||||
|
||||
using namespace Windows::Devices::HumanInterfaceDevice;
|
||||
using namespace Windows::Devices::Input::Preview;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
namespace GazeHidParsers {
|
||||
public ref class GazeHidPosition sealed
|
||||
{
|
||||
public:
|
||||
property long long X;
|
||||
property long long Y;
|
||||
property long long Z;
|
||||
};
|
||||
|
||||
public ref class GazeHidPositions sealed
|
||||
{
|
||||
public:
|
||||
property GazeHidPosition^ LeftEyePosition;
|
||||
property GazeHidPosition^ RightEyePosition;
|
||||
property GazeHidPosition^ HeadPosition;
|
||||
property GazeHidPosition^ HeadRotation;
|
||||
};
|
||||
|
||||
public ref class GazeHidPositionParser sealed
|
||||
{
|
||||
public:
|
||||
GazeHidPositionParser(GazeDevicePreview ^ gazeDevice, uint16 usage);
|
||||
|
||||
GazeHidPosition^ GetPosition(HidInputReport ^ report);
|
||||
|
||||
private:
|
||||
HidNumericControlDescription ^ _X = nullptr;
|
||||
HidNumericControlDescription ^ _Y = nullptr;
|
||||
HidNumericControlDescription ^ _Z = nullptr;
|
||||
uint16 _usage = 0x0000;
|
||||
};
|
||||
|
||||
public ref class GazeHidRotationParser sealed
|
||||
{
|
||||
public:
|
||||
GazeHidRotationParser(GazeDevicePreview ^ gazeDevice, uint16 usage);
|
||||
|
||||
GazeHidPosition^ GetRotation(HidInputReport^ report);
|
||||
|
||||
private:
|
||||
HidNumericControlDescription ^ _X = nullptr;
|
||||
HidNumericControlDescription ^ _Y = nullptr;
|
||||
HidNumericControlDescription ^ _Z = nullptr;
|
||||
uint16 _usage = 0x0000;
|
||||
};
|
||||
|
||||
public ref class GazeHidPositionsParser sealed
|
||||
{
|
||||
public:
|
||||
GazeHidPositionsParser(GazeDevicePreview ^ gazeDevice);
|
||||
|
||||
GazeHidPositions^ GetGazeHidPositions(HidInputReport ^ report);
|
||||
|
||||
private:
|
||||
GazeHidPositionParser ^ _leftEyePositionParser;
|
||||
GazeHidPositionParser ^ _rightEyePositionParser;
|
||||
GazeHidPositionParser ^ _headPositionParser;
|
||||
GazeHidRotationParser ^ _headRotationParser;
|
||||
};
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,34 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.Devices.HumanInterfaceDevice;
|
||||
using Windows.Devices.Input.Preview;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction.GazeHidParsers
|
||||
{
|
||||
internal static class GazeHidParsersHelpers
|
||||
{
|
||||
public static HidNumericControlDescription GetGazeUsageFromCollectionId(
|
||||
GazeDevicePreview gazeDevice,
|
||||
ushort childUsageId,
|
||||
ushort parentUsageId)
|
||||
{
|
||||
var numericControls = gazeDevice.GetNumericControlDescriptions(
|
||||
(ushort)GazeHidUsages.UsagePage_EyeHeadTracker, childUsageId);
|
||||
|
||||
for (int i = 0; i < numericControls.Count; i++)
|
||||
{
|
||||
var parentCollections = numericControls[i].ParentCollections;
|
||||
if (parentCollections.Count > 0 &&
|
||||
parentCollections[0].UsagePage == (ushort)GazeHidUsages.UsagePage_EyeHeadTracker &&
|
||||
parentCollections[0].UsageId == parentUsageId)
|
||||
{
|
||||
return numericControls[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction.GazeHidParsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents one Hid position
|
||||
/// </summary>
|
||||
public class GazeHidPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the X axis of this position
|
||||
/// </summary>
|
||||
public long X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Y axis of this position
|
||||
/// </summary>
|
||||
public long Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Z axis of this position
|
||||
/// </summary>
|
||||
public long Z { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.Devices.HumanInterfaceDevice;
|
||||
using Windows.Devices.Input.Preview;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction.GazeHidParsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Hid Position Parser
|
||||
/// </summary>
|
||||
public class GazeHidPositionParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GazeHidPositionParser"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gazeDevice"><see cref="GazeDevicePreview"/> used to parse.</param>
|
||||
/// <param name="usage">The <see cref="GazeHidUsages"/> used to parse.</param>
|
||||
public GazeHidPositionParser(GazeDevicePreview gazeDevice, ushort usage)
|
||||
{
|
||||
_usage = usage;
|
||||
|
||||
// Find all the position usages from the device's
|
||||
// descriptor and store them for easy access
|
||||
_x = GazeHidParsersHelpers.GetGazeUsageFromCollectionId(gazeDevice, (ushort)GazeHidUsages.Usage_PositionX, _usage);
|
||||
_y = GazeHidParsersHelpers.GetGazeUsageFromCollectionId(gazeDevice, (ushort)GazeHidUsages.Usage_PositionY, _usage);
|
||||
_z = GazeHidParsersHelpers.GetGazeUsageFromCollectionId(gazeDevice, (ushort)GazeHidUsages.Usage_PositionZ, _usage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the position from the report.
|
||||
/// </summary>
|
||||
/// <param name="report">A <see cref="HidInputReport"/> object used on the parsing.</param>
|
||||
/// <returns>The parsed <see cref="GazeHidPositions"/> from the report.</returns>
|
||||
public GazeHidPosition GetPosition(HidInputReport report)
|
||||
{
|
||||
GazeHidPosition result = null;
|
||||
|
||||
if (_x != null &&
|
||||
_y != null &&
|
||||
_z != null &&
|
||||
_usage != 0x0000)
|
||||
{
|
||||
var descX = report.GetNumericControlByDescription(_x);
|
||||
var descY = report.GetNumericControlByDescription(_y);
|
||||
var descZ = report.GetNumericControlByDescription(_z);
|
||||
|
||||
var controlDescX = descX.ControlDescription;
|
||||
var controlDescY = descY.ControlDescription;
|
||||
var controlDescZ = descZ.ControlDescription;
|
||||
|
||||
if ((controlDescX.LogicalMaximum < descX.ScaledValue || controlDescX.LogicalMinimum > descX.ScaledValue) ||
|
||||
(controlDescY.LogicalMaximum < descY.ScaledValue || controlDescY.LogicalMinimum > descY.ScaledValue) ||
|
||||
(controlDescZ.LogicalMaximum < descZ.ScaledValue || controlDescZ.LogicalMinimum > descZ.ScaledValue))
|
||||
{
|
||||
// One of the values is outside of the valid range.
|
||||
}
|
||||
else
|
||||
{
|
||||
result = new GazeHidPosition
|
||||
{
|
||||
X = descX.ScaledValue,
|
||||
Y = descY.ScaledValue,
|
||||
Z = descZ.ScaledValue
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private readonly HidNumericControlDescription _x = null;
|
||||
private readonly HidNumericControlDescription _y = null;
|
||||
private readonly HidNumericControlDescription _z = null;
|
||||
private readonly ushort _usage = 0x0000;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction.GazeHidParsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Hid positions
|
||||
/// </summary>
|
||||
public class GazeHidPositions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the left eye position
|
||||
/// </summary>
|
||||
public GazeHidPosition LeftEyePosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the right eye position
|
||||
/// </summary>
|
||||
public GazeHidPosition RightEyePosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the head position
|
||||
/// </summary>
|
||||
public GazeHidPosition HeadPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the head rotation
|
||||
/// </summary>
|
||||
public GazeHidPosition HeadRotation { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.Devices.HumanInterfaceDevice;
|
||||
using Windows.Devices.Input.Preview;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction.GazeHidParsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Hid Positions Parser
|
||||
/// </summary>
|
||||
public class GazeHidPositionsParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GazeHidPositionsParser"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gazeDevice"><see cref="GazeDevicePreview"/> used to parse.</param>
|
||||
public GazeHidPositionsParser(GazeDevicePreview gazeDevice)
|
||||
{
|
||||
_leftEyePositionParser = new GazeHidPositionParser(gazeDevice, (ushort)GazeHidUsages.Usage_LeftEyePosition);
|
||||
_rightEyePositionParser = new GazeHidPositionParser(gazeDevice, (ushort)GazeHidUsages.Usage_RightEyePosition);
|
||||
_headPositionParser = new GazeHidPositionParser(gazeDevice, (ushort)GazeHidUsages.Usage_HeadPosition);
|
||||
_headRotationParser = new GazeHidRotationParser(gazeDevice, (ushort)GazeHidUsages.Usage_HeadDirectionPoint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the positions from the report.
|
||||
/// </summary>
|
||||
/// <param name="report">A <see cref="HidInputReport"/> object used on the parsing.</param>
|
||||
/// <returns>The parsed <see cref="GazeHidPositions"/> from the report.</returns>
|
||||
public GazeHidPositions GetGazeHidPositions(HidInputReport report)
|
||||
{
|
||||
return new GazeHidPositions
|
||||
{
|
||||
LeftEyePosition = this._leftEyePositionParser.GetPosition(report),
|
||||
RightEyePosition = this._rightEyePositionParser.GetPosition(report),
|
||||
HeadPosition = this._headPositionParser.GetPosition(report),
|
||||
HeadRotation = this._headRotationParser.GetRotation(report)
|
||||
};
|
||||
}
|
||||
|
||||
private readonly GazeHidPositionParser _leftEyePositionParser;
|
||||
private readonly GazeHidPositionParser _rightEyePositionParser;
|
||||
private readonly GazeHidPositionParser _headPositionParser;
|
||||
private readonly GazeHidRotationParser _headRotationParser;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.Devices.HumanInterfaceDevice;
|
||||
using Windows.Devices.Input.Preview;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction.GazeHidParsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Hid Rotation Parser
|
||||
/// </summary>
|
||||
public class GazeHidRotationParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GazeHidRotationParser"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gazeDevice"><see cref="GazeDevicePreview"/> used to parse.</param>
|
||||
/// <param name="usage">The <see cref="GazeHidUsages"/> used to parse.</param>
|
||||
public GazeHidRotationParser(GazeDevicePreview gazeDevice, ushort usage)
|
||||
{
|
||||
_usage = usage;
|
||||
|
||||
// Find all the rotation usages from the device's
|
||||
// descriptor and store them for easy access
|
||||
_x = GazeHidParsersHelpers.GetGazeUsageFromCollectionId(gazeDevice, (ushort)GazeHidUsages.Usage_RotationX, _usage);
|
||||
_y = GazeHidParsersHelpers.GetGazeUsageFromCollectionId(gazeDevice, (ushort)GazeHidUsages.Usage_RotationY, _usage);
|
||||
_z = GazeHidParsersHelpers.GetGazeUsageFromCollectionId(gazeDevice, (ushort)GazeHidUsages.Usage_RotationZ, _usage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the rotation from the report.
|
||||
/// </summary>
|
||||
/// <param name="report">A <see cref="HidInputReport"/> object used on the parsing.</param>
|
||||
/// <returns>The parsed <see cref="GazeHidPosition"/> from the report.</returns>
|
||||
public GazeHidPosition GetRotation(HidInputReport report)
|
||||
{
|
||||
GazeHidPosition result = null;
|
||||
|
||||
if (_x != null &&
|
||||
_y != null &&
|
||||
_z != null &&
|
||||
_usage != 0x0000)
|
||||
{
|
||||
var descX = report.GetNumericControlByDescription(_x);
|
||||
var descY = report.GetNumericControlByDescription(_y);
|
||||
var descZ = report.GetNumericControlByDescription(_z);
|
||||
|
||||
var controlDescX = descX.ControlDescription;
|
||||
var controlDescY = descY.ControlDescription;
|
||||
var controlDescZ = descZ.ControlDescription;
|
||||
|
||||
if ((controlDescX.LogicalMaximum < descX.ScaledValue || controlDescX.LogicalMinimum > descX.ScaledValue) ||
|
||||
(controlDescY.LogicalMaximum < descY.ScaledValue || controlDescY.LogicalMinimum > descY.ScaledValue) ||
|
||||
(controlDescZ.LogicalMaximum < descZ.ScaledValue || controlDescZ.LogicalMinimum > descZ.ScaledValue))
|
||||
{
|
||||
// One of the values is outside of the valid range.
|
||||
}
|
||||
else
|
||||
{
|
||||
result = new GazeHidPosition
|
||||
{
|
||||
X = descX.ScaledValue,
|
||||
Y = descY.ScaledValue,
|
||||
Z = descZ.ScaledValue
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private readonly HidNumericControlDescription _x = null;
|
||||
private readonly HidNumericControlDescription _y = null;
|
||||
private readonly HidNumericControlDescription _z = null;
|
||||
private readonly ushort _usage = 0x0000;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// This enum specifies the various HID usages specified by the EyeHeadTracker HID specification
|
||||
///
|
||||
/// https://www.usb.org/sites/default/files/hutrr74_-_usage_page_for_head_and_eye_trackers_0.pdf
|
||||
/// </summary>
|
||||
public enum GazeHidUsages
|
||||
{
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
UsagePage_EyeHeadTracker = 0x0012,
|
||||
Usage_EyeTracker = 0x0001,
|
||||
Usage_HeadTracker = 0x0002,
|
||||
|
||||
// 0x0003-0x000F RESERVED
|
||||
Usage_TrackingData = 0x0010,
|
||||
Usage_Capabilities = 0x0011,
|
||||
Usage_Configuration = 0x0012,
|
||||
Usage_Status = 0x0013,
|
||||
Usage_Control = 0x0014,
|
||||
|
||||
// 0x0015-0x001F RESERVED
|
||||
Usage_Timestamp = 0x0020,
|
||||
Usage_PositionX = 0x0021,
|
||||
Usage_PositionY = 0x0022,
|
||||
Usage_PositionZ = 0x0023,
|
||||
Usage_GazePoint = 0x0024,
|
||||
Usage_LeftEyePosition = 0x0025,
|
||||
Usage_RightEyePosition = 0x0026,
|
||||
Usage_HeadPosition = 0x0027,
|
||||
Usage_HeadDirectionPoint = 0x0028,
|
||||
Usage_RotationX = 0x0029,
|
||||
Usage_RotationY = 0x002A,
|
||||
Usage_RotationZ = 0x002B,
|
||||
|
||||
// 0x002C-0x00FF RESERVED
|
||||
Usage_TrackerQuality = 0x0100,
|
||||
Usage_MinimumTrackingDistance = 0x0101,
|
||||
Usage_OptimumTrackingDistance = 0x0102,
|
||||
Usage_MaximumTrackingDistance = 0x0103,
|
||||
Usage_MaximumScreenPlaneWidth = 0x0104,
|
||||
Usage_MaximumScreenPlaneHeight = 0x0105,
|
||||
|
||||
// 0x0106-0x01FF RESERVED
|
||||
Usage_DisplayManufacturerId = 0x0200,
|
||||
Usage_DisplayProductId = 0x0201,
|
||||
Usage_DisplaySerialNumber = 0x0202,
|
||||
Usage_DisplayManufacturerDate = 0x0203,
|
||||
Usage_CalibratedScreenWidth = 0x0204,
|
||||
Usage_CalibratedScreenHeight = 0x0205,
|
||||
|
||||
// 0x0206-0x02FF RESERVED
|
||||
Usage_SamplingFrequency = 0x0300,
|
||||
Usage_ConfigurationStatus = 0x0301,
|
||||
|
||||
// 0x0302-0x03FF RESERVED
|
||||
Usage_DeviceModeRequest = 0x0400,
|
||||
|
||||
// 0x0401-0xFFFF RESERVED
|
||||
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This enum specifies the various HID usages specified by the EyeHeadTracker HID specification
|
||||
///
|
||||
/// https://www.usb.org/sites/default/files/hutrr74_-_usage_page_for_head_and_eye_trackers_0.pdf
|
||||
/// </summary>
|
||||
public enum class GazeHidUsages
|
||||
{
|
||||
UsagePage_EyeHeadTracker = 0x0012,
|
||||
Usage_EyeTracker = 0x0001,
|
||||
Usage_HeadTracker = 0x0002,
|
||||
// 0x0003-0x000F RESERVED
|
||||
Usage_TrackingData = 0x0010,
|
||||
Usage_Capabilities = 0x0011,
|
||||
Usage_Configuration = 0x0012,
|
||||
Usage_Status = 0x0013,
|
||||
Usage_Control = 0x0014,
|
||||
// 0x0015-0x001F RESERVED
|
||||
Usage_Timestamp = 0x0020,
|
||||
Usage_PositionX = 0x0021,
|
||||
Usage_PositionY = 0x0022,
|
||||
Usage_PositionZ = 0x0023,
|
||||
Usage_GazePoint = 0x0024,
|
||||
Usage_LeftEyePosition = 0x0025,
|
||||
Usage_RightEyePosition = 0x0026,
|
||||
Usage_HeadPosition = 0x0027,
|
||||
Usage_HeadDirectionPoint = 0x0028,
|
||||
Usage_RotationX = 0x0029,
|
||||
Usage_RotationY = 0x002A,
|
||||
Usage_RotationZ = 0x002B,
|
||||
// 0x002C-0x00FF RESERVED
|
||||
Usage_TrackerQuality = 0x0100,
|
||||
Usage_MinimumTrackingDistance = 0x0101,
|
||||
Usage_OptimumTrackingDistance = 0x0102,
|
||||
Usage_MaximumTrackingDistance = 0x0103,
|
||||
Usage_MaximumScreenPlaneWidth = 0x0104,
|
||||
Usage_MaximumScreenPlaneHeight = 0x0105,
|
||||
// 0x0106-0x01FF RESERVED
|
||||
Usage_DisplayManufacturerId = 0x0200,
|
||||
Usage_DisplayProductId = 0x0201,
|
||||
Usage_DisplaySerialNumber = 0x0202,
|
||||
Usage_DisplayManufacturerDate = 0x0203,
|
||||
Usage_CalibratedScreenWidth = 0x0204,
|
||||
Usage_CalibratedScreenHeight = 0x0205,
|
||||
// 0x0206-0x02FF RESERVED
|
||||
Usage_SamplingFrequency = 0x0300,
|
||||
Usage_ConfigurationStatus = 0x0301,
|
||||
// 0x0302-0x03FF RESERVED
|
||||
Usage_DeviceModeRequest = 0x0400,
|
||||
// 0x0401-0xFFFF RESERVED
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,17 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal struct GazeHistoryItem
|
||||
{
|
||||
public GazeTargetItem HitTarget { get; set; }
|
||||
|
||||
public TimeSpan Timestamp { get; set; }
|
||||
|
||||
public TimeSpan Duration { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
ref class GazeTargetItem;
|
||||
|
||||
private ref struct GazeHistoryItem
|
||||
{
|
||||
property GazeTargetItem^ HitTarget;
|
||||
property TimeSpan Timestamp;
|
||||
property TimeSpan Duration;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -1,187 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeInput.h"
|
||||
|
||||
#include "GazeElement.h"
|
||||
#include "GazePointer.h"
|
||||
#include "GazePointerProxy.h"
|
||||
#include "GazeTargetItem.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::UI;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
Brush^ GazeInput::DwellFeedbackEnterBrush::get()
|
||||
{
|
||||
return GazePointer::Instance->_enterBrush;
|
||||
}
|
||||
|
||||
void GazeInput::DwellFeedbackEnterBrush::set(Brush^ value)
|
||||
{
|
||||
GazePointer::Instance->_enterBrush = value;
|
||||
}
|
||||
|
||||
Brush^ GazeInput::DwellFeedbackProgressBrush::get()
|
||||
{
|
||||
return GazePointer::Instance->_progressBrush;
|
||||
}
|
||||
|
||||
void GazeInput::DwellFeedbackProgressBrush::set(Brush^ value)
|
||||
{
|
||||
GazePointer::Instance->_progressBrush = value;
|
||||
}
|
||||
|
||||
Brush^ GazeInput::DwellFeedbackCompleteBrush::get()
|
||||
{
|
||||
return GazePointer::Instance->_completeBrush;
|
||||
}
|
||||
|
||||
void GazeInput::DwellFeedbackCompleteBrush::set(Brush^ value)
|
||||
{
|
||||
GazePointer::Instance->_completeBrush = value;
|
||||
}
|
||||
|
||||
double GazeInput::DwellStrokeThickness::get()
|
||||
{
|
||||
return GazePointer::Instance->_dwellStrokeThickness;
|
||||
}
|
||||
|
||||
void GazeInput::DwellStrokeThickness::set(double value)
|
||||
{
|
||||
GazePointer::Instance->_dwellStrokeThickness = value;
|
||||
}
|
||||
|
||||
Interaction GazeInput::Interaction::get()
|
||||
{
|
||||
return GazePointer::Instance->_interaction;
|
||||
}
|
||||
|
||||
void GazeInput::Interaction::set(GazeInteraction::Interaction value)
|
||||
{
|
||||
if (GazePointer::Instance->_interaction != value)
|
||||
{
|
||||
if (value == GazeInteraction::Interaction::Enabled)
|
||||
{
|
||||
GazePointer::Instance->AddRoot(0);
|
||||
}
|
||||
else if (GazePointer::Instance->_interaction == GazeInteraction::Interaction::Enabled)
|
||||
{
|
||||
GazePointer::Instance->RemoveRoot(0);
|
||||
}
|
||||
|
||||
GazePointer::Instance->_interaction = value;
|
||||
}
|
||||
}
|
||||
|
||||
TimeSpan GazeInput::UnsetTimeSpan = { -1 };
|
||||
|
||||
static void OnInteractionChanged(DependencyObject^ ob, DependencyPropertyChangedEventArgs^ args)
|
||||
{
|
||||
auto element = safe_cast<FrameworkElement^>(ob);
|
||||
auto interaction = safe_cast<Interaction>(args->NewValue);
|
||||
GazePointerProxy::SetInteraction(element, interaction);
|
||||
}
|
||||
|
||||
static void OnIsCursorVisibleChanged(DependencyObject^ ob, DependencyPropertyChangedEventArgs^ args)
|
||||
{
|
||||
GazePointer::Instance->IsCursorVisible = safe_cast<bool>(args->NewValue);
|
||||
}
|
||||
|
||||
static void OnCursorRadiusChanged(DependencyObject^ ob, DependencyPropertyChangedEventArgs^ args)
|
||||
{
|
||||
GazePointer::Instance->CursorRadius = safe_cast<int>(args->NewValue);
|
||||
}
|
||||
|
||||
static void OnIsSwitchEnabledChanged(DependencyObject^ ob, DependencyPropertyChangedEventArgs^ args)
|
||||
{
|
||||
GazePointer::Instance->IsSwitchEnabled = safe_cast<bool>(args->NewValue);
|
||||
}
|
||||
|
||||
static DependencyProperty^ s_interactionProperty = DependencyProperty::RegisterAttached("Interaction", Interaction::typeid, GazeInput::typeid,
|
||||
ref new PropertyMetadata(Interaction::Inherited, ref new PropertyChangedCallback(&OnInteractionChanged)));
|
||||
static DependencyProperty^ s_isCursorVisibleProperty = DependencyProperty::RegisterAttached("IsCursorVisible", bool::typeid, GazeInput::typeid,
|
||||
ref new PropertyMetadata(true, ref new PropertyChangedCallback(&OnIsCursorVisibleChanged)));
|
||||
static DependencyProperty^ s_cursorRadiusProperty = DependencyProperty::RegisterAttached("CursorRadius", int::typeid, GazeInput::typeid,
|
||||
ref new PropertyMetadata(6, ref new PropertyChangedCallback(&OnCursorRadiusChanged)));
|
||||
static DependencyProperty^ s_gazeElementProperty = DependencyProperty::RegisterAttached("GazeElement", GazeElement::typeid, GazeInput::typeid, ref new PropertyMetadata(nullptr));
|
||||
static DependencyProperty^ s_fixationDurationProperty = DependencyProperty::RegisterAttached("FixationDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_dwellDurationProperty = DependencyProperty::RegisterAttached("DwellDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_repeatDelayDurationProperty = DependencyProperty::RegisterAttached("RepeatDelayDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_dwellRepeatDurationProperty = DependencyProperty::RegisterAttached("DwellRepeatDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_thresholdDurationProperty = DependencyProperty::RegisterAttached("ThresholdDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_maxRepeatCountProperty = DependencyProperty::RegisterAttached("MaxDwellRepeatCount", int::typeid, GazeInput::typeid, ref new PropertyMetadata(safe_cast<Object^>(0)));
|
||||
static DependencyProperty^ s_isSwitchEnabledProperty = DependencyProperty::RegisterAttached("IsSwitchEnabled", bool::typeid, GazeInput::typeid,
|
||||
ref new PropertyMetadata(false, ref new PropertyChangedCallback(&OnIsSwitchEnabledChanged)));
|
||||
|
||||
DependencyProperty^ GazeInput::InteractionProperty::get() { return s_interactionProperty; }
|
||||
DependencyProperty^ GazeInput::IsCursorVisibleProperty::get() { return s_isCursorVisibleProperty; }
|
||||
DependencyProperty^ GazeInput::CursorRadiusProperty::get() { return s_cursorRadiusProperty; }
|
||||
DependencyProperty^ GazeInput::GazeElementProperty::get() { return s_gazeElementProperty; }
|
||||
DependencyProperty^ GazeInput::FixationDurationProperty::get() { return s_fixationDurationProperty; }
|
||||
DependencyProperty^ GazeInput::DwellDurationProperty::get() { return s_dwellDurationProperty; }
|
||||
DependencyProperty^ GazeInput::RepeatDelayDurationProperty::get() { return s_repeatDelayDurationProperty; }
|
||||
DependencyProperty^ GazeInput::DwellRepeatDurationProperty::get() { return s_dwellRepeatDurationProperty; }
|
||||
DependencyProperty^ GazeInput::ThresholdDurationProperty::get() { return s_thresholdDurationProperty; }
|
||||
DependencyProperty^ GazeInput::MaxDwellRepeatCountProperty::get() { return s_maxRepeatCountProperty; }
|
||||
DependencyProperty^ GazeInput::IsSwitchEnabledProperty::get() { return s_isSwitchEnabledProperty; }
|
||||
|
||||
Interaction GazeInput::GetInteraction(UIElement^ element) { return safe_cast<GazeInteraction::Interaction>(element->GetValue(s_interactionProperty)); }
|
||||
bool GazeInput::GetIsCursorVisible(UIElement^ element) { return safe_cast<bool>(element->GetValue(s_isCursorVisibleProperty)); }
|
||||
int GazeInput::GetCursorRadius(UIElement^ element) { return safe_cast<int>(element->GetValue(s_cursorRadiusProperty)); }
|
||||
GazeElement^ GazeInput::GetGazeElement(UIElement^ element) { return safe_cast<GazeElement^>(element->GetValue(s_gazeElementProperty)); }
|
||||
TimeSpan GazeInput::GetFixationDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_fixationDurationProperty)); }
|
||||
TimeSpan GazeInput::GetDwellDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_dwellDurationProperty)); }
|
||||
TimeSpan GazeInput::GetRepeatDelayDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_repeatDelayDurationProperty)); }
|
||||
TimeSpan GazeInput::GetDwellRepeatDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_dwellRepeatDurationProperty)); }
|
||||
TimeSpan GazeInput::GetThresholdDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_thresholdDurationProperty)); }
|
||||
int GazeInput::GetMaxDwellRepeatCount(UIElement^ element) { return safe_cast<int>(element->GetValue(s_maxRepeatCountProperty)); }
|
||||
bool GazeInput::GetIsSwitchEnabled(UIElement^ element) { return safe_cast<bool>(element->GetValue(s_isSwitchEnabledProperty)); }
|
||||
|
||||
void GazeInput::SetInteraction(UIElement^ element, GazeInteraction::Interaction value) { element->SetValue(s_interactionProperty, value); }
|
||||
void GazeInput::SetIsCursorVisible(UIElement^ element, bool value) { element->SetValue(s_isCursorVisibleProperty, value); }
|
||||
void GazeInput::SetCursorRadius(UIElement^ element, int value) { element->SetValue(s_cursorRadiusProperty, value); }
|
||||
void GazeInput::SetGazeElement(UIElement^ element, GazeElement^ value) { element->SetValue(s_gazeElementProperty, value); }
|
||||
void GazeInput::SetFixationDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_fixationDurationProperty, span); }
|
||||
void GazeInput::SetDwellDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_dwellDurationProperty, span); }
|
||||
void GazeInput::SetRepeatDelayDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_repeatDelayDurationProperty, span); }
|
||||
void GazeInput::SetDwellRepeatDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_dwellRepeatDurationProperty, span); }
|
||||
void GazeInput::SetThresholdDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_thresholdDurationProperty, span); }
|
||||
void GazeInput::SetMaxDwellRepeatCount(UIElement^ element, int value) { element->SetValue(s_maxRepeatCountProperty, value); }
|
||||
void GazeInput::SetIsSwitchEnabled(UIElement^ element, bool value) { element->SetValue(s_isSwitchEnabledProperty, value); }
|
||||
|
||||
GazePointer^ GazeInput::GetGazePointer(Page^ page)
|
||||
{
|
||||
return GazePointer::Instance;
|
||||
}
|
||||
|
||||
void GazeInput::Invoke(UIElement^ element)
|
||||
{
|
||||
auto item = GazeTargetItem::GetOrCreate(element);
|
||||
item->Invoke();
|
||||
}
|
||||
|
||||
void GazeInput::LoadSettings(ValueSet^ settings)
|
||||
{
|
||||
GazePointer::Instance->LoadSettings(settings);
|
||||
}
|
||||
|
||||
bool GazeInput::IsDeviceAvailable::get()
|
||||
{
|
||||
return GazePointer::Instance->IsDeviceAvailable;
|
||||
}
|
||||
|
||||
EventRegistrationToken GazeInput::IsDeviceAvailableChanged::add(EventHandler<Object^>^ handler)
|
||||
{
|
||||
return GazePointer::Instance->IsDeviceAvailableChanged += handler;
|
||||
}
|
||||
|
||||
void GazeInput::IsDeviceAvailableChanged::remove(EventRegistrationToken token)
|
||||
{
|
||||
GazePointer::Instance->IsDeviceAvailableChanged -= token;
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,432 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class primarily providing access to attached properties controlling gaze behavior.
|
||||
/// </summary>
|
||||
[Windows.Foundation.Metadata.WebHostHidden]
|
||||
public class GazeInput
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Interaction dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty InteractionProperty { get; } = DependencyProperty.RegisterAttached("Interaction", typeof(Interaction), typeof(GazeInput), new PropertyMetadata(Interaction.Inherited, new PropertyChangedCallback(OnInteractionChanged)));
|
||||
|
||||
private static void OnInteractionChanged(DependencyObject ob, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
var element = ob as FrameworkElement;
|
||||
var interaction = (Interaction)args.NewValue;
|
||||
GazePointerProxy.SetInteraction(element, interaction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the IsCursorVisible dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty IsCursorVisibleProperty { get; } = DependencyProperty.RegisterAttached("IsCursorVisible", typeof(bool), typeof(GazeInput), new PropertyMetadata(true, new PropertyChangedCallback(OnIsCursorVisibleChanged)));
|
||||
|
||||
private static void OnIsCursorVisibleChanged(DependencyObject ob, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
GazePointer.Instance.IsCursorVisible = (bool)args.NewValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CursorRadius dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty CursorRadiusProperty { get; } = DependencyProperty.RegisterAttached("CursorRadius", typeof(int), typeof(GazeInput), new PropertyMetadata(6, new PropertyChangedCallback(OnCursorRadiusChanged)));
|
||||
|
||||
private static void OnCursorRadiusChanged(DependencyObject ob, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
GazePointer.Instance.CursorRadius = (int)args.NewValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GazeElement dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty GazeElementProperty { get; } = DependencyProperty.RegisterAttached("GazeElement", typeof(GazeElement), typeof(GazeInput), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the FixationDuration dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty FixationDurationProperty { get; } = DependencyProperty.RegisterAttached("FixationDuration", typeof(TimeSpan), typeof(GazeInput), new PropertyMetadata(UnsetTimeSpan));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DwellDuration dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty DwellDurationProperty { get; } = DependencyProperty.RegisterAttached("DwellDuration", typeof(TimeSpan), typeof(GazeInput), new PropertyMetadata(UnsetTimeSpan));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RepeatDelayDuration dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty RepeatDelayDurationProperty { get; } = DependencyProperty.RegisterAttached("RepeatDelayDuration", typeof(TimeSpan), typeof(GazeInput), new PropertyMetadata(UnsetTimeSpan));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DwellRepeatDuration dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty DwellRepeatDurationProperty { get; } = DependencyProperty.RegisterAttached("DwellRepeatDuration", typeof(TimeSpan), typeof(GazeInput), new PropertyMetadata(UnsetTimeSpan));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ThresholdDuration dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty ThresholdDurationProperty { get; } = DependencyProperty.RegisterAttached("ThresholdDuration", typeof(TimeSpan), typeof(GazeInput), new PropertyMetadata(GazeInput.UnsetTimeSpan));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MaxDwellRepeatCount dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty MaxDwellRepeatCountProperty { get; } = DependencyProperty.RegisterAttached("MaxDwellRepeatCount", typeof(int), typeof(GazeInput), new PropertyMetadata(0));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the IsSwitchEnabled dependency property
|
||||
/// </summary>
|
||||
public static DependencyProperty IsSwitchEnabledProperty { get; } = DependencyProperty.RegisterAttached("IsSwitchEnabled", typeof(bool), typeof(GazeInput), new PropertyMetadata(false, new PropertyChangedCallback(OnIsSwitchEnabledChanged)));
|
||||
|
||||
private static void OnIsSwitchEnabledChanged(DependencyObject ob, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
GazePointer.Instance.IsSwitchEnabled = (bool)args.NewValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when displaying the default indication that gaze entered a control
|
||||
/// </summary>
|
||||
public static Brush DwellFeedbackEnterBrush
|
||||
{
|
||||
get
|
||||
{
|
||||
return GazePointer.Instance.EnterBrush;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
GazePointer.Instance.EnterBrush = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when displaying the default animation for dwell press
|
||||
/// </summary>
|
||||
public static Brush DwellFeedbackProgressBrush
|
||||
{
|
||||
get
|
||||
{
|
||||
return GazePointer.Instance.ProgressBrush;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
GazePointer.Instance.ProgressBrush = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when displaying the default animation for dwell complete
|
||||
/// </summary>
|
||||
public static Brush DwellFeedbackCompleteBrush
|
||||
{
|
||||
get
|
||||
{
|
||||
return GazePointer.Instance.CompleteBrush;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
GazePointer.Instance.CompleteBrush = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thickness of the lines animated for dwell.
|
||||
/// </summary>
|
||||
public static double DwellStrokeThickness
|
||||
{
|
||||
get
|
||||
{
|
||||
return GazePointer.Instance.DwellStrokeThickness;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
GazePointer.Instance.DwellStrokeThickness = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interaction default
|
||||
/// </summary>
|
||||
public static Interaction Interaction
|
||||
{
|
||||
get
|
||||
{
|
||||
return GazePointer.Instance.Interaction;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (GazePointer.Instance.Interaction != value)
|
||||
{
|
||||
if (value == GazeInteraction.Interaction.Enabled)
|
||||
{
|
||||
GazePointer.Instance.AddRoot(0);
|
||||
}
|
||||
else if (GazePointer.Instance.Interaction == GazeInteraction.Interaction.Enabled)
|
||||
{
|
||||
GazePointer.Instance.RemoveRoot(0);
|
||||
}
|
||||
|
||||
GazePointer.Instance.Interaction = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of gaze interaction over that particular XAML element.
|
||||
/// </summary>
|
||||
/// <returns>The status of gaze interaction over that particular XAML element.</returns>
|
||||
public static Interaction GetInteraction(UIElement element)
|
||||
{
|
||||
return (Interaction)element.GetValue(InteractionProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether cursor is shown while user is looking at the school.
|
||||
/// </summary>
|
||||
/// <returns>True the cursor is shown while user is looking at the school; otherwise, false.</returns>
|
||||
public static bool GetIsCursorVisible(UIElement element)
|
||||
{
|
||||
return (bool)element.GetValue(IsCursorVisibleProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the gaze cursor radius.
|
||||
/// </summary>
|
||||
/// <returns>The size of the gaze cursor radius.</returns>
|
||||
public static int GetCursorRadius(UIElement element)
|
||||
{
|
||||
return (int)element.GetValue(CursorRadiusProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GazeElement associated with an UIElement.
|
||||
/// </summary>
|
||||
/// <returns>The GazeElement associated with an UIElement.</returns>
|
||||
public static GazeElement GetGazeElement(UIElement element)
|
||||
{
|
||||
return (GazeElement)element.GetValue(GazeElementProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration for the control to transition from the Enter state to the Fixation state. At this point, a StateChanged event is fired with PointerState set to Fixation. This event should be used to control the earliest visual feedback the application needs to provide to the user about the gaze location. The default is 350ms.
|
||||
/// </summary>
|
||||
/// <returns>Duration for the control to transition from the Enter state to the Fixation state.</returns>
|
||||
public static TimeSpan GetFixationDuration(UIElement element)
|
||||
{
|
||||
return (TimeSpan)element.GetValue(FixationDurationProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration for the control to transition from the Fixation state to the Dwell state. At this point, a StateChanged event is fired with PointerState set to Dwell. The Enter and Fixation states are typicaly achieved too rapidly for the user to have much control over. In contrast Dwell is conscious event. This is the point at which the control is invoked, e.g. a button click. The application can modify this property to control when a gaze enabled UI element gets invoked after a user starts looking at it.
|
||||
/// </summary>
|
||||
/// <returns>The duration for the control to transition from the Fixation state to the Dwell state.</returns>
|
||||
public static TimeSpan GetDwellDuration(UIElement element)
|
||||
{
|
||||
return (TimeSpan)element.GetValue(DwellDurationProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the additional duration for the first repeat to occur. This prevents inadvertent repeated invocation.
|
||||
/// </summary>
|
||||
/// <returns>The additional duration for the first repeat to occur.</returns>
|
||||
public static TimeSpan GetRepeatDelayDuration(UIElement element)
|
||||
{
|
||||
return (TimeSpan)element.GetValue(RepeatDelayDurationProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration of repeated dwell invocations, should the user continue to dwell on the control. The first repeat will occur after an additional delay specified by RepeatDelayDuration. Subsequent repeats happen after every period of DwellRepeatDuration. A control is invoked repeatedly only if MaxDwellRepeatCount is set to greater than zero.
|
||||
/// </summary>
|
||||
/// <returns>The duration of repeated dwell invocations.</returns>
|
||||
public static TimeSpan GetDwellRepeatDuration(UIElement element)
|
||||
{
|
||||
return (TimeSpan)element.GetValue(DwellRepeatDurationProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration that controls when the PointerState moves to either the Enter state or the Exit state. When this duration has elapsed after the user's gaze first enters a control, the PointerState is set to Enter. And when this duration has elapsed after the user's gaze has left the control, the PointerState is set to Exit. In both cases, a StateChanged event is fired. The default is 50ms.
|
||||
/// </summary>
|
||||
/// <returns>The duration that controls when the PointerState moves to either the Enter state or the Exit state.</returns>
|
||||
public static TimeSpan GetThresholdDuration(UIElement element)
|
||||
{
|
||||
return (TimeSpan)element.GetValue(ThresholdDurationProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum times the control will invoked repeatedly without the user's gaze having to leave and re-enter the control. The default value is zero which disables repeated invocation of a control. Developers can set a higher value to enable repeated invocation.
|
||||
/// </summary>
|
||||
/// <returns>The maximum times the control will invoked repeatedly without the user's gaze having to leave and re-enter the control.</returns>
|
||||
public static int GetMaxDwellRepeatCount(UIElement element)
|
||||
{
|
||||
return (int)element.GetValue(MaxDwellRepeatCountProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether gaze plus switch is enabled.
|
||||
/// </summary>
|
||||
/// <returns>A boolean indicating whether gaze plus switch is enabled.</returns>
|
||||
public static bool GetIsSwitchEnabled(UIElement element)
|
||||
{
|
||||
return (bool)element.GetValue(IsSwitchEnabledProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the status of gaze interaction over that particular XAML element.
|
||||
/// </summary>
|
||||
public static void SetInteraction(UIElement element, Interaction value)
|
||||
{
|
||||
element.SetValue(InteractionProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether cursor is shown while user is looking at the school.
|
||||
/// </summary>
|
||||
public static void SetIsCursorVisible(UIElement element, bool value)
|
||||
{
|
||||
element.SetValue(IsCursorVisibleProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the size of the gaze cursor radius.
|
||||
/// </summary>
|
||||
public static void SetCursorRadius(UIElement element, int value)
|
||||
{
|
||||
element.SetValue(CursorRadiusProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the GazeElement associated with an UIElement.
|
||||
/// </summary>
|
||||
public static void SetGazeElement(UIElement element, GazeElement value)
|
||||
{
|
||||
element.SetValue(GazeElementProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration for the control to transition from the Enter state to the Fixation state. At this point, a StateChanged event is fired with PointerState set to Fixation. This event should be used to control the earliest visual feedback the application needs to provide to the user about the gaze location. The default is 350ms.
|
||||
/// </summary>
|
||||
public static void SetFixationDuration(UIElement element, TimeSpan span)
|
||||
{
|
||||
element.SetValue(FixationDurationProperty, span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration for the control to transition from the Fixation state to the Dwell state. At this point, a StateChanged event is fired with PointerState set to Dwell. The Enter and Fixation states are typicaly achieved too rapidly for the user to have much control over. In contrast Dwell is conscious event. This is the point at which the control is invoked, e.g. a button click. The application can modify this property to control when a gaze enabled UI element gets invoked after a user starts looking at it.
|
||||
/// </summary>
|
||||
public static void SetDwellDuration(UIElement element, TimeSpan span)
|
||||
{
|
||||
element.SetValue(DwellDurationProperty, span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the additional duration for the first repeat to occur.This prevents inadvertent repeated invocation.
|
||||
/// </summary>
|
||||
public static void SetRepeatDelayDuration(UIElement element, TimeSpan span)
|
||||
{
|
||||
element.SetValue(RepeatDelayDurationProperty, span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration of repeated dwell invocations, should the user continue to dwell on the control. The first repeat will occur after an additional delay specified by RepeatDelayDuration. Subsequent repeats happen after every period of DwellRepeatDuration. A control is invoked repeatedly only if MaxDwellRepeatCount is set to greater than zero.
|
||||
/// </summary>
|
||||
public static void SetDwellRepeatDuration(UIElement element, TimeSpan span)
|
||||
{
|
||||
element.SetValue(DwellRepeatDurationProperty, span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration that controls when the PointerState moves to either the Enter state or the Exit state. When this duration has elapsed after the user's gaze first enters a control, the PointerState is set to Enter. And when this duration has elapsed after the user's gaze has left the control, the PointerState is set to Exit. In both cases, a StateChanged event is fired. The default is 50ms.
|
||||
/// </summary>
|
||||
public static void SetThresholdDuration(UIElement element, TimeSpan span)
|
||||
{
|
||||
element.SetValue(ThresholdDurationProperty, span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum times the control will invoked repeatedly without the user's gaze having to leave and re-enter the control. The default value is zero which disables repeated invocation of a control. Developers can set a higher value to enable repeated invocation.
|
||||
/// </summary>
|
||||
public static void SetMaxDwellRepeatCount(UIElement element, int value)
|
||||
{
|
||||
element.SetValue(MaxDwellRepeatCountProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the boolean indicating whether gaze plus switch is enabled.
|
||||
/// </summary>
|
||||
public static void SetIsSwitchEnabled(UIElement element, bool value)
|
||||
{
|
||||
element.SetValue(IsSwitchEnabledProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GazePointer object.
|
||||
/// </summary>
|
||||
/// <returns>The GazePointer associated with that particular page.</returns>
|
||||
public static GazePointer GetGazePointer(Page page)
|
||||
{
|
||||
return GazePointer.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoke the default action of the specified UIElement.
|
||||
/// </summary>
|
||||
public static void Invoke(UIElement element)
|
||||
{
|
||||
var item = GazeTargetItem.GetOrCreate(element);
|
||||
item.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a gaze input device is available, and hence whether there is any possibility of gaze events occurring in the application.
|
||||
/// </summary>
|
||||
public static bool IsDeviceAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
return GazePointer.Instance.IsDeviceAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered whenever IsDeviceAvailable changes value.
|
||||
/// </summary>
|
||||
public static event EventHandler<object> IsDeviceAvailableChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
GazePointer.Instance.IsDeviceAvailableChanged += value;
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
GazePointer.Instance.IsDeviceAvailableChanged -= value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a settings collection into GazeInput.
|
||||
/// Note: This must be loaded from a UI thread to be valid, since the GazeInput
|
||||
/// instance is tied to the UI thread.
|
||||
/// </summary>
|
||||
public static void LoadSettings(ValueSet settings)
|
||||
{
|
||||
GazePointer.Instance.LoadSettings(settings);
|
||||
}
|
||||
|
||||
internal static readonly TimeSpan UnsetTimeSpan = new TimeSpan(-1);
|
||||
}
|
||||
}
|
|
@ -1,251 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Interaction.h"
|
||||
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
ref class GazeElement;
|
||||
ref class GazePointer;
|
||||
|
||||
/// <summary>
|
||||
/// Static class primarily providing access to attached properties controlling gaze behavior.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class GazeInput sealed
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the Interaction dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ InteractionProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the IsCursorVisible dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ IsCursorVisibleProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the CursorRadius dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ CursorRadiusProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the GazeElement dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ GazeElementProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the FixationDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ FixationDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the DwellDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ DwellDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the RepeatDelayDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ RepeatDelayDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the DwellRepeatDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ DwellRepeatDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the ThresholdDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ ThresholdDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the MaxDwellRepeatCount dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ MaxDwellRepeatCountProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the IsSwitchEnabled dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ IsSwitchEnabledProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when displaying the default indication that gaze entered a control
|
||||
/// </summary>
|
||||
static property Brush^ DwellFeedbackEnterBrush { Brush^ get(); void set(Brush^ value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when displaying the default animation for dwell press
|
||||
/// </summary>
|
||||
static property Brush^ DwellFeedbackProgressBrush { Brush^ get(); void set(Brush^ value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when displaying the default animation for dwell complete
|
||||
/// </summary>
|
||||
static property Brush^ DwellFeedbackCompleteBrush { Brush^ get(); void set(Brush^ value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thickness of the lines animated for dwell.
|
||||
/// </summary>
|
||||
static property double DwellStrokeThickness { double get(); void set(double value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interaction default
|
||||
/// </summary>
|
||||
static property GazeInteraction::Interaction Interaction { GazeInteraction::Interaction get(); void set(GazeInteraction::Interaction value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of gaze interaction over that particular XAML element.
|
||||
/// </summary>
|
||||
static GazeInteraction::Interaction GetInteraction(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets Boolean indicating whether cursor is shown while user is looking at the school.
|
||||
/// </summary>
|
||||
static bool GetIsCursorVisible(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the gaze cursor radius.
|
||||
/// </summary>
|
||||
static int GetCursorRadius(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GazeElement associated with an UIElement.
|
||||
/// </summary>
|
||||
static GazeElement^ GetGazeElement(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration for the control to transition from the Enter state to the Fixation state. At this point, a StateChanged event is fired with PointerState set to Fixation. This event should be used to control the earliest visual feedback the application needs to provide to the user about the gaze location. The default is 350ms.
|
||||
/// </summary>
|
||||
static TimeSpan GetFixationDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration for the control to transition from the Fixation state to the Dwell state. At this point, a StateChanged event is fired with PointerState set to Dwell. The Enter and Fixation states are typicaly achieved too rapidly for the user to have much control over. In contrast Dwell is conscious event. This is the point at which the control is invoked, e.g. a button click. The application can modify this property to control when a gaze enabled UI element gets invoked after a user starts looking at it.
|
||||
/// </summary>
|
||||
static TimeSpan GetDwellDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the additional duration for the first repeat to occur.This prevents inadvertent repeated invocation.
|
||||
/// </summary>
|
||||
static TimeSpan GetRepeatDelayDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration of repeated dwell invocations, should the user continue to dwell on the control. The first repeat will occur after an additional delay specified by RepeatDelayDuration. Subsequent repeats happen after every period of DwellRepeatDuration. A control is invoked repeatedly only if MaxDwellRepeatCount is set to greater than zero.
|
||||
/// </summary>
|
||||
static TimeSpan GetDwellRepeatDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration that controls when the PointerState moves to either the Enter state or the Exit state. When this duration has elapsed after the user's gaze first enters a control, the PointerState is set to Enter. And when this duration has elapsed after the user's gaze has left the control, the PointerState is set to Exit. In both cases, a StateChanged event is fired. The default is 50ms.
|
||||
/// </summary>
|
||||
static TimeSpan GetThresholdDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum times the control will invoked repeatedly without the user's gaze having to leave and re-enter the control. The default value is zero which disables repeated invocation of a control. Developers can set a higher value to enable repeated invocation.
|
||||
/// </summary>
|
||||
static int GetMaxDwellRepeatCount(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Boolean indicating whether gaze plus switch is enabled.
|
||||
/// </summary>
|
||||
static bool GetIsSwitchEnabled(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the status of gaze interaction over that particular XAML element.
|
||||
/// </summary>
|
||||
static void SetInteraction(UIElement^ element, GazeInteraction::Interaction value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets Boolean indicating whether cursor is shown while user is looking at the school.
|
||||
/// </summary>
|
||||
static void SetIsCursorVisible(UIElement^ element, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the size of the gaze cursor radius.
|
||||
/// </summary>
|
||||
static void SetCursorRadius(UIElement^ element, int value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the GazeElement associated with an UIElement.
|
||||
/// </summary>
|
||||
static void SetGazeElement(UIElement^ element, GazeElement^ value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration for the control to transition from the Enter state to the Fixation state. At this point, a StateChanged event is fired with PointerState set to Fixation. This event should be used to control the earliest visual feedback the application needs to provide to the user about the gaze location. The default is 350ms.
|
||||
/// </summary>
|
||||
static void SetFixationDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration for the control to transition from the Fixation state to the Dwell state. At this point, a StateChanged event is fired with PointerState set to Dwell. The Enter and Fixation states are typicaly achieved too rapidly for the user to have much control over. In contrast Dwell is conscious event. This is the point at which the control is invoked, e.g. a button click. The application can modify this property to control when a gaze enabled UI element gets invoked after a user starts looking at it.
|
||||
/// </summary>
|
||||
static void SetDwellDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the additional duration for the first repeat to occur.This prevents inadvertent repeated invocation.
|
||||
/// </summary>
|
||||
static void SetRepeatDelayDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration of repeated dwell invocations, should the user continue to dwell on the control. The first repeat will occur after an additional delay specified by RepeatDelayDuration. Subsequent repeats happen after every period of DwellRepeatDuration. A control is invoked repeatedly only if MaxDwellRepeatCount is set to greater than zero.
|
||||
/// </summary>
|
||||
static void SetDwellRepeatDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration that controls when the PointerState moves to either the Enter state or the Exit state. When this duration has elapsed after the user's gaze first enters a control, the PointerState is set to Enter. And when this duration has elapsed after the user's gaze has left the control, the PointerState is set to Exit. In both cases, a StateChanged event is fired. The default is 50ms.
|
||||
/// </summary>
|
||||
static void SetThresholdDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum times the control will invoked repeatedly without the user's gaze having to leave and re-enter the control. The default value is zero which disables repeated invocation of a control. Developers can set a higher value to enable repeated invocation.
|
||||
/// </summary>
|
||||
static void SetMaxDwellRepeatCount(UIElement^ element, int value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Boolean indicating whether gaze plus switch is enabled.
|
||||
/// </summary>
|
||||
static void SetIsSwitchEnabled(UIElement^ element, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GazePointer object.
|
||||
/// </summary>
|
||||
static GazePointer^ GetGazePointer(Page^ page);
|
||||
|
||||
/// <summary>
|
||||
/// Invoke the default action of the specified UIElement.
|
||||
/// </summary>
|
||||
static void Invoke(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Reports whether a gaze input device is available, and hence whether there is any possibility of gaze events occurring in the application.
|
||||
/// </summary>
|
||||
static property bool IsDeviceAvailable { bool get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered whenever IsDeviceAvailable changes value.
|
||||
/// </summary>
|
||||
static event EventHandler<Object^>^ IsDeviceAvailableChanged
|
||||
{
|
||||
EventRegistrationToken add(EventHandler<Object^>^ handler);
|
||||
void remove(EventRegistrationToken token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a settings collection into GazeInput.
|
||||
/// Note: This must be loaded from a UI thread to be valid, since the GazeInput
|
||||
/// instance is tied to the UI thread.
|
||||
/// </summary>
|
||||
static void LoadSettings(ValueSet^ settings);
|
||||
|
||||
internal:
|
||||
|
||||
static TimeSpan UnsetTimeSpan;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -1,774 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "GazePointer.h"
|
||||
|
||||
#include "GazeElement.h"
|
||||
#include "GazeHistoryItem.h"
|
||||
#include "GazeTargetItem.h"
|
||||
#include "StateChangedEventArgs.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
ref class NonInvokeGazeTargetItem sealed : GazeTargetItem
|
||||
{
|
||||
internal:
|
||||
|
||||
NonInvokeGazeTargetItem()
|
||||
: GazeTargetItem(ref new Page())
|
||||
{
|
||||
}
|
||||
|
||||
internal:
|
||||
|
||||
virtual property bool IsInvokable { bool get() override { return false; } }
|
||||
|
||||
void Invoke() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
GazePointer^ GazePointer::Instance::get()
|
||||
{
|
||||
thread_local static GazePointer^ value;
|
||||
if (value == nullptr)
|
||||
{
|
||||
value = ref new GazePointer();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void GazePointer::AddRoot(int proxyId)
|
||||
{
|
||||
_roots->InsertAt(0, proxyId);
|
||||
|
||||
if (_roots->Size == 1)
|
||||
{
|
||||
_isShuttingDown = false;
|
||||
InitializeGazeInputSource();
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::RemoveRoot(int proxyId)
|
||||
{
|
||||
unsigned int index = 0;
|
||||
if (_roots->IndexOf(proxyId, &index))
|
||||
{
|
||||
_roots->RemoveAt(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (_roots->Size == 0)
|
||||
{
|
||||
_isShuttingDown = true;
|
||||
_gazeCursor->IsGazeEntered = false;
|
||||
DeinitializeGazeInputSource();
|
||||
}
|
||||
}
|
||||
|
||||
GazePointer::GazePointer()
|
||||
{
|
||||
_nonInvokeGazeTargetItem = ref new NonInvokeGazeTargetItem();
|
||||
|
||||
// Default to not filtering sample data
|
||||
Filter = ref new NullFilter();
|
||||
|
||||
_gazeCursor = ref new GazeCursor();
|
||||
|
||||
// timer that gets called back if there gaze samples haven't been received in a while
|
||||
_eyesOffTimer = ref new DispatcherTimer();
|
||||
_eyesOffTimer->Tick += ref new EventHandler<Object^>(this, &GazePointer::OnEyesOff);
|
||||
|
||||
// provide a default of GAZE_IDLE_TIME microseconds to fire eyes off
|
||||
EyesOffDelay = GAZE_IDLE_TIME;
|
||||
|
||||
InitializeHistogram();
|
||||
|
||||
_devices = ref new Vector<GazeDevicePreview^>();
|
||||
_watcher = GazeInputSourcePreview::CreateWatcher();
|
||||
_watcher->Added += ref new TypedEventHandler<GazeDeviceWatcherPreview^, GazeDeviceWatcherAddedPreviewEventArgs^>(this, &GazePointer::OnDeviceAdded);
|
||||
_watcher->Removed += ref new TypedEventHandler<GazeDeviceWatcherPreview^, GazeDeviceWatcherRemovedPreviewEventArgs^>(this, &GazePointer::OnDeviceRemoved);
|
||||
_watcher->Start();
|
||||
}
|
||||
|
||||
EventRegistrationToken GazePointer::GazeEvent::add(EventHandler<GazeEventArgs^>^ handler)
|
||||
{
|
||||
_gazeEventCount++;
|
||||
return _gazeEvent += handler;
|
||||
}
|
||||
|
||||
void GazePointer::GazeEvent::remove(EventRegistrationToken token)
|
||||
{
|
||||
_gazeEventCount--;
|
||||
_gazeEvent -= token;
|
||||
}
|
||||
|
||||
void GazePointer::GazeEvent::raise(Object^ sender, GazeEventArgs^ e)
|
||||
{
|
||||
_gazeEvent(sender, e);
|
||||
}
|
||||
|
||||
void GazePointer::OnDeviceAdded(GazeDeviceWatcherPreview^ sender, GazeDeviceWatcherAddedPreviewEventArgs^ args)
|
||||
{
|
||||
_devices->Append(args->Device);
|
||||
|
||||
if (_devices->Size == 1)
|
||||
{
|
||||
IsDeviceAvailableChanged(nullptr, nullptr);
|
||||
|
||||
InitializeGazeInputSource();
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::OnDeviceRemoved(GazeDeviceWatcherPreview^ sender, GazeDeviceWatcherRemovedPreviewEventArgs^ args)
|
||||
{
|
||||
auto index = 0u;
|
||||
while (index < _devices->Size && _devices->GetAt(index)->Id != args->Device->Id)
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index < _devices->Size)
|
||||
{
|
||||
_devices->RemoveAt(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
_devices->RemoveAt(0);
|
||||
}
|
||||
|
||||
if (_devices->Size == 0)
|
||||
{
|
||||
IsDeviceAvailableChanged(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
GazePointer::~GazePointer()
|
||||
{
|
||||
_watcher->Added -= _deviceAddedToken;
|
||||
_watcher->Removed -= _deviceRemovedToken;
|
||||
|
||||
if (_gazeInputSource != nullptr)
|
||||
{
|
||||
_gazeInputSource->GazeEntered -= _gazeEnteredToken;
|
||||
_gazeInputSource->GazeMoved -= _gazeMovedToken;
|
||||
_gazeInputSource->GazeExited -= _gazeExitedToken;
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::LoadSettings(ValueSet^ settings)
|
||||
{
|
||||
_gazeCursor->LoadSettings(settings);
|
||||
Filter->LoadSettings(settings);
|
||||
|
||||
// TODO Add logic to protect against missing settings
|
||||
|
||||
if (settings->HasKey("GazePointer.FixationDelay"))
|
||||
{
|
||||
_defaultFixation = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.FixationDelay")));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.DwellDelay"))
|
||||
{
|
||||
_defaultDwell = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.DwellDelay")));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.DwellRepeatDelay"))
|
||||
{
|
||||
_defaultDwellRepeatDelay = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.DwellRepeatDelay")));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.RepeatDelay"))
|
||||
{
|
||||
_defaultRepeatDelay = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.RepeatDelay")));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.ThresholdDelay"))
|
||||
{
|
||||
_defaultThreshold = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.ThresholdDelay")));
|
||||
}
|
||||
|
||||
// TODO need to set fixation and dwell for all elements
|
||||
if (settings->HasKey("GazePointer.FixationDelay"))
|
||||
{
|
||||
SetElementStateDelay(_offScreenElement, PointerState::Fixation, TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.FixationDelay"))));
|
||||
}
|
||||
if (settings->HasKey("GazePointer.DwellDelay"))
|
||||
{
|
||||
SetElementStateDelay(_offScreenElement, PointerState::Dwell, TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.DwellDelay"))));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.GazeIdleTime"))
|
||||
{
|
||||
EyesOffDelay = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.GazeIdleTime")));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.IsSwitchEnabled"))
|
||||
{
|
||||
IsSwitchEnabled = (bool)(settings->Lookup("GazePointer.IsSwitchEnabled"));
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::InitializeHistogram()
|
||||
{
|
||||
_activeHitTargetTimes = ref new Vector<GazeTargetItem^>();
|
||||
|
||||
_offScreenElement = ref new UserControl();
|
||||
SetElementStateDelay(_offScreenElement, PointerState::Fixation, _defaultFixation);
|
||||
SetElementStateDelay(_offScreenElement, PointerState::Dwell, _defaultDwell);
|
||||
|
||||
_maxHistoryTime = DEFAULT_MAX_HISTORY_DURATION; // maintain about 3 seconds of history (in microseconds)
|
||||
_gazeHistory = ref new Vector<GazeHistoryItem^>();
|
||||
}
|
||||
|
||||
void GazePointer::InitializeGazeInputSource()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
if (_roots->Size != 0 && _devices->Size != 0)
|
||||
{
|
||||
if (_gazeInputSource == nullptr)
|
||||
{
|
||||
_gazeInputSource = GazeInputSourcePreview::GetForCurrentView();
|
||||
}
|
||||
|
||||
if (_gazeInputSource != nullptr)
|
||||
{
|
||||
_gazeEnteredToken = _gazeInputSource->GazeEntered += ref new TypedEventHandler<
|
||||
GazeInputSourcePreview^, GazeEnteredPreviewEventArgs^>(this, &GazePointer::OnGazeEntered);
|
||||
_gazeMovedToken = _gazeInputSource->GazeMoved += ref new TypedEventHandler<
|
||||
GazeInputSourcePreview^, GazeMovedPreviewEventArgs^>(this, &GazePointer::OnGazeMoved);
|
||||
_gazeExitedToken = _gazeInputSource->GazeExited += ref new TypedEventHandler<
|
||||
GazeInputSourcePreview^, GazeExitedPreviewEventArgs^>(this, &GazePointer::OnGazeExited);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::DeinitializeGazeInputSource()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
_initialized = false;
|
||||
|
||||
_gazeInputSource->GazeEntered -= _gazeEnteredToken;
|
||||
_gazeInputSource->GazeMoved -= _gazeMovedToken;
|
||||
_gazeInputSource->GazeExited -= _gazeExitedToken;
|
||||
}
|
||||
}
|
||||
|
||||
static DependencyProperty^ GetProperty(PointerState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PointerState::Fixation: return GazeInput::FixationDurationProperty;
|
||||
case PointerState::Dwell: return GazeInput::DwellDurationProperty;
|
||||
case PointerState::DwellRepeat: return GazeInput::DwellRepeatDurationProperty;
|
||||
case PointerState::Enter: return GazeInput::ThresholdDurationProperty;
|
||||
case PointerState::Exit: return GazeInput::ThresholdDurationProperty;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TimeSpan GazePointer::GetDefaultPropertyValue(PointerState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PointerState::Fixation: return _defaultFixation;
|
||||
case PointerState::Dwell: return _defaultDwell;
|
||||
case PointerState::DwellRepeat: return _defaultRepeatDelay;
|
||||
case PointerState::Enter: return _defaultThreshold;
|
||||
case PointerState::Exit: return _defaultThreshold;
|
||||
default: throw ref new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::SetElementStateDelay(UIElement ^element, PointerState relevantState, TimeSpan stateDelay)
|
||||
{
|
||||
auto property = GetProperty(relevantState);
|
||||
element->SetValue(property, stateDelay);
|
||||
|
||||
// fix up _maxHistoryTime in case the new param exceeds the history length we are currently tracking
|
||||
auto dwellTime = GetElementStateDelay(element, PointerState::Dwell);
|
||||
auto repeatTime = GetElementStateDelay(element, PointerState::DwellRepeat);
|
||||
_maxHistoryTime = 2 * max(dwellTime, repeatTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the parent to inherit properties from.
|
||||
/// </summary>
|
||||
static UIElement^ GetInheritenceParent(UIElement^ child)
|
||||
{
|
||||
// The result value.
|
||||
Object^ parent = nullptr;
|
||||
|
||||
// Get the automation peer...
|
||||
auto peer = FrameworkElementAutomationPeer::FromElement(child);
|
||||
if (peer != nullptr)
|
||||
{
|
||||
// ...if it exists, get the peer's parent...
|
||||
auto peerParent = dynamic_cast<FrameworkElementAutomationPeer^>(peer->Navigate(AutomationNavigationDirection::Parent));
|
||||
if (peerParent != nullptr)
|
||||
{
|
||||
// ...and if it has a parent, get the corresponding object.
|
||||
parent = peerParent->Owner;
|
||||
}
|
||||
}
|
||||
|
||||
// If the above failed to find a parent...
|
||||
if (parent == nullptr)
|
||||
{
|
||||
// ...use the visual parent.
|
||||
parent = VisualTreeHelper::GetParent(child);
|
||||
}
|
||||
|
||||
// Safely pun the value we found to a UIElement reference.
|
||||
return dynamic_cast<UIElement^>(parent);
|
||||
}
|
||||
|
||||
TimeSpan GazePointer::GetElementStateDelay(UIElement ^element, DependencyProperty^ property, TimeSpan defaultValue)
|
||||
{
|
||||
UIElement^ walker = element;
|
||||
Object^ valueAtWalker = walker->GetValue(property);
|
||||
|
||||
while (GazeInput::UnsetTimeSpan.Equals(valueAtWalker) && walker != nullptr)
|
||||
{
|
||||
walker = GetInheritenceParent(walker);
|
||||
|
||||
if (walker != nullptr)
|
||||
{
|
||||
valueAtWalker = walker->GetValue(property);
|
||||
}
|
||||
}
|
||||
|
||||
auto ticks = GazeInput::UnsetTimeSpan.Equals(valueAtWalker) ? defaultValue : safe_cast<TimeSpan>(valueAtWalker);
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
TimeSpan GazePointer::GetElementStateDelay(UIElement ^element, PointerState pointerState)
|
||||
{
|
||||
auto property = GetProperty(pointerState);
|
||||
auto defaultValue = GetDefaultPropertyValue(pointerState);
|
||||
auto ticks = GetElementStateDelay(element, property, defaultValue);
|
||||
|
||||
switch (pointerState)
|
||||
{
|
||||
case PointerState::Dwell:
|
||||
case PointerState::DwellRepeat:
|
||||
_maxHistoryTime = max(_maxHistoryTime, 2 * ticks);
|
||||
break;
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
void GazePointer::Reset()
|
||||
{
|
||||
_activeHitTargetTimes->Clear();
|
||||
_gazeHistory->Clear();
|
||||
|
||||
_maxHistoryTime = DEFAULT_MAX_HISTORY_DURATION;
|
||||
}
|
||||
|
||||
GazeTargetItem^ GazePointer::GetHitTarget(Point gazePoint)
|
||||
{
|
||||
GazeTargetItem^ invokable;
|
||||
|
||||
switch (Window::Current->CoreWindow->ActivationMode)
|
||||
{
|
||||
default:
|
||||
if (!_isAlwaysActivated)
|
||||
{
|
||||
invokable = _nonInvokeGazeTargetItem;
|
||||
break;
|
||||
}
|
||||
|
||||
case CoreWindowActivationMode::ActivatedInForeground:
|
||||
case CoreWindowActivationMode::ActivatedNotForeground:
|
||||
auto elements = VisualTreeHelper::FindElementsInHostCoordinates(gazePoint, nullptr, false);
|
||||
auto first = elements->First();
|
||||
auto element = first->HasCurrent ? first->Current : nullptr;
|
||||
|
||||
invokable = nullptr;
|
||||
|
||||
if (element != nullptr)
|
||||
{
|
||||
invokable = GazeTargetItem::GetOrCreate(element);
|
||||
|
||||
while (element != nullptr && !invokable->IsInvokable)
|
||||
{
|
||||
element = dynamic_cast<UIElement^>(VisualTreeHelper::GetParent(element));
|
||||
|
||||
if (element != nullptr)
|
||||
{
|
||||
invokable = GazeTargetItem::GetOrCreate(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (element == nullptr || !invokable->IsInvokable)
|
||||
{
|
||||
invokable = _nonInvokeGazeTargetItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
Interaction interaction;
|
||||
do
|
||||
{
|
||||
interaction = GazeInput::GetInteraction(element);
|
||||
if (interaction == Interaction::Inherited)
|
||||
{
|
||||
element = GetInheritenceParent(element);
|
||||
}
|
||||
} while (interaction == Interaction::Inherited && element != nullptr);
|
||||
|
||||
if (interaction == Interaction::Inherited)
|
||||
{
|
||||
interaction = GazeInput::Interaction;
|
||||
}
|
||||
|
||||
if (interaction != Interaction::Enabled)
|
||||
{
|
||||
invokable = _nonInvokeGazeTargetItem;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return invokable;
|
||||
}
|
||||
|
||||
void GazePointer::ActivateGazeTargetItem(GazeTargetItem^ target)
|
||||
{
|
||||
unsigned int index;
|
||||
if (!_activeHitTargetTimes->IndexOf(target, &index))
|
||||
{
|
||||
_activeHitTargetTimes->Append(target);
|
||||
|
||||
// calculate the time that the first DwellRepeat needs to be fired after. this will be updated every time a DwellRepeat is
|
||||
// fired to keep track of when the next one is to be fired after that.
|
||||
auto nextStateTime = GetElementStateDelay(target->TargetElement, PointerState::Enter);
|
||||
|
||||
target->Reset(nextStateTime);
|
||||
}
|
||||
}
|
||||
|
||||
GazeTargetItem^ GazePointer::ResolveHitTarget(Point gazePoint, TimeSpan timestamp)
|
||||
{
|
||||
// TODO: The existance of a GazeTargetItem should be used to indicate that
|
||||
// the target item is invokable. The method of invokation should be stored
|
||||
// within the GazeTargetItem when it is created and not recalculated when
|
||||
// subsequently needed.
|
||||
|
||||
// create GazeHistoryItem to deal with this sample
|
||||
auto target = GetHitTarget(gazePoint);
|
||||
auto historyItem = ref new GazeHistoryItem();
|
||||
historyItem->HitTarget = target;
|
||||
historyItem->Timestamp = timestamp;
|
||||
historyItem->Duration = TimeSpanZero;
|
||||
assert(historyItem->HitTarget != nullptr);
|
||||
|
||||
// create new GazeTargetItem with a (default) total elapsed time of zero if one does not exist already.
|
||||
// this ensures that there will always be an entry for target elements in the code below.
|
||||
ActivateGazeTargetItem(target);
|
||||
target->LastTimestamp = timestamp;
|
||||
|
||||
// find elapsed time since we got the last hit target
|
||||
historyItem->Duration = timestamp - _lastTimestamp;
|
||||
if (historyItem->Duration > MAX_SINGLE_SAMPLE_DURATION)
|
||||
{
|
||||
historyItem->Duration = MAX_SINGLE_SAMPLE_DURATION;
|
||||
}
|
||||
_gazeHistory->Append(historyItem);
|
||||
|
||||
// update the time this particular hit target has accumulated
|
||||
target->DetailedTime += historyItem->Duration;
|
||||
|
||||
// drop the oldest samples from the list until we have samples only
|
||||
// within the window we are monitoring
|
||||
//
|
||||
// historyItem is the last item we just appended a few lines above.
|
||||
for (auto evOldest = _gazeHistory->GetAt(0);
|
||||
historyItem->Timestamp - evOldest->Timestamp > _maxHistoryTime;
|
||||
evOldest = _gazeHistory->GetAt(0))
|
||||
{
|
||||
_gazeHistory->RemoveAt(0);
|
||||
|
||||
// subtract the duration obtained from the oldest sample in _gazeHistory
|
||||
auto targetItem = evOldest->HitTarget;
|
||||
assert(targetItem->DetailedTime - evOldest->Duration >= TimeSpanZero);
|
||||
targetItem->DetailedTime -= evOldest->Duration;
|
||||
if (targetItem->ElementState != PointerState::PreEnter)
|
||||
{
|
||||
targetItem->OverflowTime += evOldest->Duration;
|
||||
}
|
||||
}
|
||||
|
||||
_lastTimestamp = timestamp;
|
||||
|
||||
// Return the most recent hit target
|
||||
// Intuition would tell us that we should return NOT the most recent
|
||||
// hitTarget, but the one with the most accumulated time in
|
||||
// in the maintained history. But the effect of that is that
|
||||
// the user will feel that they have clicked on the wrong thing
|
||||
// when they are looking at something else.
|
||||
// That is why we return the most recent hitTarget so that
|
||||
// when its dwell time has elapsed, it will be invoked
|
||||
return target;
|
||||
}
|
||||
|
||||
void GazePointer::OnEyesOff(Object ^sender, Object ^ea)
|
||||
{
|
||||
_eyesOffTimer->Stop();
|
||||
|
||||
CheckIfExiting(_lastTimestamp + EyesOffDelay);
|
||||
RaiseGazePointerEvent(nullptr, PointerState::Enter, EyesOffDelay);
|
||||
}
|
||||
|
||||
void GazePointer::CheckIfExiting(TimeSpan curTimestamp)
|
||||
{
|
||||
for (unsigned int index = 0; index < _activeHitTargetTimes->Size; index++)
|
||||
{
|
||||
auto targetItem = _activeHitTargetTimes->GetAt(index);
|
||||
auto targetElement = targetItem->TargetElement;
|
||||
auto exitDelay = GetElementStateDelay(targetElement, PointerState::Exit);
|
||||
|
||||
auto idleDuration = curTimestamp - targetItem->LastTimestamp;
|
||||
if (targetItem->ElementState != PointerState::PreEnter && idleDuration > exitDelay)
|
||||
{
|
||||
targetItem->ElementState = PointerState::PreEnter;
|
||||
|
||||
// Transitioning to exit - clear the cached fixated element
|
||||
_currentlyFixatedElement = nullptr;
|
||||
|
||||
RaiseGazePointerEvent(targetItem, PointerState::Exit, targetItem->ElapsedTime);
|
||||
targetItem->GiveFeedback();
|
||||
|
||||
_activeHitTargetTimes->RemoveAt(index);
|
||||
|
||||
// remove all history samples referring to deleted hit target
|
||||
for (unsigned i = 0; i < _gazeHistory->Size; )
|
||||
{
|
||||
auto hitTarget = _gazeHistory->GetAt(i)->HitTarget;
|
||||
if (hitTarget->TargetElement == targetElement)
|
||||
{
|
||||
_gazeHistory->RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// return because only one element can be exited at a time and at this point
|
||||
// we have done everything that we can do
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t *PointerStates[] = {
|
||||
L"Exit",
|
||||
L"PreEnter",
|
||||
L"Enter",
|
||||
L"Fixation",
|
||||
L"Dwell",
|
||||
L"DwellRepeat"
|
||||
};
|
||||
|
||||
void GazePointer::RaiseGazePointerEvent(GazeTargetItem^ target, PointerState state, TimeSpan elapsedTime)
|
||||
{
|
||||
auto control = target != nullptr ? target->TargetElement : nullptr;
|
||||
//assert(target != _rootElement);
|
||||
auto gpea = ref new StateChangedEventArgs(control, state, elapsedTime);
|
||||
//auto buttonObj = dynamic_cast<Button ^>(target);
|
||||
//if (buttonObj && buttonObj->Content)
|
||||
//{
|
||||
// String^ buttonText = dynamic_cast<String^>(buttonObj->Content);
|
||||
// Debug::WriteLine(L"GPE: %s -> %s, %d", buttonText, PointerStates[(int)state], elapsedTime);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// Debug::WriteLine(L"GPE: 0x%08x -> %s, %d", target != nullptr ? target->GetHashCode() : 0, PointerStates[(int)state], elapsedTime);
|
||||
//}
|
||||
|
||||
auto gazeElement = target != nullptr ? GazeInput::GetGazeElement(control) : nullptr;
|
||||
|
||||
if (gazeElement != nullptr)
|
||||
{
|
||||
gazeElement->RaiseStateChanged(control, gpea);
|
||||
}
|
||||
|
||||
if (state == PointerState::Dwell)
|
||||
{
|
||||
auto handled = false;
|
||||
|
||||
if (gazeElement != nullptr)
|
||||
{
|
||||
auto args = ref new DwellInvokedRoutedEventArgs();
|
||||
gazeElement->RaiseInvoked(control, args);
|
||||
handled = args->Handled;
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
target->Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::OnGazeEntered(GazeInputSourcePreview^ provider, GazeEnteredPreviewEventArgs^ args)
|
||||
{
|
||||
//Debug::WriteLine(L"Entered at %ld", args->CurrentPoint->Timestamp);
|
||||
_gazeCursor->IsGazeEntered = true;
|
||||
}
|
||||
|
||||
void GazePointer::OnGazeMoved(GazeInputSourcePreview^ provider, GazeMovedPreviewEventArgs^ args)
|
||||
{
|
||||
if (!_isShuttingDown)
|
||||
{
|
||||
auto intermediatePoints = args->GetIntermediatePoints();
|
||||
for each(auto point in intermediatePoints)
|
||||
{
|
||||
auto position = point->EyeGazePosition;
|
||||
if (position != nullptr)
|
||||
{
|
||||
_gazeCursor->IsGazeEntered = true;
|
||||
ProcessGazePoint(TimeSpanFromMicroseconds(point->Timestamp), position->Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug::WriteLine(L"Null position eaten at %ld", point->Timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::OnGazeExited(GazeInputSourcePreview^ provider, GazeExitedPreviewEventArgs^ args)
|
||||
{
|
||||
//Debug::WriteLine(L"Exited at %ld", args->CurrentPoint->Timestamp);
|
||||
_gazeCursor->IsGazeEntered = false;
|
||||
}
|
||||
|
||||
void GazePointer::ProcessGazePoint(TimeSpan timestamp, Point position)
|
||||
{
|
||||
auto ea = ref new GazeFilterArgs(position, timestamp);
|
||||
|
||||
auto fa = Filter->Update(ea);
|
||||
_gazeCursor->Position = fa->Location;
|
||||
|
||||
if (_gazeEventCount != 0)
|
||||
{
|
||||
_gazeEventArgs->Set(fa->Location, timestamp);
|
||||
GazeEvent(this, _gazeEventArgs);
|
||||
if (_gazeEventArgs->Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto targetItem = ResolveHitTarget(fa->Location, fa->Timestamp);
|
||||
assert(targetItem != nullptr);
|
||||
|
||||
//Debug::WriteLine(L"ProcessGazePoint: %llu -> [%d, %d], %llu", hitTarget->GetHashCode(), (int)fa->Location.X, (int)fa->Location.Y, fa->Timestamp);
|
||||
|
||||
// check to see if any element in _hitTargetTimes needs an exit event fired.
|
||||
// this ensures that all exit events are fired before enter event
|
||||
CheckIfExiting(fa->Timestamp);
|
||||
|
||||
PointerState nextState = static_cast<PointerState>(static_cast<int>(targetItem->ElementState) + 1);
|
||||
|
||||
//Debug::WriteLine(L"%llu -> State=%d, Elapsed=%d, NextStateTime=%d", targetItem->TargetElement, targetItem->ElementState, targetItem->ElapsedTime, targetItem->NextStateTime);
|
||||
|
||||
if (targetItem->ElapsedTime > targetItem->NextStateTime)
|
||||
{
|
||||
auto prevStateTime = targetItem->NextStateTime;
|
||||
|
||||
// prevent targetItem from ever actually transitioning into the DwellRepeat state so as
|
||||
// to continuously emit the DwellRepeat event
|
||||
if (nextState != PointerState::DwellRepeat)
|
||||
{
|
||||
targetItem->ElementState = nextState;
|
||||
nextState = static_cast<PointerState>(static_cast<int>(nextState) + 1); // nextState++
|
||||
targetItem->NextStateTime += GetElementStateDelay(targetItem->TargetElement, nextState);
|
||||
|
||||
if (targetItem->ElementState == PointerState::Dwell)
|
||||
{
|
||||
targetItem->NextStateTime += GetElementStateDelay(targetItem->TargetElement, GazeInput::RepeatDelayDurationProperty, _defaultDwellRepeatDelay);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the NextStateTime by one dwell period, while continuing to stay in Dwell state
|
||||
targetItem->NextStateTime += GetElementStateDelay(targetItem->TargetElement, PointerState::DwellRepeat);
|
||||
}
|
||||
|
||||
if (targetItem->ElementState == PointerState::Dwell)
|
||||
{
|
||||
targetItem->RepeatCount++;
|
||||
if (targetItem->MaxDwellRepeatCount < targetItem->RepeatCount)
|
||||
{
|
||||
targetItem->NextStateTime = TimeSpan{ MAXINT64 };
|
||||
}
|
||||
}
|
||||
|
||||
if (targetItem->ElementState == PointerState::Fixation)
|
||||
{
|
||||
// Cache the fixated item
|
||||
_currentlyFixatedElement = targetItem;
|
||||
|
||||
// We are about to transition into the Dwell state
|
||||
// If switch input is enabled, make sure dwell never completes
|
||||
// via eye gaze
|
||||
if (_isSwitchEnabled)
|
||||
{
|
||||
// Don't allow the next state (Dwell) to progress
|
||||
targetItem->NextStateTime = TimeSpan{ MAXINT64 };
|
||||
}
|
||||
}
|
||||
|
||||
RaiseGazePointerEvent(targetItem, targetItem->ElementState, targetItem->ElapsedTime);
|
||||
}
|
||||
|
||||
targetItem->GiveFeedback();
|
||||
|
||||
_eyesOffTimer->Start();
|
||||
_lastTimestamp = fa->Timestamp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When in switch mode, will issue a click on the currently fixated element
|
||||
/// </summary>
|
||||
void GazePointer::Click()
|
||||
{
|
||||
if (_isSwitchEnabled &&
|
||||
_currentlyFixatedElement != nullptr)
|
||||
{
|
||||
_currentlyFixatedElement->Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run device calibration.
|
||||
/// </summary>
|
||||
IAsyncOperation<bool>^ GazePointer::RequestCalibrationAsync()
|
||||
{
|
||||
return _devices->Size == 1 ?
|
||||
_devices->GetAt(0)->RequestCalibrationAsync() :
|
||||
concurrency::create_async([] { return false; });
|
||||
}
|
||||
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,918 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Devices.Input.Preview;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Class of singleton object coordinating gaze input.
|
||||
/// </summary>
|
||||
public class GazePointer
|
||||
{
|
||||
// units in microseconds
|
||||
private static readonly TimeSpan DEFAULT_FIXATION_DELAY = new TimeSpan(3500000);
|
||||
private static readonly TimeSpan DEFAULT_DWELL_DELAY = new TimeSpan(4000000);
|
||||
private static readonly TimeSpan DEFAULT_DWELL_REPEAT_DELAY = new TimeSpan(4000000);
|
||||
private static readonly TimeSpan DEFAULT_REPEAT_DELAY = new TimeSpan(4000000);
|
||||
private static readonly TimeSpan DEFAULT_THRESHOLD_DELAY = new TimeSpan(500000);
|
||||
private static readonly TimeSpan DEFAULT_MAX_HISTORY_DURATION = new TimeSpan(30000000);
|
||||
private static readonly TimeSpan MAX_SINGLE_SAMPLE_DURATION = new TimeSpan(1000000);
|
||||
|
||||
private static readonly TimeSpan GAZE_IDLE_TIME = new TimeSpan(250000000);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="GazePointer"/> class.
|
||||
/// </summary>
|
||||
~GazePointer()
|
||||
{
|
||||
_watcher.Added -= OnDeviceAdded;
|
||||
_watcher.Removed -= OnDeviceRemoved;
|
||||
|
||||
if (_gazeInputSource != null)
|
||||
{
|
||||
_gazeInputSource.GazeEntered -= OnGazeEntered;
|
||||
_gazeInputSource.GazeMoved -= OnGazeMoved;
|
||||
_gazeInputSource.GazeExited -= OnGazeExited;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a settings collection into GazePointer.
|
||||
/// </summary>
|
||||
public void LoadSettings(ValueSet settings)
|
||||
{
|
||||
_gazeCursor.LoadSettings(settings);
|
||||
Filter.LoadSettings(settings);
|
||||
|
||||
// TODO Add logic to protect against missing settings
|
||||
if (settings.ContainsKey("GazePointer.FixationDelay"))
|
||||
{
|
||||
_defaultFixation = new TimeSpan((int)settings["GazePointer.FixationDelay"] * 10);
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("GazePointer.DwellDelay"))
|
||||
{
|
||||
_defaultDwell = new TimeSpan((int)settings["GazePointer.DwellDelay"] * 10);
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("GazePointer.DwellRepeatDelay"))
|
||||
{
|
||||
_defaultDwellRepeatDelay = new TimeSpan((int)settings["GazePointer.DwellRepeatDelay"] * 10);
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("GazePointer.RepeatDelay"))
|
||||
{
|
||||
_defaultRepeatDelay = new TimeSpan((int)settings["GazePointer.RepeatDelay"] * 10);
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("GazePointer.ThresholdDelay"))
|
||||
{
|
||||
_defaultThreshold = new TimeSpan((int)settings["GazePointer.ThresholdDelay"] * 10);
|
||||
}
|
||||
|
||||
// TODO need to set fixation and dwell for all elements
|
||||
if (settings.ContainsKey("GazePointer.FixationDelay"))
|
||||
{
|
||||
SetElementStateDelay(_offScreenElement, PointerState.Fixation, new TimeSpan((int)settings["GazePointer.FixationDelay"] * 10));
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("GazePointer.DwellDelay"))
|
||||
{
|
||||
SetElementStateDelay(_offScreenElement, PointerState.Dwell, new TimeSpan((int)settings["GazePointer.DwellDelay"] * 10));
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("GazePointer.GazeIdleTime"))
|
||||
{
|
||||
EyesOffDelay = new TimeSpan((int)settings["GazePointer.GazeIdleTime"] * 10);
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("GazePointer.IsSwitchEnabled"))
|
||||
{
|
||||
IsSwitchEnabled = (bool)settings["GazePointer.IsSwitchEnabled"];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When in switch mode, will issue a click on the currently fixated element
|
||||
/// </summary>
|
||||
public void Click()
|
||||
{
|
||||
if (IsSwitchEnabled &&
|
||||
_currentlyFixatedElement != null)
|
||||
{
|
||||
_currentlyFixatedElement.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run device calibration.
|
||||
/// </summary>
|
||||
/// <returns>Task that returns true, if calibration completes successfully; otherwise, false.</returns>
|
||||
public Task<bool> RequestCalibrationAsync()
|
||||
{
|
||||
return _devices.Count == 1 ?
|
||||
_devices[0].RequestCalibrationAsync().AsTask() :
|
||||
Task.FromResult(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for Gaze related events, of with a <see cref="GazeEventArgs"/> object.
|
||||
/// </summary>
|
||||
public event EventHandler<GazeEventArgs> GazeEvent
|
||||
{
|
||||
add
|
||||
{
|
||||
_gazeEventCount++;
|
||||
_gazeEvent += value;
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
_gazeEventCount--;
|
||||
_gazeEvent -= value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UIElement representing the cursor.
|
||||
/// </summary>
|
||||
public UIElement CursorElement
|
||||
{
|
||||
get { return _gazeCursor.PopupChild; }
|
||||
set { _gazeCursor.PopupChild = value; }
|
||||
}
|
||||
|
||||
private EventHandler<GazeEventArgs> _gazeEvent;
|
||||
|
||||
private int _gazeEventCount = 0;
|
||||
|
||||
internal Brush EnterBrush { get; set; } = null;
|
||||
|
||||
internal Brush ProgressBrush { get; set; } = new SolidColorBrush(Colors.Green);
|
||||
|
||||
internal Brush CompleteBrush { get; set; } = new SolidColorBrush(Colors.Red);
|
||||
|
||||
internal double DwellStrokeThickness { get; set; } = 2;
|
||||
|
||||
internal Interaction Interaction { get; set; } = Interaction.Disabled;
|
||||
|
||||
internal GazeTargetItem NonInvokeGazeTargetItem { get; }
|
||||
|
||||
internal GazeFeedbackPopupFactory GazeFeedbackPopupFactory { get; } = new GazeFeedbackPopupFactory();
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
_activeHitTargetTimes.Clear();
|
||||
_gazeHistory.Clear();
|
||||
|
||||
_maxHistoryTime = DEFAULT_MAX_HISTORY_DURATION;
|
||||
}
|
||||
|
||||
internal void SetElementStateDelay(UIElement element, PointerState relevantState, TimeSpan stateDelay)
|
||||
{
|
||||
var property = GetProperty(relevantState);
|
||||
element.SetValue(property, stateDelay);
|
||||
|
||||
// fix up _maxHistoryTime in case the new param exceeds the history length we are currently tracking
|
||||
var dwellTime = GetElementStateDelay(element, PointerState.Dwell);
|
||||
var repeatTime = GetElementStateDelay(element, PointerState.DwellRepeat);
|
||||
_maxHistoryTime = new TimeSpan(2 * Math.Max(dwellTime.Ticks, repeatTime.Ticks));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the parent to inherit properties from.
|
||||
/// </summary>
|
||||
private static UIElement GetInheritenceParent(UIElement child)
|
||||
{
|
||||
// The result value.
|
||||
object parent = null;
|
||||
|
||||
// Get the automation peer...
|
||||
var peer = FrameworkElementAutomationPeer.FromElement(child);
|
||||
if (peer != null)
|
||||
{
|
||||
// ...if it exists, get the peer's parent...
|
||||
var peerParent = peer.Navigate(AutomationNavigationDirection.Parent) as FrameworkElementAutomationPeer;
|
||||
if (peerParent != null)
|
||||
{
|
||||
// ...and if it has a parent, get the corresponding object.
|
||||
parent = peerParent.Owner;
|
||||
}
|
||||
}
|
||||
|
||||
// If the above failed to find a parent...
|
||||
if (parent == null)
|
||||
{
|
||||
// ...use the visual parent.
|
||||
parent = VisualTreeHelper.GetParent(child);
|
||||
}
|
||||
|
||||
// Safely pun the value we found to a UIElement reference.
|
||||
return parent as UIElement;
|
||||
}
|
||||
|
||||
internal TimeSpan GetElementStateDelay(UIElement element, DependencyProperty property, TimeSpan defaultValue)
|
||||
{
|
||||
UIElement walker = element;
|
||||
object valueAtWalker = walker.GetValue(property);
|
||||
|
||||
while (GazeInput.UnsetTimeSpan.Equals(valueAtWalker) && walker != null)
|
||||
{
|
||||
walker = GetInheritenceParent(walker);
|
||||
|
||||
if (walker != null)
|
||||
{
|
||||
valueAtWalker = walker.GetValue(property);
|
||||
}
|
||||
}
|
||||
|
||||
var ticks = GazeInput.UnsetTimeSpan.Equals(valueAtWalker) ? defaultValue : (TimeSpan)valueAtWalker;
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
internal TimeSpan GetElementStateDelay(UIElement element, PointerState pointerState)
|
||||
{
|
||||
var property = GetProperty(pointerState);
|
||||
var defaultValue = GetDefaultPropertyValue(pointerState);
|
||||
var ticks = GetElementStateDelay(element, property, defaultValue);
|
||||
|
||||
switch (pointerState)
|
||||
{
|
||||
case PointerState.Dwell:
|
||||
case PointerState.DwellRepeat:
|
||||
_maxHistoryTime = new TimeSpan(Math.Max(_maxHistoryTime.Ticks, 2 * ticks.Ticks));
|
||||
break;
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
// Provide a configurable delay for when the EyesOffDelay event is fired
|
||||
// GOTCHA: this value requires that _eyesOffTimer is instantiated so that it
|
||||
// can update the timer interval
|
||||
internal TimeSpan EyesOffDelay
|
||||
{
|
||||
get
|
||||
{
|
||||
return _eyesOffDelay;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_eyesOffDelay = value;
|
||||
|
||||
// convert GAZE_IDLE_TIME units (microseconds) to 100-nanosecond units used
|
||||
// by TimeSpan struct
|
||||
_eyesOffTimer.Interval = EyesOffDelay;
|
||||
}
|
||||
}
|
||||
|
||||
// Pluggable filter for eye tracking sample data. This defaults to being set to the
|
||||
// NullFilter which performs no filtering of input samples.
|
||||
internal IGazeFilter Filter { get; set; }
|
||||
|
||||
internal bool IsCursorVisible
|
||||
{
|
||||
get { return _gazeCursor.IsCursorVisible; }
|
||||
set { _gazeCursor.IsCursorVisible = value; }
|
||||
}
|
||||
|
||||
internal int CursorRadius
|
||||
{
|
||||
get { return _gazeCursor.CursorRadius; }
|
||||
set { _gazeCursor.CursorRadius = value; }
|
||||
}
|
||||
|
||||
internal bool IsSwitchEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this GazePointer should always be activated or not.
|
||||
/// </summary>
|
||||
public bool IsAlwaysActivated { get; set; }
|
||||
|
||||
private static GazePointer _instance = null;
|
||||
|
||||
internal static GazePointer Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new GazePointer();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddRoot(int proxyId)
|
||||
{
|
||||
_roots.Insert(0, proxyId);
|
||||
|
||||
if (_roots.Count == 1)
|
||||
{
|
||||
_isShuttingDown = false;
|
||||
InitializeGazeInputSource();
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveRoot(int proxyId)
|
||||
{
|
||||
int index;
|
||||
if ((index = _roots.IndexOf(proxyId)) != -1)
|
||||
{
|
||||
_roots.RemoveAt(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "Invalid proxyId when trying to remove a Root from the GazeProxy");
|
||||
}
|
||||
|
||||
if (_roots.Count == 0)
|
||||
{
|
||||
_isShuttingDown = true;
|
||||
_gazeCursor.IsGazeEntered = false;
|
||||
DeinitializeGazeInputSource();
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsDeviceAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
return _devices.Count != 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal event EventHandler<object> IsDeviceAvailableChanged;
|
||||
|
||||
private GazePointer()
|
||||
{
|
||||
NonInvokeGazeTargetItem = new NonInvokeGazeTargetItem();
|
||||
|
||||
// Default to not filtering sample data
|
||||
Filter = new NullFilter();
|
||||
|
||||
_gazeCursor = new GazeCursor();
|
||||
|
||||
// timer that gets called back if there gaze samples haven't been received in a while
|
||||
_eyesOffTimer = new DispatcherTimer();
|
||||
_eyesOffTimer.Tick += OnEyesOff;
|
||||
|
||||
// provide a default of GAZE_IDLE_TIME microseconds to fire eyes off
|
||||
EyesOffDelay = GAZE_IDLE_TIME;
|
||||
|
||||
InitializeHistogram();
|
||||
|
||||
_devices = new List<GazeDevicePreview>();
|
||||
_watcher = GazeInputSourcePreview.CreateWatcher();
|
||||
_watcher.Added += OnDeviceAdded;
|
||||
_watcher.Removed += OnDeviceRemoved;
|
||||
_watcher.Start();
|
||||
}
|
||||
|
||||
private bool _initialized;
|
||||
private bool _isShuttingDown;
|
||||
|
||||
private static DependencyProperty GetProperty(PointerState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PointerState.Fixation: return GazeInput.FixationDurationProperty;
|
||||
case PointerState.Dwell: return GazeInput.DwellDurationProperty;
|
||||
case PointerState.DwellRepeat: return GazeInput.DwellRepeatDurationProperty;
|
||||
case PointerState.Enter: return GazeInput.ThresholdDurationProperty;
|
||||
case PointerState.Exit: return GazeInput.ThresholdDurationProperty;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
private TimeSpan GetDefaultPropertyValue(PointerState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PointerState.Fixation: return _defaultFixation;
|
||||
case PointerState.Dwell: return _defaultDwell;
|
||||
case PointerState.DwellRepeat: return _defaultRepeatDelay;
|
||||
case PointerState.Enter: return _defaultThreshold;
|
||||
case PointerState.Exit: return _defaultThreshold;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeHistogram()
|
||||
{
|
||||
_activeHitTargetTimes = new List<GazeTargetItem>();
|
||||
|
||||
_offScreenElement = new UserControl();
|
||||
SetElementStateDelay(_offScreenElement, PointerState.Fixation, _defaultFixation);
|
||||
SetElementStateDelay(_offScreenElement, PointerState.Dwell, _defaultDwell);
|
||||
|
||||
_maxHistoryTime = DEFAULT_MAX_HISTORY_DURATION; // maintain about 3 seconds of history (in microseconds)
|
||||
_gazeHistory = new List<GazeHistoryItem>();
|
||||
}
|
||||
|
||||
private void InitializeGazeInputSource()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
if (_roots.Count != 0 && _devices.Count != 0)
|
||||
{
|
||||
if (_gazeInputSource == null)
|
||||
{
|
||||
_gazeInputSource = GazeInputSourcePreview.GetForCurrentView();
|
||||
}
|
||||
|
||||
if (_gazeInputSource != null)
|
||||
{
|
||||
_gazeInputSource.GazeEntered += OnGazeEntered;
|
||||
_gazeInputSource.GazeMoved += OnGazeMoved;
|
||||
_gazeInputSource.GazeExited += OnGazeExited;
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DeinitializeGazeInputSource()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
_initialized = false;
|
||||
|
||||
_gazeInputSource.GazeEntered -= OnGazeEntered;
|
||||
_gazeInputSource.GazeMoved -= OnGazeMoved;
|
||||
_gazeInputSource.GazeExited -= OnGazeExited;
|
||||
}
|
||||
}
|
||||
|
||||
private void ActivateGazeTargetItem(GazeTargetItem target)
|
||||
{
|
||||
if (_activeHitTargetTimes.IndexOf(target) == -1)
|
||||
{
|
||||
_activeHitTargetTimes.Add(target);
|
||||
|
||||
// calculate the time that the first DwellRepeat needs to be fired after. this will be updated every time a DwellRepeat is
|
||||
// fired to keep track of when the next one is to be fired after that.
|
||||
var nextStateTime = GetElementStateDelay(target.TargetElement, PointerState.Enter);
|
||||
|
||||
target.Reset(nextStateTime);
|
||||
}
|
||||
}
|
||||
|
||||
private GazeTargetItem GetHitTarget(Point gazePoint)
|
||||
{
|
||||
GazeTargetItem invokable = null;
|
||||
|
||||
if (!IsAlwaysActivated)
|
||||
{
|
||||
invokable = NonInvokeGazeTargetItem;
|
||||
}
|
||||
|
||||
switch (Window.Current.CoreWindow.ActivationMode)
|
||||
{
|
||||
case CoreWindowActivationMode.ActivatedInForeground:
|
||||
case CoreWindowActivationMode.ActivatedNotForeground:
|
||||
var elements = VisualTreeHelper.FindElementsInHostCoordinates(gazePoint, null, false);
|
||||
var element = elements.FirstOrDefault();
|
||||
|
||||
invokable = null;
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
invokable = GazeTargetItem.GetOrCreate(element);
|
||||
|
||||
while (element != null && !invokable.IsInvokable)
|
||||
{
|
||||
element = VisualTreeHelper.GetParent(element) as UIElement;
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
invokable = GazeTargetItem.GetOrCreate(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (element == null || !invokable.IsInvokable)
|
||||
{
|
||||
invokable = NonInvokeGazeTargetItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
Interaction interaction;
|
||||
do
|
||||
{
|
||||
interaction = GazeInput.GetInteraction(element);
|
||||
if (interaction == Interaction.Inherited)
|
||||
{
|
||||
element = GetInheritenceParent(element);
|
||||
}
|
||||
}
|
||||
while (interaction == Interaction.Inherited && element != null);
|
||||
|
||||
if (interaction == Interaction.Inherited)
|
||||
{
|
||||
interaction = GazeInput.Interaction;
|
||||
}
|
||||
|
||||
if (interaction != Interaction.Enabled)
|
||||
{
|
||||
invokable = NonInvokeGazeTargetItem;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return invokable;
|
||||
}
|
||||
|
||||
private GazeTargetItem ResolveHitTarget(Point gazePoint, TimeSpan timestamp)
|
||||
{
|
||||
// TODO: The existance of a GazeTargetItem should be used to indicate that
|
||||
// the target item is invokable. The method of invokation should be stored
|
||||
// within the GazeTargetItem when it is created and not recalculated when
|
||||
// subsequently needed.
|
||||
|
||||
// create GazeHistoryItem to deal with this sample
|
||||
var target = GetHitTarget(gazePoint);
|
||||
GazeHistoryItem historyItem = default;
|
||||
historyItem.HitTarget = target;
|
||||
historyItem.Timestamp = timestamp;
|
||||
historyItem.Duration = TimeSpan.Zero;
|
||||
Debug.Assert(historyItem.HitTarget != null, "historyItem.HitTarget should not be null");
|
||||
|
||||
// create new GazeTargetItem with a (default) total elapsed time of zero if one does not exist already.
|
||||
// this ensures that there will always be an entry for target elements in the code below.
|
||||
ActivateGazeTargetItem(target);
|
||||
target.LastTimestamp = timestamp;
|
||||
|
||||
// find elapsed time since we got the last hit target
|
||||
historyItem.Duration = timestamp - _lastTimestamp;
|
||||
if (historyItem.Duration > MAX_SINGLE_SAMPLE_DURATION)
|
||||
{
|
||||
historyItem.Duration = MAX_SINGLE_SAMPLE_DURATION;
|
||||
}
|
||||
|
||||
_gazeHistory.Add(historyItem);
|
||||
|
||||
// update the time this particular hit target has accumulated
|
||||
target.DetailedTime += historyItem.Duration;
|
||||
|
||||
// drop the oldest samples from the list until we have samples only
|
||||
// within the window we are monitoring
|
||||
//
|
||||
// historyItem is the last item we just appended a few lines above.
|
||||
for (var evOldest = _gazeHistory[0];
|
||||
historyItem.Timestamp - evOldest.Timestamp > _maxHistoryTime;
|
||||
evOldest = _gazeHistory[0])
|
||||
{
|
||||
_gazeHistory.RemoveAt(0);
|
||||
|
||||
// subtract the duration obtained from the oldest sample in _gazeHistory
|
||||
var targetItem = evOldest.HitTarget;
|
||||
Debug.Assert(targetItem.DetailedTime - evOldest.Duration >= TimeSpan.Zero, "DetailedTime of targetItem should be less than oldest history Duration");
|
||||
targetItem.DetailedTime -= evOldest.Duration;
|
||||
if (targetItem.ElementState != PointerState.PreEnter)
|
||||
{
|
||||
targetItem.OverflowTime += evOldest.Duration;
|
||||
}
|
||||
}
|
||||
|
||||
_lastTimestamp = timestamp;
|
||||
|
||||
// Return the most recent hit target
|
||||
// Intuition would tell us that we should return NOT the most recent
|
||||
// hitTarget, but the one with the most accumulated time in
|
||||
// in the maintained history. But the effect of that is that
|
||||
// the user will feel that they have clicked on the wrong thing
|
||||
// when they are looking at something else.
|
||||
// That is why we return the most recent hitTarget so that
|
||||
// when its dwell time has elapsed, it will be invoked
|
||||
return target;
|
||||
}
|
||||
|
||||
private void CheckIfExiting(TimeSpan curTimestamp)
|
||||
{
|
||||
for (int index = 0; index < _activeHitTargetTimes.Count; index++)
|
||||
{
|
||||
var targetItem = _activeHitTargetTimes[index];
|
||||
var targetElement = targetItem.TargetElement;
|
||||
var exitDelay = GetElementStateDelay(targetElement, PointerState.Exit);
|
||||
|
||||
var idleDuration = curTimestamp - targetItem.LastTimestamp;
|
||||
if (targetItem.ElementState != PointerState.PreEnter && idleDuration > exitDelay)
|
||||
{
|
||||
targetItem.ElementState = PointerState.PreEnter;
|
||||
|
||||
// Transitioning to exit - clear the cached fixated element
|
||||
_currentlyFixatedElement = null;
|
||||
|
||||
RaiseGazePointerEvent(targetItem, PointerState.Exit, targetItem.ElapsedTime);
|
||||
targetItem.GiveFeedback();
|
||||
|
||||
_activeHitTargetTimes.RemoveAt(index);
|
||||
|
||||
// remove all history samples referring to deleted hit target
|
||||
for (int i = 0; i < _gazeHistory.Count;)
|
||||
{
|
||||
var hitTarget = _gazeHistory[i].HitTarget;
|
||||
if (hitTarget.TargetElement == targetElement)
|
||||
{
|
||||
_gazeHistory.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// return because only one element can be exited at a time and at this point
|
||||
// we have done everything that we can do
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private const string[] pointerStates =
|
||||
{
|
||||
"Exit",
|
||||
"PreEnter",
|
||||
"Enter",
|
||||
"Fixation",
|
||||
"Dwel",
|
||||
"DwellRepeat"
|
||||
};
|
||||
*/
|
||||
|
||||
private void RaiseGazePointerEvent(GazeTargetItem target, PointerState state, TimeSpan elapsedTime)
|
||||
{
|
||||
var control = target?.TargetElement;
|
||||
|
||||
// assert(target != _rootElement);
|
||||
var gpea = new StateChangedEventArgs(control, state, elapsedTime);
|
||||
|
||||
/*
|
||||
* var buttonObj = dynamic_cast<Button >(target);
|
||||
* if (buttonObj && buttonObj.Content)
|
||||
* {
|
||||
* String buttonText = dynamic_cast<String>(buttonObj.Content);
|
||||
* Debug.WriteLine("GPE: %s . %s, %d", buttonText, PointerStates[(int)state], elapsedTime);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* Debug.WriteLine("GPE: 0x%08x . %s, %d", target != null ? target.GetHashCode() : 0, PointerStates[(int)state], elapsedTime);
|
||||
* }
|
||||
*/
|
||||
|
||||
var gazeElement = target != null ? GazeInput.GetGazeElement(control) : null;
|
||||
|
||||
if (gazeElement != null)
|
||||
{
|
||||
gazeElement.RaiseStateChanged(control, gpea);
|
||||
}
|
||||
|
||||
if (state == PointerState.Dwell)
|
||||
{
|
||||
var handled = false;
|
||||
|
||||
if (gazeElement != null)
|
||||
{
|
||||
var args = new DwellInvokedRoutedEventArgs();
|
||||
gazeElement.RaiseInvoked(control, args);
|
||||
handled = args.Handled;
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
target.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGazeEntered(
|
||||
GazeInputSourcePreview provider,
|
||||
GazeEnteredPreviewEventArgs args)
|
||||
{
|
||||
// Debug.WriteLine("Entered at %ld", args.CurrentPoint.Timestamp);
|
||||
_gazeCursor.IsGazeEntered = true;
|
||||
}
|
||||
|
||||
private void OnGazeMoved(
|
||||
GazeInputSourcePreview provider,
|
||||
GazeMovedPreviewEventArgs args)
|
||||
{
|
||||
if (!_isShuttingDown)
|
||||
{
|
||||
var intermediatePoints = args.GetIntermediatePoints();
|
||||
foreach (var point in intermediatePoints)
|
||||
{
|
||||
var position = point.EyeGazePosition;
|
||||
if (position != null)
|
||||
{
|
||||
_gazeCursor.IsGazeEntered = true;
|
||||
ProcessGazePoint(new TimeSpan((long)point.Timestamp * 10), position.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Debug.WriteLine("Null position eaten at %ld", point.Timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGazeExited(
|
||||
GazeInputSourcePreview provider,
|
||||
GazeExitedPreviewEventArgs args)
|
||||
{
|
||||
// Debug.WriteLine("Exited at %ld", args.CurrentPoint.Timestamp);
|
||||
_gazeCursor.IsGazeEntered = false;
|
||||
}
|
||||
|
||||
private void ProcessGazePoint(TimeSpan timestamp, Point position)
|
||||
{
|
||||
var ea = new GazeFilterArgs(position, timestamp);
|
||||
|
||||
var fa = Filter.Update(ea);
|
||||
_gazeCursor.Position = fa.Location;
|
||||
|
||||
if (_gazeEventCount != 0)
|
||||
{
|
||||
_gazeEventArgs.Set(fa.Location, timestamp);
|
||||
_gazeEvent?.Invoke(this, _gazeEventArgs);
|
||||
if (_gazeEventArgs.Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var targetItem = ResolveHitTarget(fa.Location, fa.Timestamp);
|
||||
Debug.Assert(targetItem != null, "targetItem is null when processing gaze point");
|
||||
|
||||
// Debug.WriteLine("ProcessGazePoint. [{0}, {1}], {2}", (int)fa.Location.X, (int)fa.Location.Y, fa.Timestamp);
|
||||
|
||||
// check to see if any element in _hitTargetTimes needs an exit event fired.
|
||||
// this ensures that all exit events are fired before enter event
|
||||
CheckIfExiting(fa.Timestamp);
|
||||
|
||||
PointerState nextState = (PointerState)((int)targetItem.ElementState + 1);
|
||||
|
||||
// Debug.WriteLine(targetItem.TargetElement.ToString());
|
||||
// Debug.WriteLine("\tState={0}, Elapsed={1}, NextStateTime={2}", targetItem.ElementState, targetItem.ElapsedTime, targetItem.NextStateTime);
|
||||
if (targetItem.ElapsedTime > targetItem.NextStateTime)
|
||||
{
|
||||
var prevStateTime = targetItem.NextStateTime;
|
||||
Debug.WriteLine(prevStateTime);
|
||||
|
||||
// prevent targetItem from ever actually transitioning into the DwellRepeat state so as
|
||||
// to continuously emit the DwellRepeat event
|
||||
if (nextState != PointerState.DwellRepeat)
|
||||
{
|
||||
targetItem.ElementState = nextState;
|
||||
nextState = (PointerState)((int)nextState + 1); // nextState++
|
||||
targetItem.NextStateTime += GetElementStateDelay(targetItem.TargetElement, nextState);
|
||||
|
||||
if (targetItem.ElementState == PointerState.Dwell)
|
||||
{
|
||||
targetItem.NextStateTime += GetElementStateDelay(targetItem.TargetElement, GazeInput.RepeatDelayDurationProperty, _defaultDwellRepeatDelay);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the NextStateTime by one dwell period, while continuing to stay in Dwell state
|
||||
targetItem.NextStateTime += GetElementStateDelay(targetItem.TargetElement, PointerState.DwellRepeat);
|
||||
}
|
||||
|
||||
if (targetItem.ElementState == PointerState.Dwell)
|
||||
{
|
||||
targetItem.RepeatCount++;
|
||||
if (targetItem.MaxDwellRepeatCount < targetItem.RepeatCount)
|
||||
{
|
||||
targetItem.NextStateTime = new TimeSpan(long.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetItem.ElementState == PointerState.Fixation)
|
||||
{
|
||||
// Cache the fixated item
|
||||
_currentlyFixatedElement = targetItem;
|
||||
|
||||
// We are about to transition into the Dwell state
|
||||
// If switch input is enabled, make sure dwell never completes
|
||||
// via eye gaze
|
||||
if (IsSwitchEnabled)
|
||||
{
|
||||
// Don't allow the next state (Dwell) to progress
|
||||
targetItem.NextStateTime = new TimeSpan(long.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
RaiseGazePointerEvent(targetItem, targetItem.ElementState, targetItem.ElapsedTime);
|
||||
}
|
||||
|
||||
targetItem.GiveFeedback();
|
||||
|
||||
_eyesOffTimer.Start();
|
||||
_lastTimestamp = fa.Timestamp;
|
||||
}
|
||||
|
||||
private void OnEyesOff(object sender, object ea)
|
||||
{
|
||||
_eyesOffTimer.Stop();
|
||||
|
||||
CheckIfExiting(_lastTimestamp + EyesOffDelay);
|
||||
RaiseGazePointerEvent(null, PointerState.Enter, EyesOffDelay);
|
||||
}
|
||||
|
||||
private void OnDeviceAdded(GazeDeviceWatcherPreview sender, GazeDeviceWatcherAddedPreviewEventArgs args)
|
||||
{
|
||||
_devices.Add(args.Device);
|
||||
|
||||
if (_devices.Count == 1)
|
||||
{
|
||||
IsDeviceAvailableChanged?.Invoke(null, null);
|
||||
|
||||
InitializeGazeInputSource();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDeviceRemoved(GazeDeviceWatcherPreview sender, GazeDeviceWatcherRemovedPreviewEventArgs args)
|
||||
{
|
||||
var index = 0;
|
||||
while (index < _devices.Count && _devices.ElementAt(index).Id != args.Device.Id)
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index < _devices.Count)
|
||||
{
|
||||
_devices.RemoveAt(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
_devices.RemoveAt(0);
|
||||
}
|
||||
|
||||
if (_devices.Count == 0)
|
||||
{
|
||||
IsDeviceAvailableChanged?.Invoke(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<int> _roots = new List<int>();
|
||||
|
||||
private readonly DispatcherTimer _eyesOffTimer;
|
||||
|
||||
private readonly GazeCursor _gazeCursor;
|
||||
|
||||
private readonly GazeDeviceWatcherPreview _watcher;
|
||||
private readonly List<GazeDevicePreview> _devices;
|
||||
|
||||
private readonly GazeEventArgs _gazeEventArgs = new GazeEventArgs();
|
||||
|
||||
private TimeSpan _eyesOffDelay;
|
||||
|
||||
// _offScreenElement is a pseudo-element that represents the area outside
|
||||
// the screen so we can track how long the user has been looking outside
|
||||
// the screen and appropriately trigger the EyesOff event
|
||||
private Control _offScreenElement;
|
||||
|
||||
// The value is the total time that FrameworkElement has been gazed at
|
||||
private List<GazeTargetItem> _activeHitTargetTimes;
|
||||
|
||||
// A vector to track the history of observed gaze targets
|
||||
private List<GazeHistoryItem> _gazeHistory;
|
||||
private TimeSpan _maxHistoryTime;
|
||||
|
||||
// Used to determine if exit events need to be fired by adding GAZE_IDLE_TIME to the last
|
||||
// saved timestamp
|
||||
private TimeSpan _lastTimestamp;
|
||||
|
||||
private GazeInputSourcePreview _gazeInputSource;
|
||||
|
||||
private TimeSpan _defaultFixation = DEFAULT_FIXATION_DELAY;
|
||||
private TimeSpan _defaultDwell = DEFAULT_DWELL_DELAY;
|
||||
private TimeSpan _defaultDwellRepeatDelay = DEFAULT_DWELL_REPEAT_DELAY;
|
||||
private TimeSpan _defaultRepeatDelay = DEFAULT_REPEAT_DELAY;
|
||||
private TimeSpan _defaultThreshold = DEFAULT_THRESHOLD_DELAY;
|
||||
private GazeTargetItem _currentlyFixatedElement;
|
||||
}
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GazeCursor.h"
|
||||
#include "GazeFeedbackPopupFactory.h"
|
||||
#include "GazeEventArgs.h"
|
||||
#include "IGazeFilter.h"
|
||||
#include "Interaction.h"
|
||||
#include "PointerState.h"
|
||||
|
||||
using namespace Platform::Collections;
|
||||
using namespace Windows::Devices::Input::Preview;
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
ref class GazeTargetItem;
|
||||
ref struct GazeHistoryItem;
|
||||
|
||||
/// <summary>
|
||||
/// Class of singleton object coordinating gaze input.
|
||||
/// </summary>
|
||||
public ref class GazePointer sealed
|
||||
{
|
||||
private:
|
||||
|
||||
// units in microseconds
|
||||
const TimeSpan DEFAULT_FIXATION_DELAY = TimeSpanFromMicroseconds(350000);
|
||||
const TimeSpan DEFAULT_DWELL_DELAY = TimeSpanFromMicroseconds(400000);
|
||||
const TimeSpan DEFAULT_DWELL_REPEAT_DELAY = TimeSpanFromMicroseconds(400000);
|
||||
const TimeSpan DEFAULT_REPEAT_DELAY = TimeSpanFromMicroseconds(400000);
|
||||
const TimeSpan DEFAULT_THRESHOLD_DELAY = TimeSpanFromMicroseconds(50000);
|
||||
const TimeSpan DEFAULT_MAX_HISTORY_DURATION = TimeSpanFromMicroseconds(3000000);
|
||||
const TimeSpan MAX_SINGLE_SAMPLE_DURATION = TimeSpanFromMicroseconds(100000);
|
||||
|
||||
const TimeSpan GAZE_IDLE_TIME{ 25000000 };
|
||||
|
||||
public:
|
||||
virtual ~GazePointer();
|
||||
|
||||
/// <summary>
|
||||
/// Loads a settings collection into GazePointer.
|
||||
/// </summary>
|
||||
void LoadSettings(ValueSet^ settings);
|
||||
|
||||
/// <summary>
|
||||
/// When in switch mode, will issue a click on the currently fixated element
|
||||
/// </summary>
|
||||
void Click();
|
||||
|
||||
/// <summary>
|
||||
/// Run device calibration.
|
||||
/// </summary>
|
||||
IAsyncOperation<bool>^ RequestCalibrationAsync();
|
||||
|
||||
event EventHandler<GazeEventArgs^>^ GazeEvent
|
||||
{
|
||||
EventRegistrationToken add(EventHandler<GazeEventArgs^>^ handler);
|
||||
void remove(EventRegistrationToken token);
|
||||
void raise(Object^ sender, GazeEventArgs^ e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The UIElement representing the cursor.
|
||||
/// </summary>
|
||||
property UIElement^ CursorElement
|
||||
{
|
||||
UIElement^ get() { return _gazeCursor->PopupChild; }
|
||||
void set(UIElement^ value) { _gazeCursor->PopupChild = value; }
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
event EventHandler<GazeEventArgs^>^ _gazeEvent;
|
||||
GazeEventArgs^ const _gazeEventArgs = ref new GazeEventArgs();
|
||||
int _gazeEventCount = 0;
|
||||
|
||||
internal:
|
||||
Brush^ _enterBrush = nullptr;
|
||||
|
||||
Brush^ _progressBrush = ref new SolidColorBrush(Colors::Green);
|
||||
|
||||
Brush^ _completeBrush = ref new SolidColorBrush(Colors::Red);
|
||||
|
||||
double _dwellStrokeThickness = 2;
|
||||
|
||||
Interaction _interaction = Interaction::Disabled;
|
||||
|
||||
GazeTargetItem^ _nonInvokeGazeTargetItem;
|
||||
|
||||
GazeFeedbackPopupFactory^ _gazeFeedbackPopupFactory = ref new GazeFeedbackPopupFactory();
|
||||
|
||||
internal:
|
||||
void Reset();
|
||||
void SetElementStateDelay(UIElement ^element, PointerState pointerState, TimeSpan stateDelay);
|
||||
TimeSpan GetElementStateDelay(UIElement ^element, DependencyProperty^ property, TimeSpan defaultValue);
|
||||
TimeSpan GetElementStateDelay(UIElement^ element, PointerState pointerState);
|
||||
|
||||
// Provide a configurable delay for when the EyesOffDelay event is fired
|
||||
// GOTCHA: this value requires that _eyesOffTimer is instantiated so that it
|
||||
// can update the timer interval
|
||||
property TimeSpan EyesOffDelay
|
||||
{
|
||||
TimeSpan get() { return _eyesOffDelay; }
|
||||
void set(TimeSpan value)
|
||||
{
|
||||
_eyesOffDelay = value;
|
||||
|
||||
// convert GAZE_IDLE_TIME units (microseconds) to 100-nanosecond units used
|
||||
// by TimeSpan struct
|
||||
_eyesOffTimer->Interval = EyesOffDelay;
|
||||
}
|
||||
}
|
||||
|
||||
// Pluggable filter for eye tracking sample data. This defaults to being set to the
|
||||
// NullFilter which performs no filtering of input samples.
|
||||
property IGazeFilter^ Filter;
|
||||
|
||||
property bool IsCursorVisible
|
||||
{
|
||||
bool get() { return _gazeCursor->IsCursorVisible; }
|
||||
void set(bool value) { _gazeCursor->IsCursorVisible = value; }
|
||||
}
|
||||
|
||||
property int CursorRadius
|
||||
{
|
||||
int get() { return _gazeCursor->CursorRadius; }
|
||||
void set(int value) { _gazeCursor->CursorRadius = value; }
|
||||
}
|
||||
|
||||
property bool IsSwitchEnabled
|
||||
{
|
||||
bool get() { return _isSwitchEnabled; }
|
||||
void set(bool value) { _isSwitchEnabled = value; }
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
property bool IsAlwaysActivated
|
||||
{
|
||||
bool get() { return _isAlwaysActivated; }
|
||||
void set(bool value) { _isAlwaysActivated = value; }
|
||||
}
|
||||
|
||||
internal:
|
||||
|
||||
static property GazePointer^ Instance { GazePointer^ get(); }
|
||||
EventRegistrationToken _unloadedToken;
|
||||
|
||||
void AddRoot(int proxyId);
|
||||
void RemoveRoot(int proxyId);
|
||||
|
||||
|
||||
property bool IsDeviceAvailable { bool get() { return _devices->Size != 0; }}
|
||||
event EventHandler<Object^>^ IsDeviceAvailableChanged;
|
||||
|
||||
private:
|
||||
|
||||
GazePointer();
|
||||
|
||||
private:
|
||||
|
||||
bool _initialized;
|
||||
bool _isShuttingDown;
|
||||
|
||||
TimeSpan GetDefaultPropertyValue(PointerState state);
|
||||
|
||||
void InitializeHistogram();
|
||||
void InitializeGazeInputSource();
|
||||
void DeinitializeGazeInputSource();
|
||||
|
||||
void ActivateGazeTargetItem(GazeTargetItem^ target);
|
||||
GazeTargetItem^ GetHitTarget(Point gazePoint);
|
||||
GazeTargetItem^ ResolveHitTarget(Point gazePoint, TimeSpan timestamp);
|
||||
|
||||
void CheckIfExiting(TimeSpan curTimestamp);
|
||||
void RaiseGazePointerEvent(GazeTargetItem^ target, PointerState state, TimeSpan elapsedTime);
|
||||
|
||||
void OnGazeEntered(
|
||||
GazeInputSourcePreview^ provider,
|
||||
GazeEnteredPreviewEventArgs^ args);
|
||||
void OnGazeMoved(
|
||||
GazeInputSourcePreview^ provider,
|
||||
GazeMovedPreviewEventArgs^ args);
|
||||
void OnGazeExited(
|
||||
GazeInputSourcePreview^ provider,
|
||||
GazeExitedPreviewEventArgs^ args);
|
||||
|
||||
void ProcessGazePoint(TimeSpan timestamp, Point position);
|
||||
|
||||
void OnEyesOff(Object ^sender, Object ^ea);
|
||||
|
||||
void OnDeviceAdded(GazeDeviceWatcherPreview^ sender, GazeDeviceWatcherAddedPreviewEventArgs^ args);
|
||||
void OnDeviceRemoved(GazeDeviceWatcherPreview^ sender, GazeDeviceWatcherRemovedPreviewEventArgs^ args);
|
||||
|
||||
private:
|
||||
Vector<int>^ _roots = ref new Vector<int>();
|
||||
|
||||
TimeSpan _eyesOffDelay;
|
||||
|
||||
GazeCursor^ _gazeCursor;
|
||||
DispatcherTimer^ _eyesOffTimer;
|
||||
|
||||
// _offScreenElement is a pseudo-element that represents the area outside
|
||||
// the screen so we can track how long the user has been looking outside
|
||||
// the screen and appropriately trigger the EyesOff event
|
||||
Control^ _offScreenElement;
|
||||
|
||||
// The value is the total time that FrameworkElement has been gazed at
|
||||
Vector<GazeTargetItem^>^ _activeHitTargetTimes;
|
||||
|
||||
// A vector to track the history of observed gaze targets
|
||||
Vector<GazeHistoryItem^>^ _gazeHistory;
|
||||
TimeSpan _maxHistoryTime;
|
||||
|
||||
// Used to determine if exit events need to be fired by adding GAZE_IDLE_TIME to the last
|
||||
// saved timestamp
|
||||
TimeSpan _lastTimestamp;
|
||||
|
||||
GazeInputSourcePreview^ _gazeInputSource;
|
||||
EventRegistrationToken _gazeEnteredToken;
|
||||
EventRegistrationToken _gazeMovedToken;
|
||||
EventRegistrationToken _gazeExitedToken;
|
||||
|
||||
GazeDeviceWatcherPreview^ _watcher;
|
||||
Vector<GazeDevicePreview^>^ _devices;
|
||||
EventRegistrationToken _deviceAddedToken;
|
||||
EventRegistrationToken _deviceRemovedToken;
|
||||
|
||||
TimeSpan _defaultFixation = DEFAULT_FIXATION_DELAY;
|
||||
TimeSpan _defaultDwell = DEFAULT_DWELL_DELAY;
|
||||
TimeSpan _defaultDwellRepeatDelay = DEFAULT_DWELL_REPEAT_DELAY;
|
||||
TimeSpan _defaultRepeatDelay = DEFAULT_REPEAT_DELAY;
|
||||
TimeSpan _defaultThreshold = DEFAULT_THRESHOLD_DELAY;
|
||||
|
||||
bool _isAlwaysActivated;
|
||||
bool _isSwitchEnabled;
|
||||
GazeTargetItem^ _currentlyFixatedElement;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -1,139 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazePointerProxy.h"
|
||||
#include "GazePointer.h"
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// The IsLoaded heuristic for testing whether a FrameworkElement is in the visual tree.
|
||||
/// </summary>
|
||||
static bool IsLoadedHeuristic(FrameworkElement^ element)
|
||||
{
|
||||
bool isLoaded;
|
||||
|
||||
// element.Loaded has already happened if it is in the visual tree...
|
||||
auto parent = VisualTreeHelper::GetParent(element);
|
||||
if (parent != nullptr)
|
||||
{
|
||||
isLoaded = true;
|
||||
}
|
||||
// ...or...
|
||||
else
|
||||
{
|
||||
// ...if the element is a dynamically created Popup that has been opened.
|
||||
auto popup = dynamic_cast<Popup^>(element);
|
||||
isLoaded = popup != nullptr && popup->IsOpen;
|
||||
}
|
||||
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
DependencyProperty^ GazePointerProxy::GazePointerProxyProperty::get()
|
||||
{
|
||||
// The attached property registration.
|
||||
static auto value = DependencyProperty::RegisterAttached("_GazePointerProxy", GazePointerProxy::typeid, GazePointerProxy::typeid,
|
||||
ref new PropertyMetadata(nullptr));
|
||||
return value;
|
||||
}
|
||||
|
||||
GazePointerProxy::GazePointerProxy(FrameworkElement^ element)
|
||||
{
|
||||
static int lastId = 0;
|
||||
lastId++;
|
||||
_uniqueId = lastId;
|
||||
|
||||
_isLoaded = IsLoadedHeuristic(element);
|
||||
|
||||
// Start watching for the element to enter and leave the visual tree.
|
||||
element->Loaded += ref new RoutedEventHandler(this, &GazePointerProxy::OnLoaded);
|
||||
element->Unloaded += ref new RoutedEventHandler(this, &GazePointerProxy::OnUnloaded);
|
||||
}
|
||||
|
||||
void GazePointerProxy::SetInteraction(FrameworkElement^ element, Interaction value)
|
||||
{
|
||||
// Get or create a GazePointerProxy for element.
|
||||
auto proxy = safe_cast<GazePointerProxy^>(element->GetValue(GazePointerProxyProperty));
|
||||
if (proxy == nullptr)
|
||||
{
|
||||
proxy = ref new GazePointerProxy(element);
|
||||
element->SetValue(GazePointerProxyProperty, proxy);
|
||||
}
|
||||
|
||||
// Set the proxy's _isEnabled value.
|
||||
proxy->SetIsEnabled(element, value == Interaction::Enabled);
|
||||
}
|
||||
|
||||
void GazePointerProxy::SetIsEnabled(Object^ sender, bool value)
|
||||
{
|
||||
// If we have a new value...
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
// ...record the new value.
|
||||
_isEnabled = value;
|
||||
|
||||
// If we are in the visual tree...
|
||||
if (_isLoaded)
|
||||
{
|
||||
// ...if we're being enabled...
|
||||
if (value)
|
||||
{
|
||||
// ...count the element in...
|
||||
GazePointer::Instance->AddRoot(_uniqueId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...otherwise count the element out.
|
||||
GazePointer::Instance->RemoveRoot(_uniqueId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointerProxy::OnLoaded(Object^ sender, RoutedEventArgs^ args)
|
||||
{
|
||||
assert(IsLoadedHeuristic(safe_cast<FrameworkElement^>(sender)));
|
||||
|
||||
if (!_isLoaded)
|
||||
{
|
||||
// Record that we are now loaded.
|
||||
_isLoaded = true;
|
||||
|
||||
// If we were previously enabled...
|
||||
if (_isEnabled)
|
||||
{
|
||||
// ...we can now be counted as actively enabled.
|
||||
GazePointer::Instance->AddRoot(_uniqueId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug::WriteLine(L"Unexpected Load");
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointerProxy::OnUnloaded(Object^ sender, RoutedEventArgs^ args)
|
||||
{
|
||||
assert(!IsLoadedHeuristic(safe_cast<FrameworkElement^>(sender)));
|
||||
|
||||
if (_isLoaded)
|
||||
{
|
||||
// Record that we have left the visual tree.
|
||||
_isLoaded = false;
|
||||
|
||||
// If we are set as enabled...
|
||||
if (_isEnabled)
|
||||
{
|
||||
// ...we no longer count as being actively enabled (because we have fallen out the visual tree).
|
||||
GazePointer::Instance->RemoveRoot(_uniqueId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug::WriteLine(L"Unexpected unload");
|
||||
}
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,184 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class that helps track which UIElements in the visual tree are enabled.
|
||||
///
|
||||
/// The GazePointer is enabled when one or more UIElements in the visual tree have
|
||||
/// their GazeInput.InteractionProperty value set to Enabled. Notice that there are
|
||||
/// two conditions for enablement: that attached property is Enabled; that the UIElement
|
||||
/// is in the visual tree.
|
||||
/// </summary>
|
||||
internal class GazePointerProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The IsLoaded heuristic for testing whether a FrameworkElement is in the visual tree.
|
||||
/// </summary>
|
||||
private static bool IsLoadedHeuristic(FrameworkElement element)
|
||||
{
|
||||
bool isLoaded;
|
||||
|
||||
// element.Loaded has already happened if it is in the visual tree...
|
||||
var parent = VisualTreeHelper.GetParent(element);
|
||||
if (parent != null)
|
||||
{
|
||||
isLoaded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...or...
|
||||
// ...if the element is a dynamically created Popup that has been opened.
|
||||
var popup = element as Popup;
|
||||
isLoaded = popup != null && popup.IsOpen;
|
||||
}
|
||||
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A private attached property for associating an instance of this class with the UIElement
|
||||
/// to which it refers.
|
||||
/// </summary>
|
||||
private static readonly DependencyProperty _gazePointerProxyProperty = DependencyProperty.RegisterAttached("_GazePointerProxy", typeof(GazePointerProxy), typeof(GazePointerProxy), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Method called when the GazeInput.Interaction attached property is set to a new value.
|
||||
/// </summary>
|
||||
/// <param name="element">The element being set. May be null to indicate whole user interface.</param>
|
||||
/// <param name="value">The interaction enablement value being set.</param>
|
||||
internal static void SetInteraction(FrameworkElement element, Interaction value)
|
||||
{
|
||||
// Get or create a GazePointerProxy for element.
|
||||
if (!(element.GetValue(_gazePointerProxyProperty) is GazePointerProxy proxy))
|
||||
{
|
||||
proxy = new GazePointerProxy(element);
|
||||
element.SetValue(_gazePointerProxyProperty, proxy);
|
||||
}
|
||||
|
||||
// Set the proxy's _isEnabled value.
|
||||
proxy.SetIsEnabled(element, value == Interaction.Enabled);
|
||||
}
|
||||
|
||||
private static int _lastId = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GazePointerProxy"/> class.
|
||||
/// </summary>
|
||||
/// <param name="element">The element proxy is attaching to.</param>
|
||||
private GazePointerProxy(FrameworkElement element)
|
||||
{
|
||||
_lastId++;
|
||||
_uniqueId = _lastId;
|
||||
|
||||
_isLoaded = IsLoadedHeuristic(element);
|
||||
|
||||
// Start watching for the element to enter and leave the visual tree.
|
||||
element.Loaded += OnLoaded;
|
||||
element.Unloaded += OnUnloaded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the enablement of this proxy.
|
||||
/// </summary>
|
||||
/// <param name="sender">The object setting the enable value.</param>
|
||||
/// <param name="value">The new enable value.</param>
|
||||
private void SetIsEnabled(object sender, bool value)
|
||||
{
|
||||
// If we have a new value...
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
// ...record the new value.
|
||||
_isEnabled = value;
|
||||
|
||||
// If we are in the visual tree...
|
||||
if (_isLoaded)
|
||||
{
|
||||
// ...if we're being enabled...
|
||||
if (value)
|
||||
{
|
||||
// ...count the element in...
|
||||
GazePointer.Instance.AddRoot(_uniqueId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...otherwise count the element out.
|
||||
GazePointer.Instance.RemoveRoot(_uniqueId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The handler to be called when the corresponding element joins the visual tree.
|
||||
/// </summary>
|
||||
private void OnLoaded(object sender, RoutedEventArgs args)
|
||||
{
|
||||
Debug.Assert(IsLoadedHeuristic(sender as FrameworkElement), "Should not be loaded if this is called");
|
||||
|
||||
if (!_isLoaded)
|
||||
{
|
||||
// Record that we are now loaded.
|
||||
_isLoaded = true;
|
||||
|
||||
// If we were previously enabled...
|
||||
if (_isEnabled)
|
||||
{
|
||||
// ...we can now be counted as actively enabled.
|
||||
GazePointer.Instance.AddRoot(_uniqueId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("Unexpected Load");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The handler to be called when the corresponding element leaves the visual tree.
|
||||
/// </summary>
|
||||
private void OnUnloaded(object sender, RoutedEventArgs args)
|
||||
{
|
||||
Debug.Assert(!IsLoadedHeuristic(sender as FrameworkElement), "Should be loaded if this is called");
|
||||
|
||||
if (_isLoaded)
|
||||
{
|
||||
// Record that we have left the visual tree.
|
||||
_isLoaded = false;
|
||||
|
||||
// If we are set as enabled...
|
||||
if (_isEnabled)
|
||||
{
|
||||
// ...we no longer count as being actively enabled (because we have fallen out the visual tree).
|
||||
GazePointer.Instance.RemoveRoot(_uniqueId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("Unexpected unload");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-zero ID associated with this instance.
|
||||
/// </summary>
|
||||
private readonly int _uniqueId;
|
||||
|
||||
/// <summary>
|
||||
/// Indicator that the corresponding element is part of the visual tree.
|
||||
/// </summary>
|
||||
private bool _isLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Boolean representing whether gaze is enabled for the corresponding element and its subtree.
|
||||
/// </summary>
|
||||
private bool _isEnabled;
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Interaction.h"
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// Helper class that helps track which UIElements in the visual tree are enabled.
|
||||
///
|
||||
/// The GazePointer is enabled when one or more UIElements in the visual tree have
|
||||
/// their GazeInput.InteractionProperty value set to Enabled. Notice that there are
|
||||
/// two conditions for enablement: that attached property is Enabled; that the UIElement
|
||||
/// is in the visual tree.
|
||||
/// </summary>
|
||||
private ref class GazePointerProxy sealed
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// A private attached property for associating an instance of this class with the UIElement
|
||||
/// to which it refers.
|
||||
/// </summary>
|
||||
static property DependencyProperty^ GazePointerProxyProperty { DependencyProperty^ get(); };
|
||||
|
||||
internal:
|
||||
|
||||
/// <summary>
|
||||
/// Method called when the GazeInput.Interaction attached property is set to a new value.
|
||||
/// </summary>
|
||||
/// <param name="element">The element being set. May be null to indicate whole user interface.</param>
|
||||
/// <param name="value">The interaction enablement value being set.</param>
|
||||
static void SetInteraction(FrameworkElement^ element, Interaction value);
|
||||
|
||||
private:
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="element">The element proxy is attaching to.</param>
|
||||
GazePointerProxy(FrameworkElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Set the enablement of this proxy.
|
||||
/// </summary>
|
||||
/// <param name="sender">The object setting the enable value.</param>
|
||||
/// <param name="value">The new enable value.</param>
|
||||
void SetIsEnabled(Object^ sender, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// The handler to be called when the corresponding element joins the visual tree.
|
||||
/// </summary>
|
||||
void OnLoaded(Object^ sender, RoutedEventArgs^ args);
|
||||
|
||||
/// <summary>
|
||||
/// The handler to be called when the corresponding element leaves the visual tree.
|
||||
/// </summary>
|
||||
void OnUnloaded(Object^ sender, RoutedEventArgs^ args);
|
||||
|
||||
private:
|
||||
|
||||
/// <summary>
|
||||
/// Non-zero ID associated with this instance.
|
||||
/// </summary>
|
||||
int _uniqueId;
|
||||
|
||||
/// <summary>
|
||||
/// Indicator that the corresponding element is part of the visual tree.
|
||||
/// </summary>
|
||||
bool _isLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Boolean representing whether gaze is enabled for the corresponding element and its subtree.
|
||||
/// </summary>
|
||||
bool _isEnabled;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
||||
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeSettingsHelper.h"
|
||||
|
||||
using namespace concurrency;
|
||||
using namespace Windows::ApplicationModel::AppService;
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
GazeSettingsHelper::GazeSettingsHelper()
|
||||
{
|
||||
}
|
||||
|
||||
Windows::Foundation::IAsyncAction^ GazeSettingsHelper::RetrieveSharedSettings(ValueSet^ settings)
|
||||
{
|
||||
return create_async([settings] {
|
||||
// Setup a new app service connection
|
||||
AppServiceConnection^ connection = ref new AppServiceConnection();
|
||||
connection->AppServiceName = "com.microsoft.ectksettings";
|
||||
connection->PackageFamilyName = "Microsoft.EyeControlToolkitSettings_s9y1p3hwd5qda";
|
||||
|
||||
// open the connection
|
||||
return create_task(connection->OpenAsync()).then([settings, connection](AppServiceConnectionStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case AppServiceConnectionStatus::Success:
|
||||
// The new connection opened successfully
|
||||
// Set up the inputs and send a message to the service
|
||||
return create_task(connection->SendMessageAsync(ref new ValueSet()));
|
||||
break;
|
||||
|
||||
default:
|
||||
case AppServiceConnectionStatus::AppNotInstalled:
|
||||
case AppServiceConnectionStatus::AppUnavailable:
|
||||
case AppServiceConnectionStatus::AppServiceUnavailable:
|
||||
case AppServiceConnectionStatus::Unknown:
|
||||
// All return paths need to return a task of type AppServiceResponse, so fake it
|
||||
AppServiceResponse ^ response = nullptr;
|
||||
return task_from_result(response);
|
||||
}
|
||||
}).then([settings](AppServiceResponse^ response)
|
||||
{
|
||||
if (response == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (response->Status)
|
||||
{
|
||||
case AppServiceResponseStatus::Success:
|
||||
for each (auto item in response->Message)
|
||||
{
|
||||
settings->Insert(item->Key, item->Value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case AppServiceResponseStatus::Failure:
|
||||
case AppServiceResponseStatus::ResourceLimitsExceeded:
|
||||
case AppServiceResponseStatus::Unknown:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}); // create_async()
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,73 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.AppService;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class to read a ValueSet and retrieve relevant settings
|
||||
/// </summary>
|
||||
public class GazeSettingsHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves settings as a ValueSet from a shared store.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public static async Task RetrieveSharedSettings(ValueSet settings)
|
||||
{
|
||||
// Setup a new app service connection
|
||||
AppServiceConnection connection = new AppServiceConnection();
|
||||
connection.AppServiceName = "com.microsoft.ectksettings";
|
||||
connection.PackageFamilyName = "Microsoft.EyeControlToolkitSettings_s9y1p3hwd5qda";
|
||||
|
||||
// open the connection
|
||||
var status = await connection.OpenAsync();
|
||||
switch (status)
|
||||
{
|
||||
case AppServiceConnectionStatus.Success:
|
||||
// The new connection opened successfully
|
||||
// Set up the inputs and send a message to the service
|
||||
var response = await connection.SendMessageAsync(new ValueSet());
|
||||
|
||||
if (response == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (response.Status)
|
||||
{
|
||||
case AppServiceResponseStatus.Success:
|
||||
foreach (var item in response.Message)
|
||||
{
|
||||
settings.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
case AppServiceResponseStatus.Failure:
|
||||
case AppServiceResponseStatus.ResourceLimitsExceeded:
|
||||
case AppServiceResponseStatus.Unknown:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
case AppServiceConnectionStatus.AppNotInstalled:
|
||||
case AppServiceConnectionStatus.AppUnavailable:
|
||||
case AppServiceConnectionStatus.AppServiceUnavailable:
|
||||
case AppServiceConnectionStatus.Unknown:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private GazeSettingsHelper()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
/// <summary>
|
||||
/// A helper class to read a ValueSet and retrieve relevant settings
|
||||
/// </summary>
|
||||
public ref class GazeSettingsHelper sealed
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves settings as a ValueSet from a shared store.
|
||||
/// </summary>
|
||||
static Windows::Foundation::IAsyncAction^ RetrieveSharedSettings(ValueSet^ settings);
|
||||
|
||||
private:
|
||||
GazeSettingsHelper();
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -1,47 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeStats.h"
|
||||
|
||||
using namespace Platform::Collections;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
GazeStats::GazeStats(int maxHistoryLen)
|
||||
{
|
||||
_maxHistoryLen = maxHistoryLen;
|
||||
_history = ref new Vector<Point>();
|
||||
}
|
||||
|
||||
void GazeStats::Reset()
|
||||
{
|
||||
_sumX = 0;
|
||||
_sumY = 0;
|
||||
_sumSquaredX = 0;
|
||||
_sumSquaredY = 0;
|
||||
_history->Clear();
|
||||
}
|
||||
|
||||
void GazeStats::Update(float x, float y)
|
||||
{
|
||||
Point pt(x, y);
|
||||
_history->Append(pt);
|
||||
|
||||
if (_history->Size > _maxHistoryLen)
|
||||
{
|
||||
auto oldest = _history->GetAt(0);
|
||||
_history->RemoveAt(0);
|
||||
|
||||
_sumX -= oldest.X;
|
||||
_sumY -= oldest.Y;
|
||||
_sumSquaredX -= oldest.X * oldest.X;
|
||||
_sumSquaredY -= oldest.Y * oldest.Y;
|
||||
}
|
||||
_sumX += x;
|
||||
_sumY += y;
|
||||
_sumSquaredX += x * x;
|
||||
_sumSquaredY += y * y;
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,105 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides basic stats for eye gaze
|
||||
/// </summary>
|
||||
public class GazeStats
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GazeStats"/> class.
|
||||
/// </summary>
|
||||
/// <param name="maxHistoryLen">Defines the size of the circular history for the calculations of stats</param>
|
||||
public GazeStats(int maxHistoryLen)
|
||||
{
|
||||
_maxHistoryLen = maxHistoryLen;
|
||||
_history = new List<Point>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the history, and stats
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_sumX = 0;
|
||||
_sumY = 0;
|
||||
_sumSquaredX = 0;
|
||||
_sumSquaredY = 0;
|
||||
_history.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new item to the history
|
||||
/// </summary>
|
||||
/// <param name="x">X axis of the new point</param>
|
||||
/// <param name="y">Y axis of the new point</param>
|
||||
public void Update(float x, float y)
|
||||
{
|
||||
var pt = new Point(x, y);
|
||||
_history.Add(pt);
|
||||
|
||||
if (_history.Count > _maxHistoryLen)
|
||||
{
|
||||
var oldest = _history[0];
|
||||
_history.RemoveAt(0);
|
||||
|
||||
_sumX -= oldest.X;
|
||||
_sumY -= oldest.Y;
|
||||
_sumSquaredX -= oldest.X * oldest.X;
|
||||
_sumSquaredY -= oldest.Y * oldest.Y;
|
||||
}
|
||||
|
||||
_sumX += x;
|
||||
_sumY += y;
|
||||
_sumSquaredX += x * x;
|
||||
_sumSquaredY += y * y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mean of the history of points
|
||||
/// </summary>
|
||||
public Point Mean
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = _history.Count;
|
||||
return new Point((float)_sumX / count, (float)_sumY / count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the StandardDeviation of the items on the history => sqrt(Variance) = sqrt(E[X^2] – (E[X])^2)
|
||||
/// </summary>
|
||||
public Point StandardDeviation
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = _history.Count;
|
||||
if (count < _maxHistoryLen)
|
||||
{
|
||||
return new Point(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
double meanX = _sumX / count;
|
||||
double meanY = _sumY / count;
|
||||
float stddevX = (float)Math.Sqrt((_sumSquaredX / count) - (meanX * meanX));
|
||||
float stddevY = (float)Math.Sqrt((_sumSquaredY / count) - (meanY * meanY));
|
||||
return new Point(stddevX, stddevY);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly int _maxHistoryLen;
|
||||
private readonly List<Point> _history;
|
||||
private double _sumX;
|
||||
private double _sumY;
|
||||
private double _sumSquaredX;
|
||||
private double _sumSquaredY;
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
using namespace Platform::Collections;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
public ref class GazeStats sealed
|
||||
{
|
||||
public:
|
||||
GazeStats(int maxHistoryLen);
|
||||
void Reset();
|
||||
void Update(float x, float y);
|
||||
|
||||
property Point Mean
|
||||
{
|
||||
Point get()
|
||||
{
|
||||
UINT count = _history->Size;
|
||||
return Point((float)_sumX / count, (float)_sumY / count);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// StdDev = sqrt(Variance) = sqrt(E[X^2] – (E[X])^2)
|
||||
//
|
||||
property Point StandardDeviation
|
||||
{
|
||||
Point get()
|
||||
{
|
||||
UINT count = _history->Size;
|
||||
if (count < _maxHistoryLen)
|
||||
{
|
||||
return Point(0.0f, 0.0f);
|
||||
}
|
||||
double meanX = _sumX / count;
|
||||
double meanY = _sumY / count;
|
||||
float stddevX = (float)sqrt((_sumSquaredX / count) - (meanX * meanX));
|
||||
float stddevY = (float)sqrt((_sumSquaredY / count) - (meanY * meanY));
|
||||
return Point(stddevX, stddevY);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
UINT _maxHistoryLen;
|
||||
double _sumX;
|
||||
double _sumY;
|
||||
double _sumSquaredX;
|
||||
double _sumSquaredY;
|
||||
Vector<Point>^ _history;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -1,304 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeTargetItem.h"
|
||||
#include "GazePointer.h"
|
||||
|
||||
#include "GazeElement.h"
|
||||
#include "GazeFeedbackPopupFactory.h"
|
||||
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Provider;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
static DependencyProperty^ GazeTargetItemProperty = DependencyProperty::RegisterAttached("_GazeTargetItem", GazeTargetItem::typeid, GazeTargetItem::typeid, ref new PropertyMetadata(nullptr));
|
||||
|
||||
template<PatternInterface P, typename T>
|
||||
ref class PatternGazeTargetItem abstract : public GazeTargetItem
|
||||
{
|
||||
internal:
|
||||
|
||||
PatternGazeTargetItem(UIElement^ element)
|
||||
: GazeTargetItem(element)
|
||||
{
|
||||
}
|
||||
|
||||
static T^ GetPattern(AutomationPeer^ peer)
|
||||
{
|
||||
auto pattern = peer->GetPattern(P);
|
||||
return safe_cast<T^>(pattern);
|
||||
}
|
||||
|
||||
static bool IsCandidate(AutomationPeer^ peer)
|
||||
{
|
||||
auto provider = GetPattern(peer);
|
||||
return provider != nullptr;
|
||||
}
|
||||
|
||||
void Invoke() override sealed
|
||||
{
|
||||
auto peer = FrameworkElementAutomationPeer::FromElement(TargetElement);
|
||||
auto provider = GetPattern(peer);
|
||||
Invoke(provider);
|
||||
}
|
||||
|
||||
virtual void Invoke(T^ provider) = 0;
|
||||
};
|
||||
|
||||
ref class InvokePatternGazeTargetItem : public PatternGazeTargetItem<PatternInterface::Invoke, IInvokeProvider>
|
||||
{
|
||||
internal:
|
||||
|
||||
InvokePatternGazeTargetItem(UIElement^ element)
|
||||
: PatternGazeTargetItem(element)
|
||||
{
|
||||
}
|
||||
|
||||
void Invoke(IInvokeProvider^ provider) override sealed
|
||||
{
|
||||
provider->Invoke();
|
||||
}
|
||||
};
|
||||
|
||||
ref class TogglePatternGazeTargetItem : public PatternGazeTargetItem<PatternInterface::Toggle, IToggleProvider>
|
||||
{
|
||||
internal:
|
||||
|
||||
TogglePatternGazeTargetItem(UIElement^ element)
|
||||
: PatternGazeTargetItem(element)
|
||||
{
|
||||
}
|
||||
|
||||
void Invoke(IToggleProvider^ provider) override
|
||||
{
|
||||
provider->Toggle();
|
||||
}
|
||||
};
|
||||
|
||||
ref class SelectionItemPatternGazeTargetItem : public PatternGazeTargetItem<PatternInterface::SelectionItem, ISelectionItemProvider>
|
||||
{
|
||||
internal:
|
||||
|
||||
SelectionItemPatternGazeTargetItem(UIElement^ element)
|
||||
: PatternGazeTargetItem(element)
|
||||
{
|
||||
}
|
||||
|
||||
void Invoke(ISelectionItemProvider^ provider) override
|
||||
{
|
||||
provider->Select();
|
||||
}
|
||||
};
|
||||
|
||||
ref class ExpandCollapsePatternGazeTargetItem : public PatternGazeTargetItem<PatternInterface::ExpandCollapse, IExpandCollapseProvider>
|
||||
{
|
||||
internal:
|
||||
|
||||
ExpandCollapsePatternGazeTargetItem(UIElement^ element)
|
||||
: PatternGazeTargetItem(element)
|
||||
{
|
||||
}
|
||||
|
||||
void Invoke(IExpandCollapseProvider^ provider) override
|
||||
{
|
||||
switch (provider->ExpandCollapseState)
|
||||
{
|
||||
case ExpandCollapseState::Collapsed:
|
||||
provider->Expand();
|
||||
break;
|
||||
|
||||
case ExpandCollapseState::Expanded:
|
||||
provider->Collapse();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ref class ComboBoxItemGazeTargetItem sealed : GazeTargetItem
|
||||
{
|
||||
internal:
|
||||
|
||||
ComboBoxItemGazeTargetItem(UIElement^ element)
|
||||
: GazeTargetItem(element)
|
||||
{
|
||||
}
|
||||
|
||||
void Invoke() override
|
||||
{
|
||||
auto peer = FrameworkElementAutomationPeer::FromElement(TargetElement);
|
||||
auto comboBoxItemAutomationPeer = dynamic_cast<ComboBoxItemAutomationPeer^>(peer);
|
||||
auto comboBoxItem = safe_cast<ComboBoxItem^>(comboBoxItemAutomationPeer->Owner);
|
||||
|
||||
AutomationPeer^ ancestor = comboBoxItemAutomationPeer;
|
||||
auto comboBoxAutomationPeer = dynamic_cast<ComboBoxAutomationPeer^>(ancestor);
|
||||
while (comboBoxAutomationPeer == nullptr)
|
||||
{
|
||||
ancestor = safe_cast<AutomationPeer^>(ancestor->Navigate(AutomationNavigationDirection::Parent));
|
||||
comboBoxAutomationPeer = dynamic_cast<ComboBoxAutomationPeer^>(ancestor);
|
||||
}
|
||||
|
||||
comboBoxItem->IsSelected = true;
|
||||
comboBoxAutomationPeer->Collapse();
|
||||
}
|
||||
};
|
||||
|
||||
ref class PivotItemGazeTargetItem sealed : GazeTargetItem
|
||||
{
|
||||
internal:
|
||||
|
||||
PivotItemGazeTargetItem(UIElement^ element)
|
||||
: GazeTargetItem(element)
|
||||
{
|
||||
}
|
||||
|
||||
void Invoke() override
|
||||
{
|
||||
auto headerItem = safe_cast<PivotHeaderItem^>(TargetElement);
|
||||
auto headerPanel = safe_cast<PivotHeaderPanel^>(VisualTreeHelper::GetParent(headerItem));
|
||||
unsigned index;
|
||||
headerPanel->Children->IndexOf(headerItem, &index);
|
||||
|
||||
DependencyObject^ walker = headerPanel;
|
||||
Pivot^ pivot;
|
||||
do
|
||||
{
|
||||
walker = VisualTreeHelper::GetParent(walker);
|
||||
pivot = dynamic_cast<Pivot^>(walker);
|
||||
} while (pivot == nullptr);
|
||||
|
||||
pivot->SelectedIndex = index;
|
||||
}
|
||||
};
|
||||
|
||||
GazeTargetItem^ GazeTargetItem::GetOrCreate(UIElement^ element)
|
||||
{
|
||||
GazeTargetItem^ item;
|
||||
|
||||
auto value = element->ReadLocalValue(GazeTargetItemProperty);
|
||||
|
||||
if (value != DependencyProperty::UnsetValue)
|
||||
{
|
||||
item = safe_cast<GazeTargetItem^>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto peer = FrameworkElementAutomationPeer::FromElement(element);
|
||||
|
||||
if (peer == nullptr)
|
||||
{
|
||||
if (dynamic_cast<PivotHeaderItem^>(element) != nullptr)
|
||||
{
|
||||
item = ref new PivotItemGazeTargetItem(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = GazePointer::Instance->_nonInvokeGazeTargetItem;
|
||||
}
|
||||
}
|
||||
else if (InvokePatternGazeTargetItem::IsCandidate(peer))
|
||||
{
|
||||
item = ref new InvokePatternGazeTargetItem(element);
|
||||
}
|
||||
else if (TogglePatternGazeTargetItem::IsCandidate(peer))
|
||||
{
|
||||
item = ref new TogglePatternGazeTargetItem(element);
|
||||
}
|
||||
else if (SelectionItemPatternGazeTargetItem::IsCandidate(peer))
|
||||
{
|
||||
item = ref new SelectionItemPatternGazeTargetItem(element);
|
||||
}
|
||||
else if (ExpandCollapsePatternGazeTargetItem::IsCandidate(peer))
|
||||
{
|
||||
item = ref new ExpandCollapsePatternGazeTargetItem(element);
|
||||
}
|
||||
else if (dynamic_cast<ComboBoxItemAutomationPeer^>(peer) != nullptr)
|
||||
{
|
||||
item = ref new ComboBoxItemGazeTargetItem(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = GazePointer::Instance->_nonInvokeGazeTargetItem;
|
||||
}
|
||||
|
||||
element->SetValue(GazeTargetItemProperty, item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void GazeTargetItem::RaiseProgressEvent(DwellProgressState state)
|
||||
{
|
||||
// TODO: We should eliminate non-invokable controls before we arrive here!
|
||||
if (dynamic_cast<Page^>(TargetElement) != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_notifiedProgressState != state || state == DwellProgressState::Progressing)
|
||||
{
|
||||
auto handled = false;
|
||||
|
||||
auto gazeElement = GazeInput::GetGazeElement(TargetElement);
|
||||
if (gazeElement != nullptr)
|
||||
{
|
||||
handled = gazeElement->RaiseProgressFeedback(TargetElement, state, ElapsedTime - _prevStateTime, _nextStateTime - _prevStateTime);
|
||||
}
|
||||
|
||||
if (!handled && state != DwellProgressState::Idle)
|
||||
{
|
||||
if (_feedbackPopup == nullptr)
|
||||
{
|
||||
_feedbackPopup = GazePointer::Instance->_gazeFeedbackPopupFactory->Get();
|
||||
}
|
||||
|
||||
auto control = safe_cast<FrameworkElement^>(TargetElement);
|
||||
|
||||
auto transform = control->TransformToVisual(_feedbackPopup);
|
||||
auto bounds = transform->TransformBounds(*ref new Rect(*ref new Point(0, 0),
|
||||
*ref new Size(safe_cast<float>(control->ActualWidth), safe_cast<float>(control->ActualHeight))));
|
||||
auto rectangle = safe_cast<::Windows::UI::Xaml::Shapes::Rectangle^>(_feedbackPopup->Child);
|
||||
|
||||
if (state == DwellProgressState::Progressing)
|
||||
{
|
||||
auto progress = ((double)(ElapsedTime - _prevStateTime).Duration) / (_nextStateTime - _prevStateTime).Duration;
|
||||
|
||||
if (0 <= progress && progress < 1)
|
||||
{
|
||||
rectangle->Stroke = GazeInput::DwellFeedbackProgressBrush;
|
||||
rectangle->Width = (1 - progress) * bounds.Width;
|
||||
rectangle->Height = (1 - progress) * bounds.Height;
|
||||
|
||||
_feedbackPopup->HorizontalOffset = bounds.Left + progress * bounds.Width / 2;
|
||||
_feedbackPopup->VerticalOffset = bounds.Top + progress * bounds.Height / 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rectangle->Stroke = state == DwellProgressState::Fixating ?
|
||||
GazeInput::DwellFeedbackEnterBrush : GazeInput::DwellFeedbackCompleteBrush;
|
||||
rectangle->Width = bounds.Width;
|
||||
rectangle->Height = bounds.Height;
|
||||
|
||||
_feedbackPopup->HorizontalOffset = bounds.Left;
|
||||
_feedbackPopup->VerticalOffset = bounds.Top;
|
||||
}
|
||||
|
||||
_feedbackPopup->IsOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_feedbackPopup != nullptr)
|
||||
{
|
||||
GazePointer::Instance->_gazeFeedbackPopupFactory->Return(_feedbackPopup);
|
||||
_feedbackPopup = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_notifiedProgressState = state;
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,239 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal abstract class GazeTargetItem
|
||||
{
|
||||
private static readonly DependencyProperty _gazeTargetItemProperty = DependencyProperty.RegisterAttached("_GazeTargetItem", typeof(GazeTargetItem), typeof(GazeTargetItem), new PropertyMetadata(null));
|
||||
|
||||
internal TimeSpan DetailedTime { get; set; }
|
||||
|
||||
internal TimeSpan OverflowTime { get; set; }
|
||||
|
||||
internal TimeSpan ElapsedTime
|
||||
{
|
||||
get { return DetailedTime + OverflowTime; }
|
||||
}
|
||||
|
||||
internal TimeSpan NextStateTime { get; set; }
|
||||
|
||||
internal TimeSpan LastTimestamp { get; set; }
|
||||
|
||||
internal PointerState ElementState { get; set; }
|
||||
|
||||
internal UIElement TargetElement { get; set; }
|
||||
|
||||
internal int RepeatCount { get; set; }
|
||||
|
||||
internal int MaxDwellRepeatCount { get; set; }
|
||||
|
||||
internal GazeTargetItem(UIElement target)
|
||||
{
|
||||
TargetElement = target;
|
||||
}
|
||||
|
||||
internal static GazeTargetItem GetOrCreate(UIElement element)
|
||||
{
|
||||
GazeTargetItem item;
|
||||
|
||||
var value = element.ReadLocalValue(_gazeTargetItemProperty);
|
||||
|
||||
if (value != DependencyProperty.UnsetValue)
|
||||
{
|
||||
item = (GazeTargetItem)value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var peer = FrameworkElementAutomationPeer.FromElement(element);
|
||||
|
||||
if (peer == null)
|
||||
{
|
||||
if (element is PivotHeaderItem)
|
||||
{
|
||||
item = new PivotItemGazeTargetItem(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = GazePointer.Instance.NonInvokeGazeTargetItem;
|
||||
}
|
||||
}
|
||||
else if (peer.GetPattern(PatternInterface.Invoke) is IInvokeProvider)
|
||||
{
|
||||
item = new InvokePatternGazeTargetItem(element);
|
||||
}
|
||||
else if (peer.GetPattern(PatternInterface.Toggle) is IToggleProvider)
|
||||
{
|
||||
item = new TogglePatternGazeTargetItem(element);
|
||||
}
|
||||
else if (peer.GetPattern(PatternInterface.SelectionItem) is ISelectionItemProvider)
|
||||
{
|
||||
item = new SelectionItemPatternGazeTargetItem(element);
|
||||
}
|
||||
else if (peer.GetPattern(PatternInterface.ExpandCollapse) is IExpandCollapseProvider)
|
||||
{
|
||||
item = new ExpandCollapsePatternGazeTargetItem(element);
|
||||
}
|
||||
else if (peer is ComboBoxItemAutomationPeer)
|
||||
{
|
||||
item = new ComboBoxItemGazeTargetItem(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = GazePointer.Instance.NonInvokeGazeTargetItem;
|
||||
}
|
||||
|
||||
element.SetValue(_gazeTargetItemProperty, item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
internal abstract void Invoke();
|
||||
|
||||
internal virtual bool IsInvokable
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
internal void Reset(TimeSpan nextStateTime)
|
||||
{
|
||||
ElementState = PointerState.PreEnter;
|
||||
DetailedTime = TimeSpan.Zero;
|
||||
OverflowTime = TimeSpan.Zero;
|
||||
NextStateTime = nextStateTime;
|
||||
RepeatCount = 0;
|
||||
MaxDwellRepeatCount = GazeInput.GetMaxDwellRepeatCount(TargetElement);
|
||||
}
|
||||
|
||||
internal void GiveFeedback()
|
||||
{
|
||||
if (_nextStateTime != NextStateTime)
|
||||
{
|
||||
_prevStateTime = _nextStateTime;
|
||||
_nextStateTime = NextStateTime;
|
||||
}
|
||||
|
||||
if (ElementState != _notifiedPointerState)
|
||||
{
|
||||
switch (ElementState)
|
||||
{
|
||||
case PointerState.Enter:
|
||||
RaiseProgressEvent(DwellProgressState.Fixating);
|
||||
break;
|
||||
|
||||
case PointerState.Dwell:
|
||||
case PointerState.Fixation:
|
||||
RaiseProgressEvent(DwellProgressState.Progressing);
|
||||
break;
|
||||
|
||||
case PointerState.Exit:
|
||||
case PointerState.PreEnter:
|
||||
RaiseProgressEvent(DwellProgressState.Idle);
|
||||
break;
|
||||
}
|
||||
|
||||
_notifiedPointerState = ElementState;
|
||||
}
|
||||
else if (ElementState == PointerState.Dwell || ElementState == PointerState.Fixation)
|
||||
{
|
||||
if (RepeatCount <= MaxDwellRepeatCount)
|
||||
{
|
||||
RaiseProgressEvent(DwellProgressState.Progressing);
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseProgressEvent(DwellProgressState.Complete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseProgressEvent(DwellProgressState state)
|
||||
{
|
||||
// TODO: We should eliminate non-invokable controls before we arrive here!
|
||||
if (!(TargetElement is Page))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_notifiedProgressState != state || state == DwellProgressState.Progressing)
|
||||
{
|
||||
var handled = false;
|
||||
|
||||
var gazeElement = GazeInput.GetGazeElement(TargetElement);
|
||||
if (gazeElement != null)
|
||||
{
|
||||
handled = gazeElement.RaiseProgressFeedback(TargetElement, state, ElapsedTime - _prevStateTime, _nextStateTime - _prevStateTime);
|
||||
}
|
||||
|
||||
if (!handled && state != DwellProgressState.Idle)
|
||||
{
|
||||
if (_feedbackPopup == null)
|
||||
{
|
||||
_feedbackPopup = GazePointer.Instance.GazeFeedbackPopupFactory.Get();
|
||||
}
|
||||
|
||||
var control = TargetElement as FrameworkElement;
|
||||
|
||||
var transform = control.TransformToVisual(_feedbackPopup);
|
||||
var bounds = transform.TransformBounds(new Rect(
|
||||
new Point(0, 0),
|
||||
new Size((float)control.ActualWidth, (float)control.ActualHeight)));
|
||||
var rectangle = (Windows.UI.Xaml.Shapes.Rectangle)_feedbackPopup.Child;
|
||||
|
||||
if (state == DwellProgressState.Progressing)
|
||||
{
|
||||
var progress = ((double)(ElapsedTime - _prevStateTime).Ticks) / (_nextStateTime - _prevStateTime).Ticks;
|
||||
|
||||
if (progress >= 0 && progress < 1)
|
||||
{
|
||||
rectangle.Stroke = GazeInput.DwellFeedbackProgressBrush;
|
||||
rectangle.Width = (1 - progress) * bounds.Width;
|
||||
rectangle.Height = (1 - progress) * bounds.Height;
|
||||
|
||||
_feedbackPopup.HorizontalOffset = bounds.Left + (progress * bounds.Width / 2);
|
||||
_feedbackPopup.VerticalOffset = bounds.Top + (progress * bounds.Height / 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rectangle.Stroke = state == DwellProgressState.Fixating ?
|
||||
GazeInput.DwellFeedbackEnterBrush : GazeInput.DwellFeedbackCompleteBrush;
|
||||
rectangle.Width = bounds.Width;
|
||||
rectangle.Height = bounds.Height;
|
||||
|
||||
_feedbackPopup.HorizontalOffset = bounds.Left;
|
||||
_feedbackPopup.VerticalOffset = bounds.Top;
|
||||
}
|
||||
|
||||
_feedbackPopup.IsOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_feedbackPopup != null)
|
||||
{
|
||||
GazePointer.Instance.GazeFeedbackPopupFactory.Return(_feedbackPopup);
|
||||
_feedbackPopup = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_notifiedProgressState = state;
|
||||
}
|
||||
|
||||
private PointerState _notifiedPointerState = PointerState.Exit;
|
||||
private TimeSpan _prevStateTime;
|
||||
private TimeSpan _nextStateTime;
|
||||
private DwellProgressState _notifiedProgressState = DwellProgressState.Idle;
|
||||
private Popup _feedbackPopup;
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DwellProgressState.h"
|
||||
#include "GazeInput.h"
|
||||
#include "PointerState.h"
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
private ref class GazeTargetItem abstract
|
||||
{
|
||||
internal:
|
||||
property TimeSpan DetailedTime;
|
||||
property TimeSpan OverflowTime;
|
||||
property TimeSpan ElapsedTime { TimeSpan get() { return DetailedTime + OverflowTime; } }
|
||||
property TimeSpan NextStateTime;
|
||||
property TimeSpan LastTimestamp;
|
||||
property PointerState ElementState;
|
||||
property UIElement^ TargetElement;
|
||||
property int RepeatCount;
|
||||
property int MaxDwellRepeatCount;
|
||||
|
||||
GazeTargetItem(UIElement^ target)
|
||||
{
|
||||
TargetElement = target;
|
||||
}
|
||||
|
||||
static GazeTargetItem^ GetOrCreate(UIElement^ element);
|
||||
|
||||
virtual void Invoke() = 0;
|
||||
|
||||
virtual property bool IsInvokable { bool get() { return true; } }
|
||||
|
||||
void Reset(TimeSpan nextStateTime)
|
||||
{
|
||||
ElementState = PointerState::PreEnter;
|
||||
DetailedTime = TimeSpanZero;
|
||||
OverflowTime = TimeSpanZero;
|
||||
NextStateTime = nextStateTime;
|
||||
RepeatCount = 0;
|
||||
MaxDwellRepeatCount = GazeInput::GetMaxDwellRepeatCount(TargetElement);
|
||||
}
|
||||
|
||||
void GiveFeedback()
|
||||
{
|
||||
if (_nextStateTime != NextStateTime)
|
||||
{
|
||||
_prevStateTime = _nextStateTime;
|
||||
_nextStateTime = NextStateTime;
|
||||
}
|
||||
|
||||
if (ElementState != _notifiedPointerState)
|
||||
{
|
||||
switch (ElementState)
|
||||
{
|
||||
case PointerState::Enter:
|
||||
RaiseProgressEvent(DwellProgressState::Fixating);
|
||||
break;
|
||||
|
||||
case PointerState::Dwell:
|
||||
case PointerState::Fixation:
|
||||
RaiseProgressEvent(DwellProgressState::Progressing);
|
||||
break;
|
||||
|
||||
case PointerState::Exit:
|
||||
case PointerState::PreEnter:
|
||||
RaiseProgressEvent(DwellProgressState::Idle);
|
||||
break;
|
||||
}
|
||||
|
||||
_notifiedPointerState = ElementState;
|
||||
}
|
||||
else if (ElementState == PointerState::Dwell || ElementState == PointerState::Fixation)
|
||||
{
|
||||
if (RepeatCount <= MaxDwellRepeatCount)
|
||||
{
|
||||
RaiseProgressEvent(DwellProgressState::Progressing);
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseProgressEvent(DwellProgressState::Complete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void RaiseProgressEvent(DwellProgressState state);
|
||||
|
||||
PointerState _notifiedPointerState = PointerState::Exit;
|
||||
TimeSpan _prevStateTime;
|
||||
TimeSpan _nextStateTime;
|
||||
DwellProgressState _notifiedProgressState = DwellProgressState::Idle;
|
||||
Popup^ _feedbackPopup;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,17 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
// Every filter must provide an Wpdate method which transforms sample data
|
||||
// and returns filtered output
|
||||
internal interface IGazeFilter
|
||||
{
|
||||
GazeFilterArgs Update(GazeFilterArgs args);
|
||||
|
||||
void LoadSettings(ValueSet settings);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This struct encapsulates the location and timestamp associated with the user's gaze
|
||||
/// and is used as an input and output parameter for the IGazeFilter::Update method
|
||||
/// </summary>
|
||||
private ref struct GazeFilterArgs sealed
|
||||
{
|
||||
/// <summary>
|
||||
/// The current point in the gaze stream
|
||||
/// </summary>
|
||||
property Point Location {Point get() { return _location; }}
|
||||
|
||||
/// <summary>
|
||||
/// The timestamp associated with the current point
|
||||
/// </summary>
|
||||
property TimeSpan Timestamp {TimeSpan get() { return _timestamp; }}
|
||||
|
||||
internal:
|
||||
|
||||
GazeFilterArgs(Point location, TimeSpan timestamp)
|
||||
{
|
||||
_location = location;
|
||||
_timestamp = timestamp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Point _location;
|
||||
TimeSpan _timestamp;
|
||||
};
|
||||
|
||||
// Every filter must provide an Wpdate method which transforms sample data
|
||||
// and returns filtered output
|
||||
private interface class IGazeFilter
|
||||
{
|
||||
GazeFilterArgs^ Update(GazeFilterArgs^ args);
|
||||
void LoadSettings(ValueSet^ settings);
|
||||
};
|
||||
|
||||
|
||||
// Basic filter which performs no input filtering -- easy to
|
||||
// use as a default filter.
|
||||
private ref class NullFilter sealed : public IGazeFilter
|
||||
{
|
||||
public:
|
||||
virtual inline GazeFilterArgs^ Update(GazeFilterArgs^ args)
|
||||
{
|
||||
return args;
|
||||
}
|
||||
|
||||
virtual inline void LoadSettings(ValueSet^ settings)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,27 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// This enum indicates the current state of gaze interaction.
|
||||
/// </summary>
|
||||
public enum Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// The state of gaze interaction is inherited from the nearest parent
|
||||
/// </summary>
|
||||
Inherited,
|
||||
|
||||
/// <summary>
|
||||
/// Gaze interaction is enabled
|
||||
/// </summary>
|
||||
Enabled,
|
||||
|
||||
/// <summary>
|
||||
/// Gaze interaction is disabled
|
||||
/// </summary>
|
||||
Disabled
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This enum indicates the current state of gaze interaction.
|
||||
/// </summary>
|
||||
public enum class Interaction
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The state of gaze interaction is inherited from the nearest parent
|
||||
/// </summary>
|
||||
Inherited,
|
||||
|
||||
/// <summary>
|
||||
/// Gaze interaction is enabled
|
||||
/// </summary>
|
||||
Enabled,
|
||||
|
||||
/// <summary>
|
||||
/// Gaze interaction is disabled
|
||||
/// </summary>
|
||||
Disabled
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,25 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class InvokePatternGazeTargetItem : GazeTargetItem
|
||||
{
|
||||
internal InvokePatternGazeTargetItem(UIElement element)
|
||||
: base(element)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void Invoke()
|
||||
{
|
||||
var peer = FrameworkElementAutomationPeer.FromElement(TargetElement);
|
||||
var provider = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
|
||||
provider.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
/*
|
||||
* http://www.lifl.fr/~casiez/1euro/
|
||||
* http://www.lifl.fr/~casiez/publications/CHI2012-casiez.pdf
|
||||
*/
|
||||
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class LowpassFilter
|
||||
{
|
||||
public LowpassFilter()
|
||||
{
|
||||
Previous = new Point(0, 0);
|
||||
}
|
||||
|
||||
public LowpassFilter(Point initial)
|
||||
{
|
||||
Previous = initial;
|
||||
}
|
||||
|
||||
public Point Previous { get; set; }
|
||||
|
||||
public Point Update(Point point, Point alpha)
|
||||
{
|
||||
Point pt;
|
||||
pt.X = (alpha.X * point.X) + ((1 - alpha.X) * Previous.X);
|
||||
pt.Y = (alpha.Y * point.Y) + ((1 - alpha.Y) * Previous.Y);
|
||||
Previous = pt;
|
||||
return Previous;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PackagePlatform Condition="'$(Platform)' == 'Win32'">x86</PackagePlatform>
|
||||
<PackagePlatform Condition="'$(Platform)' != 'Win32'">$(Platform)</PackagePlatform>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'UAP'">
|
||||
<Reference Include="$(MSBuildThisFileDirectory)..\..\lib\uap10.0.17134\Microsoft.Toolkit.Uwp.Input.GazeInteraction.winmd">
|
||||
<Implementation>Microsoft.Toolkit.Uwp.Input.GazeInteraction.dll</Implementation>
|
||||
</Reference>
|
||||
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win10-$(PackagePlatform)\native\Microsoft.Toolkit.Uwp.Input.GazeInteraction.dll" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,331 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{a5e98964-45b1-442d-a07a-298a3221d81e}</ProjectGuid>
|
||||
<Keyword>WindowsRuntimeComponent</Keyword>
|
||||
<RootNamespace>Microsoft.Toolkit.Uwp.Input.GazeInteraction</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<ProjectName>Microsoft.Toolkit.Uwp.Input.GazeInteraction</ProjectName>
|
||||
<TargetFrameworks>UAP,Version=v10.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)$(MSBuildProjectName)\Output\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)$(MSBuildProjectName)\Output\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)$(MSBuildProjectName)\Output\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)$(MSBuildProjectName)\Output\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="GazeEventArgs.h" />
|
||||
<ClInclude Include="GazeHidParsers.h" />
|
||||
<ClInclude Include="GazeHidUsages.h" />
|
||||
<ClInclude Include="GazeInput.h" />
|
||||
<ClInclude Include="GazeCursor.h" />
|
||||
<ClInclude Include="GazeElement.h" />
|
||||
<ClInclude Include="Interaction.h" />
|
||||
<ClInclude Include="GazeFeedbackPopupFactory.h" />
|
||||
<ClInclude Include="GazeHistoryItem.h" />
|
||||
<ClInclude Include="DwellInvokedRoutedEventArgs.h" />
|
||||
<ClInclude Include="GazePointer.h" />
|
||||
<ClInclude Include="StateChangedEventArgs.h" />
|
||||
<ClInclude Include="GazePointerProxy.h" />
|
||||
<ClInclude Include="PointerState.h" />
|
||||
<ClInclude Include="DwellProgressEventArgs.h" />
|
||||
<ClInclude Include="DwellProgressState.h" />
|
||||
<ClInclude Include="GazeSettingsHelper.h" />
|
||||
<ClInclude Include="GazeStats.h" />
|
||||
<ClInclude Include="GazeTargetItem.h" />
|
||||
<ClInclude Include="IGazeFilter.h" />
|
||||
<ClInclude Include="OneEuroFilter.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="GazeEventArgs.cpp" />
|
||||
<ClCompile Include="GazeHidParsers.cpp" />
|
||||
<ClCompile Include="GazeInput.cpp" />
|
||||
<ClCompile Include="GazeCursor.cpp" />
|
||||
<ClCompile Include="GazeFeedbackPopupFactory.cpp" />
|
||||
<ClCompile Include="GazePointer.cpp" />
|
||||
<ClCompile Include="GazePointerProxy.cpp" />
|
||||
<ClCompile Include="GazeSettingsHelper.cpp" />
|
||||
<ClCompile Include="GazeStats.cpp" />
|
||||
<ClCompile Include="GazeTargetItem.cpp" />
|
||||
<ClCompile Include="OneEuroFilter.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="MSBuild.Sdk.Extras">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>uap10.0.17134</TargetFrameworks>
|
||||
<Title>Windows Community Toolkit Eye Gaze Library</Title>
|
||||
<Description>A library to integrate gaze interactions using eye trackers into UWP applications</Description>
|
||||
<PackageTags>UWP Toolkit Windows Eye Gaze EyeTracker</PackageTags>
|
||||
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.17134.0</TargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,25 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class NonInvokeGazeTargetItem : GazeTargetItem
|
||||
{
|
||||
internal NonInvokeGazeTargetItem()
|
||||
: base(new Page())
|
||||
{
|
||||
}
|
||||
|
||||
internal override bool IsInvokable
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
internal override void Invoke()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
// Basic filter which performs no input filtering -- easy to
|
||||
// use as a default filter.
|
||||
internal class NullFilter : IGazeFilter
|
||||
{
|
||||
public virtual GazeFilterArgs Update(GazeFilterArgs args)
|
||||
{
|
||||
return args;
|
||||
}
|
||||
|
||||
public virtual void LoadSettings(ValueSet settings)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "OneEuroFilter.h"
|
||||
|
||||
//
|
||||
// http://www.lifl.fr/~casiez/1euro/
|
||||
// http://www.lifl.fr/~casiez/publications/CHI2012-casiez.pdf
|
||||
//
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
OneEuroFilter::OneEuroFilter()
|
||||
{
|
||||
|
||||
_lastTimestamp = TimeSpanZero;
|
||||
Beta = ONEEUROFILTER_DEFAULT_BETA;
|
||||
Cutoff = ONEEUROFILTER_DEFAULT_CUTOFF;
|
||||
VelocityCutoff = ONEEUROFILTER_DEFAULT_VELOCITY_CUTOFF;
|
||||
}
|
||||
|
||||
OneEuroFilter::OneEuroFilter(float cutoff, float beta)
|
||||
{
|
||||
_lastTimestamp = TimeSpanZero;
|
||||
Beta = beta;
|
||||
Cutoff = cutoff;
|
||||
VelocityCutoff = ONEEUROFILTER_DEFAULT_VELOCITY_CUTOFF;
|
||||
}
|
||||
|
||||
GazeFilterArgs^ OneEuroFilter::Update(GazeFilterArgs^ args)
|
||||
{
|
||||
if (_lastTimestamp == TimeSpanZero)
|
||||
{
|
||||
_lastTimestamp = args->Timestamp;
|
||||
_pointFilter = ref new LowpassFilter(args->Location);
|
||||
_deltaFilter = ref new LowpassFilter(Point());
|
||||
return ref new GazeFilterArgs(args->Location, args->Timestamp);
|
||||
}
|
||||
|
||||
Point gazePoint = args->Location;
|
||||
|
||||
// Reducing _beta increases lag. Increasing beta decreases lag and improves response time
|
||||
// But a really high value of beta also contributes to jitter
|
||||
float beta = Beta;
|
||||
|
||||
// This simply represents the cutoff frequency. A lower value reduces jiiter
|
||||
// and higher value increases jitter
|
||||
float cf = Cutoff;
|
||||
Point cutoff = Point(cf, cf);
|
||||
|
||||
// determine sampling frequency based on last time stamp
|
||||
float samplingFrequency = 10000000.0f / max(1, (args->Timestamp - _lastTimestamp).Duration);
|
||||
_lastTimestamp = args->Timestamp;
|
||||
|
||||
// calculate change in distance...
|
||||
Point deltaDistance;
|
||||
deltaDistance.X = gazePoint.X - _pointFilter->Previous.X;
|
||||
deltaDistance.Y = gazePoint.Y - _pointFilter->Previous.Y;
|
||||
|
||||
// ...and velocity
|
||||
Point velocity(deltaDistance.X * samplingFrequency, deltaDistance.Y * samplingFrequency);
|
||||
|
||||
// find the alpha to use for the velocity filter
|
||||
float velocityAlpha = Alpha(samplingFrequency, VelocityCutoff);
|
||||
Point velocityAlphaPoint(velocityAlpha, velocityAlpha);
|
||||
|
||||
// find the filtered velocity
|
||||
Point filteredVelocity = _deltaFilter->Update(velocity, velocityAlphaPoint);
|
||||
|
||||
// ignore sign since it will be taken care of by deltaDistance
|
||||
filteredVelocity.X = abs(filteredVelocity.X);
|
||||
filteredVelocity.Y = abs(filteredVelocity.Y);
|
||||
|
||||
// compute new cutoff to use based on velocity
|
||||
cutoff.X += beta * filteredVelocity.X;
|
||||
cutoff.Y += beta * filteredVelocity.Y;
|
||||
|
||||
// find the new alpha to use to filter the points
|
||||
Point distanceAlpha(Alpha(samplingFrequency, cutoff.X), Alpha(samplingFrequency, cutoff.Y));
|
||||
|
||||
// find the filtered point
|
||||
Point filteredPoint = _pointFilter->Update(gazePoint, distanceAlpha);
|
||||
|
||||
// compute the new args
|
||||
auto fa = ref new GazeFilterArgs(filteredPoint, args->Timestamp);
|
||||
return fa;
|
||||
}
|
||||
|
||||
float OneEuroFilter::Alpha(float rate, float cutoff)
|
||||
{
|
||||
const float PI = 3.14159265f;
|
||||
float te = 1.0f / rate;
|
||||
float tau = (float)(1.0f / (2 * PI * cutoff));
|
||||
float alpha = te / (te + tau);
|
||||
return alpha;
|
||||
}
|
||||
void OneEuroFilter::LoadSettings(ValueSet^ settings)
|
||||
{
|
||||
if (settings->HasKey("OneEuroFilter.Beta"))
|
||||
{
|
||||
Beta = (float)(settings->Lookup("OneEuroFilter.Beta"));
|
||||
}
|
||||
if (settings->HasKey("OneEuroFilter.Cutoff"))
|
||||
{
|
||||
Cutoff = (float)(settings->Lookup("OneEuroFilter.Cutoff"));
|
||||
}
|
||||
if (settings->HasKey("OneEuroFilter.VelocityCutoff"))
|
||||
{
|
||||
VelocityCutoff = (float)(settings->Lookup("OneEuroFilter.VelocityCutoff"));
|
||||
}
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,134 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
/*
|
||||
* http://www.lifl.fr/~casiez/1euro/
|
||||
* http://www.lifl.fr/~casiez/publications/CHI2012-casiez.pdf
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class OneEuroFilter : IGazeFilter
|
||||
{
|
||||
private const float ONEEUROFILTER_DEFAULT_BETA = 5.0f;
|
||||
private const float ONEEUROFILTER_DEFAULT_CUTOFF = 0.1f;
|
||||
private const float ONEEUROFILTER_DEFAULT_VELOCITY_CUTOFF = 1.0f;
|
||||
|
||||
public OneEuroFilter()
|
||||
{
|
||||
_lastTimestamp = TimeSpan.Zero;
|
||||
Beta = ONEEUROFILTER_DEFAULT_BETA;
|
||||
Cutoff = ONEEUROFILTER_DEFAULT_CUTOFF;
|
||||
VelocityCutoff = ONEEUROFILTER_DEFAULT_VELOCITY_CUTOFF;
|
||||
}
|
||||
|
||||
public OneEuroFilter(float cutoff, float beta)
|
||||
{
|
||||
_lastTimestamp = TimeSpan.Zero;
|
||||
Beta = beta;
|
||||
Cutoff = cutoff;
|
||||
VelocityCutoff = ONEEUROFILTER_DEFAULT_VELOCITY_CUTOFF;
|
||||
}
|
||||
|
||||
public virtual GazeFilterArgs Update(GazeFilterArgs args)
|
||||
{
|
||||
if (_lastTimestamp == TimeSpan.Zero)
|
||||
{
|
||||
_lastTimestamp = args.Timestamp;
|
||||
_pointFilter = new LowpassFilter(args.Location);
|
||||
_deltaFilter = new LowpassFilter(default);
|
||||
return new GazeFilterArgs(args.Location, args.Timestamp);
|
||||
}
|
||||
|
||||
Point gazePoint = args.Location;
|
||||
|
||||
// Reducing _beta increases lag. Increasing beta decreases lag and improves response time
|
||||
// But a really high value of beta also contributes to jitter
|
||||
float beta = Beta;
|
||||
|
||||
// This simply represents the cutoff frequency. A lower value reduces jiiter
|
||||
// and higher value increases jitter
|
||||
float cf = Cutoff;
|
||||
Point cutoff = new Point(cf, cf);
|
||||
|
||||
// determine sampling frequency based on last time stamp
|
||||
float samplingFrequency = 100000000.0f / Math.Max(1, (args.Timestamp - _lastTimestamp).Ticks);
|
||||
_lastTimestamp = args.Timestamp;
|
||||
|
||||
// calculate change in distance...
|
||||
Point deltaDistance;
|
||||
deltaDistance.X = gazePoint.X - _pointFilter.Previous.X;
|
||||
deltaDistance.Y = gazePoint.Y - _pointFilter.Previous.Y;
|
||||
|
||||
// ...and velocity
|
||||
var velocity = new Point(deltaDistance.X * samplingFrequency, deltaDistance.Y * samplingFrequency);
|
||||
|
||||
// find the alpha to use for the velocity filter
|
||||
float velocityAlpha = Alpha(samplingFrequency, VelocityCutoff);
|
||||
var velocityAlphaPoint = new Point(velocityAlpha, velocityAlpha);
|
||||
|
||||
// find the filtered velocity
|
||||
Point filteredVelocity = _deltaFilter.Update(velocity, velocityAlphaPoint);
|
||||
|
||||
// ignore sign since it will be taken care of by deltaDistance
|
||||
filteredVelocity.X = Math.Abs(filteredVelocity.X);
|
||||
filteredVelocity.Y = Math.Abs(filteredVelocity.Y);
|
||||
|
||||
// compute new cutoff to use based on velocity
|
||||
cutoff.X += beta * filteredVelocity.X;
|
||||
cutoff.Y += beta * filteredVelocity.Y;
|
||||
|
||||
// find the new alpha to use to filter the points
|
||||
var distanceAlpha = new Point(Alpha(samplingFrequency, (float)cutoff.X), Alpha(samplingFrequency, (float)cutoff.Y));
|
||||
|
||||
// find the filtered point
|
||||
Point filteredPoint = _pointFilter.Update(gazePoint, distanceAlpha);
|
||||
|
||||
// compute the new args
|
||||
var fa = new GazeFilterArgs(filteredPoint, args.Timestamp);
|
||||
return fa;
|
||||
}
|
||||
|
||||
public virtual void LoadSettings(ValueSet settings)
|
||||
{
|
||||
if (settings.ContainsKey("OneEuroFilter.Beta"))
|
||||
{
|
||||
Beta = (float)settings["OneEuroFilter.Beta"];
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("OneEuroFilter.Cutoff"))
|
||||
{
|
||||
Cutoff = (float)settings["OneEuroFilter.Cutoff"];
|
||||
}
|
||||
|
||||
if (settings.ContainsKey("OneEuroFilter.VelocityCutoff"))
|
||||
{
|
||||
VelocityCutoff = (float)settings["OneEuroFilter.VelocityCutoff"];
|
||||
}
|
||||
}
|
||||
|
||||
public float Beta { get; set; }
|
||||
|
||||
public float Cutoff { get; set; }
|
||||
|
||||
public float VelocityCutoff { get; set; }
|
||||
|
||||
private float Alpha(float rate, float cutoff)
|
||||
{
|
||||
const float PI = 3.14159265f;
|
||||
float te = 1.0f / rate;
|
||||
float tau = (float)(1.0f / (2 * PI * cutoff));
|
||||
float alpha = te / (te + tau);
|
||||
return alpha;
|
||||
}
|
||||
|
||||
private TimeSpan _lastTimestamp;
|
||||
private LowpassFilter _pointFilter;
|
||||
private LowpassFilter _deltaFilter;
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGazeFilter.h"
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
const float ONEEUROFILTER_DEFAULT_BETA = 5.0f;
|
||||
const float ONEEUROFILTER_DEFAULT_CUTOFF = 0.1f;
|
||||
const float ONEEUROFILTER_DEFAULT_VELOCITY_CUTOFF = 1.0f;
|
||||
|
||||
private ref class LowpassFilter sealed
|
||||
{
|
||||
public:
|
||||
LowpassFilter()
|
||||
{
|
||||
Previous = Point(0, 0);
|
||||
}
|
||||
|
||||
LowpassFilter(Point initial)
|
||||
{
|
||||
Previous = initial;
|
||||
}
|
||||
|
||||
property Point Previous;
|
||||
|
||||
Point Update(Point point, Point alpha)
|
||||
{
|
||||
Point pt;
|
||||
pt.X = (alpha.X * point.X) + ((1 - alpha.X) * Previous.X);
|
||||
pt.Y = (alpha.Y * point.Y) + ((1 - alpha.Y) * Previous.Y);
|
||||
Previous = pt;
|
||||
return Previous;
|
||||
}
|
||||
};
|
||||
|
||||
private ref class OneEuroFilter sealed : public IGazeFilter
|
||||
{
|
||||
public:
|
||||
OneEuroFilter();
|
||||
OneEuroFilter(float cutoff, float beta);
|
||||
virtual GazeFilterArgs^ Update(GazeFilterArgs^ args);
|
||||
virtual void LoadSettings(ValueSet^ settings);
|
||||
|
||||
public:
|
||||
property float Beta;
|
||||
property float Cutoff;
|
||||
property float VelocityCutoff;
|
||||
|
||||
private:
|
||||
float Alpha(float rate, float cutoff);
|
||||
|
||||
private:
|
||||
TimeSpan _lastTimestamp;
|
||||
LowpassFilter^ _pointFilter;
|
||||
LowpassFilter^ _deltaFilter;
|
||||
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,37 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class PivotItemGazeTargetItem : GazeTargetItem
|
||||
{
|
||||
internal PivotItemGazeTargetItem(UIElement element)
|
||||
: base(element)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void Invoke()
|
||||
{
|
||||
var headerItem = (PivotHeaderItem)TargetElement;
|
||||
var headerPanel = (PivotHeaderPanel)VisualTreeHelper.GetParent(headerItem);
|
||||
int index = headerPanel.Children.IndexOf(headerItem);
|
||||
|
||||
DependencyObject walker = headerPanel;
|
||||
Pivot pivot;
|
||||
do
|
||||
{
|
||||
walker = VisualTreeHelper.GetParent(walker);
|
||||
pivot = walker as Pivot;
|
||||
}
|
||||
while (pivot == null);
|
||||
|
||||
pivot.SelectedIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// This enum reflects the states that a user's gaze through while interacting with a control using their eyes.
|
||||
/// </summary>
|
||||
public enum PointerState
|
||||
{
|
||||
/// <summary>
|
||||
/// User's gaze is no longer on the control
|
||||
/// </summary>
|
||||
Exit = 0,
|
||||
|
||||
// The order of the following elements is important because
|
||||
// they represent states that linearly transition to their
|
||||
// immediate successors.
|
||||
|
||||
/// <summary>
|
||||
/// For internal use only
|
||||
/// </summary>
|
||||
PreEnter = 1,
|
||||
|
||||
/// <summary>
|
||||
/// User's gaze has entered a control
|
||||
/// </summary>
|
||||
Enter = 2,
|
||||
|
||||
/// <summary>
|
||||
/// User eye's are focused on the control
|
||||
/// </summary>
|
||||
Fixation = 3,
|
||||
|
||||
/// <summary>
|
||||
/// User is conciously dwelling on the control with an intent to invoke, e.g. click a button
|
||||
/// </summary>
|
||||
Dwell = 4,
|
||||
|
||||
/// <summary>
|
||||
/// User is continuing to dwell on the control for repeated invocation. (For internal use only)
|
||||
/// </summary>
|
||||
DwellRepeat = 5
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This enum reflects the states that a user's gaze through while interacting with a control using their eyes.
|
||||
/// </summary>
|
||||
public enum class PointerState
|
||||
{
|
||||
/// <summary>
|
||||
/// User's gaze is no longer on the control
|
||||
/// </summary>
|
||||
Exit = 0,
|
||||
|
||||
// The order of the following elements is important because
|
||||
// they represent states that linearly transition to their
|
||||
// immediate successors.
|
||||
|
||||
/// <summary>
|
||||
/// For internal use only
|
||||
/// </summary>
|
||||
PreEnter = 1,
|
||||
|
||||
/// <summary>
|
||||
/// User's gaze has entered a control
|
||||
/// </summary>
|
||||
Enter = 2,
|
||||
|
||||
/// <summary>
|
||||
/// User eye's are focused on the control
|
||||
/// </summary>
|
||||
Fixation = 3,
|
||||
|
||||
/// <summary>
|
||||
/// User is conciously dwelling on the control with an intent to invoke, e.g. click a button
|
||||
/// </summary>
|
||||
Dwell = 4,
|
||||
|
||||
/// <summary>
|
||||
/// User is continuing to dwell on the control for repeated invocation. (For internal use only)
|
||||
/// </summary>
|
||||
DwellRepeat = 5
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,25 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class SelectionItemPatternGazeTargetItem : GazeTargetItem
|
||||
{
|
||||
internal SelectionItemPatternGazeTargetItem(UIElement element)
|
||||
: base(element)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void Invoke()
|
||||
{
|
||||
var peer = FrameworkElementAutomationPeer.FromElement(TargetElement);
|
||||
var provider = peer.GetPattern(PatternInterface.SelectionItem) as ISelectionItemProvider;
|
||||
provider.Select();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// This parameter is passed to the StateChanged event.
|
||||
/// </summary>
|
||||
public class StateChangedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the state of the user's gaze with respect to a control
|
||||
/// </summary>
|
||||
public PointerState PointerState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets elapsed time since the last state
|
||||
/// </summary>
|
||||
public TimeSpan ElapsedTime => _elapsedTime;
|
||||
|
||||
internal StateChangedEventArgs(UIElement target, PointerState state, TimeSpan elapsedTime)
|
||||
{
|
||||
_hitTarget = target;
|
||||
PointerState = state;
|
||||
_elapsedTime = elapsedTime;
|
||||
}
|
||||
|
||||
private readonly UIElement _hitTarget;
|
||||
private TimeSpan _elapsedTime;
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PointerState.h"
|
||||
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This parameter is passed to the StateChanged event.
|
||||
/// </summary>
|
||||
public ref struct StateChangedEventArgs sealed
|
||||
{
|
||||
/// <summary>
|
||||
/// The state of user's gaze with respect to a control
|
||||
/// </summary>
|
||||
property GazeInteraction::PointerState PointerState {GazeInteraction::PointerState get() { return _pointerState; }}
|
||||
|
||||
/// <summary>
|
||||
/// Elapsed time since the last state
|
||||
/// </summary>
|
||||
property TimeSpan ElapsedTime {TimeSpan get() { return _elapsedTime; }}
|
||||
|
||||
internal:
|
||||
|
||||
StateChangedEventArgs(UIElement^ target, GazeInteraction::PointerState state, TimeSpan elapsedTime)
|
||||
{
|
||||
_hitTarget = target;
|
||||
_pointerState = state;
|
||||
_elapsedTime = elapsedTime;
|
||||
}
|
||||
|
||||
private:
|
||||
UIElement ^ _hitTarget;
|
||||
GazeInteraction::PointerState _pointerState;
|
||||
TimeSpan _elapsedTime;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,25 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
{
|
||||
internal class TogglePatternGazeTargetItem : GazeTargetItem
|
||||
{
|
||||
internal TogglePatternGazeTargetItem(UIElement element)
|
||||
: base(element)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void Invoke()
|
||||
{
|
||||
var peer = FrameworkElementAutomationPeer.FromElement(TargetElement);
|
||||
var provider = peer.GetPattern(PatternInterface.Toggle) as IToggleProvider;
|
||||
provider.Toggle();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
|
@ -1,54 +0,0 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <collection.h>
|
||||
#include <ppltasks.h>
|
||||
|
||||
//#define _USE_MATH_DEFINES
|
||||
//#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
//#include <Windows.Input.Gaze.h>
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Devices::Enumeration;
|
||||
using namespace Windows::Devices::HumanInterfaceDevice;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Media;
|
||||
using namespace Windows::UI::Xaml::Shapes;
|
||||
|
||||
#include <strsafe.h>
|
||||
private class Debug
|
||||
{
|
||||
public:
|
||||
static void WriteLine(wchar_t *format, ...)
|
||||
{
|
||||
wchar_t message[1024];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
StringCchVPrintf(message, 1024, format, args);
|
||||
OutputDebugString(message);
|
||||
OutputDebugString(L"\n");
|
||||
}
|
||||
};
|
||||
|
||||
inline static TimeSpan operator + (const TimeSpan& lhs, const TimeSpan& rhs) { return TimeSpan{ lhs.Duration + rhs.Duration }; }
|
||||
inline static TimeSpan operator - (const TimeSpan& lhs, const TimeSpan& rhs) { return TimeSpan{ lhs.Duration - rhs.Duration }; }
|
||||
inline static TimeSpan operator * (int lhs, const TimeSpan& rhs) { return TimeSpan{ lhs*rhs.Duration }; }
|
||||
inline static bool operator < (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration < rhs.Duration; }
|
||||
inline static bool operator <= (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration <= rhs.Duration; }
|
||||
inline static bool operator > (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration > rhs.Duration; }
|
||||
inline static bool operator >= (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration >= rhs.Duration; }
|
||||
inline static bool operator == (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration == rhs.Duration; }
|
||||
inline static bool operator != (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration != rhs.Duration; }
|
||||
|
||||
static TimeSpan TimeSpanZero{ 0 };
|
||||
inline static TimeSpan TimeSpanFromMicroseconds(uint64 milliseconds) { return TimeSpan{ (int64)(10 * milliseconds) }; }
|
||||
inline static TimeSpan TimeSpanFromMicroseconds(int64 milliseconds) { return TimeSpan{ 10 * milliseconds }; }
|
||||
inline static TimeSpan TimeSpanFromMicroseconds(int milliseconds) { return TimeSpan{ 10 * milliseconds }; }
|
||||
|
||||
#define BEGIN_NAMESPACE_GAZE_INPUT namespace Microsoft { namespace Toolkit { namespace Uwp { namespace Input { namespace GazeInteraction {
|
||||
#define END_NAMESPACE_GAZE_INPUT } } } } }
|
|
@ -1517,8 +1517,8 @@
|
|||
<Project>{b1e850ff-dde6-44d5-a830-34250e97a687}</Project>
|
||||
<Name>Microsoft.Toolkit.Uwp.Connectivity</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.UWP.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.vcxproj">
|
||||
<Project>{a5e98964-45b1-442d-a07a-298a3221d81e}</Project>
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.csproj">
|
||||
<Project>{5bf75694-798a-43a0-8150-415de195359c}</Project>
|
||||
<Name>Microsoft.Toolkit.Uwp.Input.GazeInteraction</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.Uwp.DeveloperTools\Microsoft.Toolkit.Uwp.DeveloperTools.csproj">
|
||||
|
|
|
@ -84,7 +84,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DifferencesGen", "Microsoft
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer", "Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer\Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer.csproj", "{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Toolkit.Uwp.Input.GazeInteraction", "Microsoft.Toolkit.UWP.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.vcxproj", "{A5E98964-45B1-442D-A07A-298A3221D81E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.Input.GazeInteraction", "Microsoft.Toolkit.Uwp.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.csproj", "{5BF75694-798A-43A0-8150-415DE195359C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GazeInputTest", "GazeInputTest\GazeInputTest.csproj", "{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}"
|
||||
EndProject
|
||||
|
@ -144,11 +144,6 @@ Global
|
|||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Native|Any CPU = Native|Any CPU
|
||||
Native|ARM = Native|ARM
|
||||
Native|ARM64 = Native|ARM64
|
||||
Native|x64 = Native|x64
|
||||
Native|x86 = Native|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|ARM64 = Release|ARM64
|
||||
|
@ -171,11 +166,6 @@ Global
|
|||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Debug|x86.Build.0 = Debug|x86
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Native|Any CPU.ActiveCfg = Release|x86
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Native|ARM64.ActiveCfg = Release|ARM64
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Native|x64.ActiveCfg = Release|x64
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Native|x86.ActiveCfg = Release|x86
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{719C43C6-8753-4395-ADAA-2FCC70F76BF3}.Release|ARM.Build.0 = Release|ARM
|
||||
|
@ -199,11 +189,6 @@ Global
|
|||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{805F80DF-75C6-4C2F-8FD9-B47F6D0DF5A3}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -228,11 +213,6 @@ Global
|
|||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Debug|x86.Build.0 = Debug|x86
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Native|Any CPU.ActiveCfg = Release|x86
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Native|ARM64.ActiveCfg = Release (Anniversary Update)|ARM64
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Native|x64.ActiveCfg = Release|x64
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Native|x86.ActiveCfg = Release|x86
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Release|Any CPU.Build.0 = Release|x86
|
||||
{A139968E-AD78-4E8C-93B8-9A5523BCAC89}.Release|ARM.ActiveCfg = Release|ARM
|
||||
|
@ -257,11 +237,6 @@ Global
|
|||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3DD8AA7C-3569-4E51-992F-0C2257E8878E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -282,11 +257,6 @@ Global
|
|||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -307,11 +277,6 @@ Global
|
|||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -332,11 +297,6 @@ Global
|
|||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -357,11 +317,6 @@ Global
|
|||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DAEB9CEC-C817-33B2-74B2-BC379380DB72}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -386,11 +341,6 @@ Global
|
|||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Debug|x86.Build.0 = Debug|x86
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Native|Any CPU.ActiveCfg = Release|x86
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Native|ARM64.ActiveCfg = Release|ARM64
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Native|x64.ActiveCfg = Release|x64
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Native|x86.ActiveCfg = Release|x86
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Release|Any CPU.Build.0 = Release|x86
|
||||
{BAB1CAF4-C400-4A7F-A987-C576DE63CFFD}.Release|ARM.ActiveCfg = Release|ARM
|
||||
|
@ -415,11 +365,6 @@ Global
|
|||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Debug|x64.Build.0 = Debug|x64
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Debug|x86.Build.0 = Debug|x86
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Native|ARM64.ActiveCfg = Release|ARM64
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Native|x64.ActiveCfg = Release|x64
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Native|x86.ActiveCfg = Release|x86
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{1AE2CB5C-58A0-4F12-8E6F-2CD4AAADB34C}.Release|ARM.Build.0 = Release|ARM
|
||||
|
@ -439,11 +384,6 @@ Global
|
|||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E7697922-9555-4CFB-AEE0-C5F4D657E559}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -464,11 +404,6 @@ Global
|
|||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B1E850FF-DDE6-44D5-A830-34250E97A687}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -489,11 +424,6 @@ Global
|
|||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{34398053-FC70-4243-84F9-F355DEFFF66D}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -514,11 +444,6 @@ Global
|
|||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6FE128A8-CEFA-4A61-A987-EC92DE6B538E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -539,11 +464,6 @@ Global
|
|||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{97EE849B-403C-490E-80ED-D19D7CC153FD}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -564,11 +484,6 @@ Global
|
|||
{94994424-5F60-4CD8-ABA2-101779066208}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{94994424-5F60-4CD8-ABA2-101779066208}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -593,11 +508,6 @@ Global
|
|||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Debug|x86.Build.0 = Debug|x86
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Native|Any CPU.ActiveCfg = Release|x86
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Native|ARM64.ActiveCfg = Release|ARM64
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Native|x64.ActiveCfg = Release|x64
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Native|x86.ActiveCfg = Release|x86
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Release|Any CPU.Build.0 = Release|x86
|
||||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA}.Release|ARM.ActiveCfg = Release|ARM
|
||||
|
@ -622,11 +532,6 @@ Global
|
|||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{94F8D702-3A9D-4CFB-85C9-79FC5DBD8B22}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -646,11 +551,6 @@ Global
|
|||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x64.Build.0 = Debug|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x86.Build.0 = Debug|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Native|Any CPU.ActiveCfg = Release|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Native|ARM.ActiveCfg = Release|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Native|ARM64.ActiveCfg = Release|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Native|x64.ActiveCfg = Release|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Native|x86.ActiveCfg = Release|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|Any CPU.Build.0 = Release|x86
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|ARM.ActiveCfg = Release|x86
|
||||
|
@ -669,11 +569,6 @@ Global
|
|||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x64.Build.0 = Debug|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x86.Build.0 = Debug|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Native|Any CPU.ActiveCfg = Release|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Native|ARM.ActiveCfg = Release|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Native|ARM64.ActiveCfg = Release|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Native|x64.ActiveCfg = Release|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Native|x86.ActiveCfg = Release|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|Any CPU.Build.0 = Release|x86
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|ARM.ActiveCfg = Release|x86
|
||||
|
@ -693,11 +588,6 @@ Global
|
|||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -718,11 +608,6 @@ Global
|
|||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|ARM.Build.0 = Release|Any CPU
|
||||
|
@ -742,11 +627,6 @@ Global
|
|||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -757,35 +637,6 @@ Global
|
|||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|Any CPU.Build.0 = Debug|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|x64.Build.0 = Debug|x64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|Any CPU.ActiveCfg = Release|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|Any CPU.Build.0 = Release|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|ARM.Build.0 = Release|ARM
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|ARM64.ActiveCfg = Release|ARM64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|ARM64.Build.0 = Release|ARM64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|x64.ActiveCfg = Release|x64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|x64.Build.0 = Release|x64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|x86.ActiveCfg = Release|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Native|x86.Build.0 = Release|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Release|ARM.Build.0 = Release|ARM
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Release|x64.ActiveCfg = Release|x64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Release|x64.Build.0 = Release|x64
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Release|x86.Build.0 = Release|Win32
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Debug|ARM.Build.0 = Debug|ARM
|
||||
|
@ -799,11 +650,6 @@ Global
|
|||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Debug|x86.Build.0 = Debug|x86
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Native|Any CPU.ActiveCfg = Release|x86
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Native|ARM64.ActiveCfg = Release|ARM64
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Native|x64.ActiveCfg = Release|x64
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Native|x86.ActiveCfg = Release|x86
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}.Release|ARM.Build.0 = Release|ARM
|
||||
|
@ -827,11 +673,6 @@ Global
|
|||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Native|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Native|ARM.ActiveCfg = Debug|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Native|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Native|x64.ActiveCfg = Debug|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Native|x86.ActiveCfg = Debug|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -852,16 +693,6 @@ Global
|
|||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|ARM.ActiveCfg = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|ARM.Build.0 = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|ARM64.Build.0 = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|x64.ActiveCfg = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|x64.Build.0 = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|x86.ActiveCfg = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Native|x86.Build.0 = Debug|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D82AE6E1-E612-434E-ACB2-363EE48738D3}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -882,11 +713,6 @@ Global
|
|||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Native|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Native|ARM.ActiveCfg = Debug|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Native|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Native|x64.ActiveCfg = Debug|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Native|x86.ActiveCfg = Debug|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7878CD00-85E8-4D02-9757-8A43DB4C6510}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -907,16 +733,6 @@ Global
|
|||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|ARM.ActiveCfg = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|ARM.Build.0 = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|ARM64.Build.0 = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|x64.ActiveCfg = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|x64.Build.0 = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|x86.ActiveCfg = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Native|x86.Build.0 = Debug|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CB444381-18BA-4A51-BB32-3A498BCC1E99}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -937,11 +753,6 @@ Global
|
|||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Native|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Native|ARM.ActiveCfg = Debug|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Native|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Native|x64.ActiveCfg = Debug|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Native|x86.ActiveCfg = Debug|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -962,11 +773,6 @@ Global
|
|||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|ARM.ActiveCfg = Debug|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|x64.ActiveCfg = Debug|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|x86.ActiveCfg = Debug|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
|
@ -991,11 +797,6 @@ Global
|
|||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Debug|x86.Build.0 = Debug|x86
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Native|Any CPU.ActiveCfg = Release|x64
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Native|ARM64.ActiveCfg = Release|ARM64
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Native|x64.ActiveCfg = Release|x64
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Native|x86.ActiveCfg = Release|x86
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Release|Any CPU.Build.0 = Release|x86
|
||||
{5524523E-DB0F-41F7-A0D4-43128422A342}.Release|ARM.ActiveCfg = Release|ARM
|
||||
|
@ -1017,11 +818,6 @@ Global
|
|||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Debug|x64.Build.0 = Debug|x64
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Debug|x86.Build.0 = Debug|x86
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Native|Any CPU.ActiveCfg = Release|x86
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Native|ARM.ActiveCfg = Release|x86
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Native|ARM64.ActiveCfg = Release|x86
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Native|x64.ActiveCfg = Release|x86
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Native|x86.ActiveCfg = Release|x86
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Release|ARM.ActiveCfg = Release|x86
|
||||
{F5929F8E-7BC5-4A7B-A92A-AC751F7906E4}.Release|ARM64.ActiveCfg = Release|x86
|
||||
|
@ -1044,11 +840,6 @@ Global
|
|||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Debug|x86.Build.0 = Debug|x86
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Native|Any CPU.ActiveCfg = Debug|x86
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Native|ARM64.ActiveCfg = Release|ARM64
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Native|x64.ActiveCfg = Release|x64
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Native|x86.ActiveCfg = Release|x86
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{DCFBF9F1-2BBE-498D-B6C9-8ADE50C06CDC}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
|
@ -1071,11 +862,6 @@ Global
|
|||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Debug|x86.Build.0 = Debug|x86
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Native|Any CPU.ActiveCfg = Release|x86
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Native|ARM.ActiveCfg = Release|ARM
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Native|ARM64.ActiveCfg = Release|ARM64
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Native|x64.ActiveCfg = Release|x64
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Native|x86.ActiveCfg = Release|x86
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
|
@ -1085,6 +871,26 @@ Global
|
|||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Release|x86.ActiveCfg = Release|x86
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Release|x86.Build.0 = Release|x86
|
||||
{804D0681-52F6-4E61-864A-699F0AB44B20}.Release|x86.Deploy.0 = Release|x86
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5BF75694-798A-43A0-8150-415DE195359C}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Microsoft.Toolkit.Uwp.Input.GazeInteraction</id>
|
||||
<version>$version$</version>
|
||||
<title>Windows Community Toolkit Eye Gaze Library</title>
|
||||
<authors>Microsoft.Toolkit</authors>
|
||||
<owners>Microsoft.Toolkit</owners>
|
||||
<iconUrl>https://raw.githubusercontent.com/windows-toolkit/WindowsCommunityToolkit/master/build/nuget.png</iconUrl>
|
||||
<licenseUrl>https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/license.md</licenseUrl>
|
||||
<projectUrl>https://github.com/windows-toolkit/WindowsCommunityToolkit</projectUrl>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<description>A library to integrate gaze interactions using eye trackers into UWP applications</description>
|
||||
<releaseNotes>v5.0 release https://github.com/windows-toolkit/WindowsCommunityToolkit/releases</releaseNotes>
|
||||
<copyright>(c) .NET Foundation and Contributors. All rights reserved.</copyright>
|
||||
<tags>UWP Toolkit Windows Eye Gaze EyeTracker </tags>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- WinMd and IntelliSense files -->
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\Win32\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.winmd" target="lib\uap10.0.17134"/>
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\Win32\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.xml" target="lib\uap10.0.17134"/>
|
||||
|
||||
<!-- DLLs and resources -->
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\ARM\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.dll" target="runtimes\win10-arm\native"/>
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\ARM\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.pri" target="runtimes\win10-arm\native"/>
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\ARM\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.pdb" target="runtimes\win10-arm\native"/>
|
||||
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\ARM64\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.dll" target="runtimes\win10-arm64\native"/>
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\ARM64\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.pri" target="runtimes\win10-arm64\native"/>
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\ARM64\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.pdb" target="runtimes\win10-arm64\native"/>
|
||||
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\x64\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.dll" target="runtimes\win10-x64\native"/>
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\x64\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.pri" target="runtimes\win10-x64\native"/>
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\x64\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.pdb" target="runtimes\win10-x64\native"/>
|
||||
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\Win32\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.dll" target="runtimes\win10-x86\native"/>
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\Win32\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.pri" target="runtimes\win10-x86\native"/>
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Output\Win32\Release\Microsoft.Toolkit.Uwp.Input.GazeInteraction.pdb" target="runtimes\win10-x86\native"/>
|
||||
|
||||
<file src="..\Microsoft.Toolkit.Uwp.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.targets" target="build\native"/>
|
||||
</files>
|
||||
</package>
|
|
@ -225,39 +225,6 @@ Task("Package")
|
|||
.WithProperty("PackageOutputPath", nupkgDir);
|
||||
|
||||
MSBuild(Solution, buildSettings);
|
||||
|
||||
// Build and pack C++ packages
|
||||
buildSettings = new MSBuildSettings
|
||||
{
|
||||
MaxCpuCount = 0
|
||||
}
|
||||
.SetConfiguration("Native");
|
||||
|
||||
buildSettings.SetPlatformTarget(PlatformTarget.ARM);
|
||||
MSBuild(Solution, buildSettings);
|
||||
|
||||
buildSettings.SetPlatformTarget(PlatformTarget.ARM64);
|
||||
MSBuild(Solution, buildSettings);
|
||||
|
||||
buildSettings.SetPlatformTarget(PlatformTarget.x64);
|
||||
MSBuild(Solution, buildSettings);
|
||||
|
||||
buildSettings.SetPlatformTarget(PlatformTarget.x86);
|
||||
MSBuild(Solution, buildSettings);
|
||||
|
||||
RetrieveVersion();
|
||||
|
||||
var nuGetPackSettings = new NuGetPackSettings
|
||||
{
|
||||
OutputDirectory = nupkgDir,
|
||||
Version = Version
|
||||
};
|
||||
|
||||
var nuspecs = GetFiles("./*.nuspec");
|
||||
foreach (var nuspec in nuspecs)
|
||||
{
|
||||
NuGetPack(nuspec, nuGetPackSettings);
|
||||
}
|
||||
});
|
||||
|
||||
public string getMSTestAdapterPath(){
|
||||
|
|
Загрузка…
Ссылка в новой задаче