Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Input / ManipulationLogic.cs / 1305600 / ManipulationLogic.cs
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Input.Manipulations;
using System.Windows.Media;
using System.Windows.Threading;
using MS.Internal;
using MS.Internal.PresentationCore;
namespace System.Windows.Input
{
///
/// Handles detection of manipulations.
///
internal sealed class ManipulationLogic
{
///
/// Instantiates an instance of this class.
///
internal ManipulationLogic(ManipulationDevice manipulationDevice)
{
_manipulationDevice = manipulationDevice;
}
///
/// Hooked up to the manipulation processor and inertia processor's started event.
///
///
/// SecurityCrticial: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationStarted event, which does not need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnManipulationStarted(object sender, Manipulation2DStartedEventArgs e)
{
PushEvent(new ManipulationStartedEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY)));
}
///
/// Hooked up to the manipulation processor and inertia processor's delta event.
///
///
/// SecurityCrticial: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationDelta event, which does not need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnManipulationDelta(object sender, Manipulation2DDeltaEventArgs e)
{
var deltaArguments = new ManipulationDeltaEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY),
ConvertDelta(e.Delta, null),
ConvertDelta(e.Cumulative, _lastManipulationBeforeInertia),
ConvertVelocities(e.Velocities),
IsInertiaActive);
PushEvent(deltaArguments);
}
///
/// Hooked up to the manipulation processor's completed event.
///
///
/// SecurityCritical: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationInertiaStartingEventArgs event, which doesn't need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnManipulationCompleted(object sender, Manipulation2DCompletedEventArgs e)
{
// Manipulation portion completed.
if (_manualComplete && !_manualCompleteWithInertia)
{
// This is the last event in the sequence.
ManipulationCompletedEventArgs completedArguments = ConvertCompletedArguments(e);
RaiseManipulationCompleted(completedArguments);
}
else
{
// This event will configure inertia, which will start after this event.
_lastManipulationBeforeInertia = ConvertDelta(e.Total, null);
ManipulationInertiaStartingEventArgs inertiaArguments = new ManipulationInertiaStartingEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY),
ConvertVelocities(e.Velocities),
false);
PushEvent(inertiaArguments);
}
_manipulationProcessor = null;
}
///
/// Hooked up to the inertia processor's completed event.
///
///
/// SecurityCritical: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationInertiaStarting event, which doesn't need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnInertiaCompleted(object sender, Manipulation2DCompletedEventArgs e)
{
// Inertia portion completed.
ClearTimer();
if (_manualComplete && _manualCompleteWithInertia)
{
// Another inertia portion was requested
_lastManipulationBeforeInertia = ConvertDelta(e.Total, _lastManipulationBeforeInertia);
ManipulationInertiaStartingEventArgs inertiaArguments = new ManipulationInertiaStartingEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY),
ConvertVelocities(e.Velocities),
true);
PushEvent(inertiaArguments);
}
else
{
// This is the last event in the sequence.
ManipulationCompletedEventArgs completedArguments = ConvertCompletedArguments(e);
RaiseManipulationCompleted(completedArguments);
}
_inertiaProcessor = null;
}
///
/// SecurityCritical: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationCompleted event, which doesn't need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void RaiseManipulationCompleted(ManipulationCompletedEventArgs e)
{
PushEvent(e);
}
///
/// Called after a Completed event has been processed.
///
internal void OnCompleted()
{
_lastManipulationBeforeInertia = null;
SetContainer(null);
}
///
/// Converts an Affine2DOperationCompletedEventArgs object into a ManipulationCompletedEventArgs object.
///
private ManipulationCompletedEventArgs ConvertCompletedArguments(Manipulation2DCompletedEventArgs e)
{
return new ManipulationCompletedEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY),
ConvertDelta(e.Total, _lastManipulationBeforeInertia),
ConvertVelocities(e.Velocities),
IsInertiaActive);
}
private static ManipulationDelta ConvertDelta(ManipulationDelta2D delta, ManipulationDelta add)
{
if (add != null)
{
return new ManipulationDelta(
new Vector(delta.TranslationX + add.Translation.X, delta.TranslationY + add.Translation.Y),
AngleUtil.RadiansToDegrees(delta.Rotation) + add.Rotation,
new Vector(delta.ScaleX * add.Scale.X, delta.ScaleY * add.Scale.Y),
new Vector(delta.ExpansionX + add.Expansion.X, delta.ExpansionY + add.Expansion.Y));
}
else
{
return new ManipulationDelta(
new Vector(delta.TranslationX, delta.TranslationY),
AngleUtil.RadiansToDegrees(delta.Rotation),
new Vector(delta.ScaleX, delta.ScaleY),
new Vector(delta.ExpansionX, delta.ExpansionY));
}
}
private static ManipulationVelocities ConvertVelocities(ManipulationVelocities2D velocities)
{
return new ManipulationVelocities(
new Vector(velocities.LinearVelocityX, velocities.LinearVelocityY),
AngleUtil.RadiansToDegrees(velocities.AngularVelocity),
new Vector(velocities.ExpansionVelocityX, velocities.ExpansionVelocityY));
}
///
/// Completes any pending manipulation or inerita processing.
///
///
/// If a manipulation is active, specifies whether to continue
/// to an inertia phase (true) or simply end the sequence (true).
///
internal void Complete(bool withInertia)
{
try
{
_manualComplete = true;
_manualCompleteWithInertia = withInertia;
if (IsManipulationActive)
{
_manipulationProcessor.CompleteManipulation(GetCurrentTimestamp());
}
else if (IsInertiaActive)
{
_inertiaProcessor.Complete(GetCurrentTimestamp());
}
}
finally
{
_manualComplete = false;
_manualCompleteWithInertia = false;
}
}
///
/// Gets ManipulationCompletedEventArgs object out of ManipulationInertiaStartingEventArgs
///
private ManipulationCompletedEventArgs GetManipulationCompletedArguments(ManipulationInertiaStartingEventArgs e)
{
Debug.Assert(_lastManipulationBeforeInertia != null);
return new ManipulationCompletedEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.ManipulationOrigin.X, e.ManipulationOrigin.Y),
_lastManipulationBeforeInertia,
e.InitialVelocities,
IsInertiaActive);
}
///
/// Starts the inertia phase based on the results of a ManipulationInertiaStarting event.
///
internal void BeginInertia(ManipulationInertiaStartingEventArgs e)
{
if (e.CanBeginInertia())
{
_inertiaProcessor = new InertiaProcessor2D();
_inertiaProcessor.Delta += OnManipulationDelta;
_inertiaProcessor.Completed += OnInertiaCompleted;
e.ApplyParameters(_inertiaProcessor);
// Setup a timer to tick the inertia to completion
_inertiaTimer = new DispatcherTimer();
_inertiaTimer.Interval = TimeSpan.FromMilliseconds(15);
_inertiaTimer.Tick += new EventHandler(OnInertiaTick);
_inertiaTimer.Start();
}
else
{
// This is the last event in the sequence.
ManipulationCompletedEventArgs completedArguments = GetManipulationCompletedArguments(e);
RaiseManipulationCompleted(completedArguments);
PushEventsToDevice();
}
}
internal static Int64 GetCurrentTimestamp()
{
// Does QueryPerformanceCounter to get the current time in 100ns units
return MediaContext.CurrentTicks;
}
private void OnInertiaTick(object sender, EventArgs e)
{
// Tick the inertia
if (IsInertiaActive)
{
if (!_inertiaProcessor.Process(GetCurrentTimestamp()))
{
ClearTimer();
}
PushEventsToDevice();
}
else
{
ClearTimer();
}
}
private void ClearTimer()
{
if (_inertiaTimer != null)
{
_inertiaTimer.Stop();
_inertiaTimer = null;
}
}
///
/// Prepares and raises a manipulation event.
///
///
/// Critical: Adds an input event to a list that will eventually be added to the InputManager queue.
/// Accesses _generatedEvent.
///
[SecurityCritical]
private void PushEvent(InputEventArgs e)
{
// We only expect to generate one event at a time and should never need a queue.
Debug.Assert(_generatedEvent == null, "There is already a generated event waiting to be pushed.");
_generatedEvent = e;
}
///
/// Pushes generated events to the inertia input provider.
///
///
/// SecurityCritical: ProcessManipulationInput. Accesses _generatedEvent.
/// TreatAsSafe: OK to send manipulation and inertia events.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void PushEventsToDevice()
{
if (_generatedEvent != null)
{
InputEventArgs generatedEvent = _generatedEvent;
_generatedEvent = null;
_manipulationDevice.ProcessManipulationInput(generatedEvent);
}
}
///
/// Raises ManipulationBoundaryFeedback to allow handlers to provide feedback that manipulation has hit an edge.
///
/// The total unused manipulation.
///
/// SecurityCrticial: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationBoundaryFeedbackEventArgs event, which does not need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void RaiseBoundaryFeedback(ManipulationDelta unusedManipulation, bool requestedComplete)
{
bool hasUnusedManipulation = (unusedManipulation != null);
if ((!hasUnusedManipulation || requestedComplete) && HasPendingBoundaryFeedback)
{
// Create a "zero" message to end currently pending feedback
unusedManipulation = new ManipulationDelta(new Vector(), 0.0, new Vector(1.0, 1.0), new Vector());
HasPendingBoundaryFeedback = false;
}
else if (hasUnusedManipulation)
{
HasPendingBoundaryFeedback = true;
}
if (unusedManipulation != null)
{
PushEvent(new ManipulationBoundaryFeedbackEventArgs(_manipulationDevice, LastTimestamp, _currentContainer, unusedManipulation));
}
}
private bool HasPendingBoundaryFeedback
{
get;
set;
}
private int LastTimestamp
{
get;
set;
}
internal void ReportFrame(ICollection manipulators)
{
Int64 timestamp = GetCurrentTimestamp();
LastTimestamp = (int)timestamp; // InputEventArgs timestamps are Int32 while the processors take Int64
int numManipulators = manipulators.Count;
if (IsInertiaActive && (numManipulators > 0))
{
// Inertia is active but now there are fingers, stop inertia
_inertiaProcessor.Complete(timestamp);
PushEventsToDevice();
}
if (!IsManipulationActive && (numManipulators > 0))
{
// Time to start a new manipulation
ManipulationStartingEventArgs startingArgs = RaiseStarting();
if (!startingArgs.RequestedCancel && (startingArgs.Mode != ManipulationModes.None))
{
// Determine if we allow single-finger manipulation
if (startingArgs.IsSingleTouchEnabled || (numManipulators >= 2))
{
SetContainer(startingArgs.ManipulationContainer);
_mode = startingArgs.Mode;
_pivot = startingArgs.Pivot;
IList parameters = startingArgs.Parameters;
_manipulationProcessor = new ManipulationProcessor2D(ConvertMode(_mode), ConvertPivot(_pivot));
if (parameters != null)
{
int count = parameters.Count;
for (int i = 0; i < parameters.Count; i++)
{
_manipulationProcessor.SetParameters(parameters[i]);
}
}
_manipulationProcessor.Started += OnManipulationStarted;
_manipulationProcessor.Delta += OnManipulationDelta;
_manipulationProcessor.Completed += OnManipulationCompleted;
_currentManipulators.Clear();
}
}
}
if (IsManipulationActive)
{
// A manipulation process is available to process this frame of manipulators
UpdateManipulators(manipulators);
_manipulationProcessor.ProcessManipulators(timestamp, CurrentManipulators);
PushEventsToDevice();
}
}
///
/// Critical - Calls ProcessManipulationInput.
/// TreatAsSafe - Creates the event being raised itself, an event that is not considered critical.
///
[SecurityCritical, SecurityTreatAsSafe]
private ManipulationStartingEventArgs RaiseStarting()
{
ManipulationStartingEventArgs starting = new ManipulationStartingEventArgs(_manipulationDevice, Environment.TickCount);
starting.ManipulationContainer = _manipulationDevice.Target;
_manipulationDevice.ProcessManipulationInput(starting);
return starting;
}
internal IInputElement ManipulationContainer
{
get { return _currentContainer; }
set
{
//
SetContainer(value);
}
}
internal ManipulationModes ManipulationMode
{
get { return _mode; }
set
{
_mode = value;
if (_manipulationProcessor != null)
{
_manipulationProcessor.SupportedManipulations = ConvertMode(_mode);
}
}
}
private static Manipulations2D ConvertMode(ManipulationModes mode)
{
Manipulations2D manipulations = Manipulations2D.None;
if ((mode & ManipulationModes.TranslateX) != 0)
{
manipulations |= Manipulations2D.TranslateX;
}
if ((mode & ManipulationModes.TranslateY) != 0)
{
manipulations |= Manipulations2D.TranslateY;
}
if ((mode & ManipulationModes.Scale) != 0)
{
manipulations |= Manipulations2D.Scale;
}
if ((mode & ManipulationModes.Rotate) != 0)
{
manipulations |= Manipulations2D.Rotate;
}
return manipulations;
}
internal ManipulationPivot ManipulationPivot
{
get { return _pivot; }
set
{
_pivot = value;
if (_manipulationProcessor != null)
{
_manipulationProcessor.Pivot = ConvertPivot(value);
}
}
}
private static ManipulationPivot2D ConvertPivot(ManipulationPivot pivot)
{
if (pivot != null)
{
Point center = pivot.Center;
return new ManipulationPivot2D()
{
X = (float)center.X,
Y = (float)center.Y,
Radius = (float)Math.Max(1.0, pivot.Radius)
};
}
return null;
}
internal void SetManipulationParameters(ManipulationParameters2D parameter)
{
if (_manipulationProcessor != null)
{
_manipulationProcessor.SetParameters(parameter);
}
}
private void UpdateManipulators(ICollection updatedManipulators)
{
// Clear out the old removed collection and use it to store
// the new current collection. The old current collection
// will be used to generate the new removed collection.
_removedManipulators.Clear();
var temp = _removedManipulators;
_removedManipulators = _currentManipulators;
_currentManipulators = temp;
// End the manipulation if the element is not
// visible anymore
UIElement uie = _currentContainer as UIElement;
if (uie != null)
{
if (!uie.IsVisible)
{
return;
}
}
else
{
UIElement3D uie3D = _currentContainer as UIElement3D;
if (uie3D != null &&
!uie3D.IsVisible)
{
return;
}
}
// For each updated manipulator, convert it to the correct format in the
// current collection and remove it from the removed collection. What is left
// in the removed collection will be the manipulators that were removed.
foreach (IManipulator updatedManipulator in updatedManipulators)
{
//
int id = updatedManipulator.Id;
_removedManipulators.Remove(id); // This manipulator was not removed
Point position = updatedManipulator.GetPosition(_currentContainer);
position = _manipulationDevice.GetTransformedManipulatorPosition(position);
_currentManipulators[id] = new Manipulator2D(id, (float)position.X, (float)position.Y);
}
}
///
/// Critical - Calls PresentationSource.CriticalFromVisual.
/// TreatAsSafe - Does not expose PresentationSource itself.
///
[SecurityCritical, SecurityTreatAsSafe]
private void SetContainer(IInputElement newContainer)
{
// unsubscribe from LayoutUpdated
UnsubscribeFromLayoutUpdated();
// clear cached values
_containerPivotPoint = new Point();
_containerSize = new Size();
_root = null;
// remember the new container
_currentContainer = newContainer;
if (newContainer != null)
{
// get the new root
PresentationSource presentationSource = PresentationSource.CriticalFromVisual((Visual)newContainer);
if (presentationSource != null)
{
_root = presentationSource.RootVisual as UIElement;
}
// subscribe to LayoutUpdated
if (_containerLayoutUpdated != null)
{
SubscribeToLayoutUpdated();
}
}
}
internal event EventHandler ContainerLayoutUpdated
{
add
{
bool wasNull = _containerLayoutUpdated == null;
_containerLayoutUpdated += value;
// if this is the first handler, try to subscribe to LayoutUpdated event
if (wasNull && _containerLayoutUpdated != null)
{
SubscribeToLayoutUpdated();
}
}
remove
{
bool wasNull = _containerLayoutUpdated == null;
_containerLayoutUpdated -= value;
// if this is the last handler, unsubscribe from LayoutUpdated event
if (!wasNull && _containerLayoutUpdated == null)
{
UnsubscribeFromLayoutUpdated();
}
}
}
private void SubscribeToLayoutUpdated()
{
UIElement container = _currentContainer as UIElement;
if (container != null)
{
container.LayoutUpdated += OnLayoutUpdated;
}
}
private void UnsubscribeFromLayoutUpdated()
{
UIElement container = _currentContainer as UIElement;
if (container != null)
{
container.LayoutUpdated -= OnLayoutUpdated;
}
}
///
/// OnLayoutUpdated handler, raises ContainerLayoutUpdated event if container's position or size have been changed
/// since the last LayoutUpdate.
///
///
///
private void OnLayoutUpdated(object sender, EventArgs e)
{
Debug.Assert(_containerLayoutUpdated != null);
//check position and size and update the cached values
if (UpdateCachedPositionAndSize())
{
_containerLayoutUpdated(this, EventArgs.Empty);
}
}
private bool UpdateCachedPositionAndSize()
{
// Determine if the manipulation needs to be updated because of position or size change.
// * Size change is detected by comparing RenderSize
// * Position change is detected by translating PivotPoint to the element coordinate, in general
// this is not accurate because rotation over PivotPoint won't be detected but the PivotPoint is selected far outside
// of the Window bounds, so practically that should be a very rare case.
// The more accurate solution would require 2 or 3 points which is more expensive.
if (_root == null)
{
return false;
}
UIElement container = _currentContainer as UIElement;
if (container == null)
{
return false;
}
Size renderSize = container.RenderSize;
Point translatedPivotPoint = _root.TranslatePoint(LayoutUpdateDetectionPivotPoint, container);
bool changed = (!DoubleUtil.AreClose(renderSize, _containerSize) ||
!DoubleUtil.AreClose(translatedPivotPoint, _containerPivotPoint));
if (changed)
{
// update cached values
_containerSize = renderSize;
_containerPivotPoint = translatedPivotPoint;
}
return changed;
}
private IEnumerable CurrentManipulators
{
get { return (_currentManipulators.Count > 0) ? _currentManipulators.Values : null; }
}
internal bool IsManipulationActive
{
get { return _manipulationProcessor != null; }
}
private bool IsInertiaActive
{
get { return _inertiaProcessor != null; }
}
private ManipulationDevice _manipulationDevice;
private IInputElement _currentContainer;
private ManipulationPivot _pivot;
private ManipulationModes _mode;
private ManipulationProcessor2D _manipulationProcessor;
private InertiaProcessor2D _inertiaProcessor;
// A list of manipulators that are currently active (i.e. fingers touching the screen)
private Dictionary _currentManipulators = new Dictionary(2);
// A list of manipulators that have been removed (stored to avoid allocating each frame)
private Dictionary _removedManipulators = new Dictionary(2);
// When inertia starts, its values are relative to the end point specified in
// this event. WPF's API wants to expose inertia deltas relative to the first
// Started event. This Completed event provides enough information to convert
// the delta values so that they are relative to the Started event.
private ManipulationDelta _lastManipulationBeforeInertia;
///
/// Critical: This event is sent to the input manager queue -- possible spoofing vector.
///
[SecurityCritical]
private InputEventArgs _generatedEvent;
private DispatcherTimer _inertiaTimer;
private bool _manualComplete;
private bool _manualCompleteWithInertia;
private EventHandler _containerLayoutUpdated;
// pivot point to detect position and size change, see UpdateCachedPositionAndSize for more details
// The odd magic number is to make it more rare.
private static readonly Point LayoutUpdateDetectionPivotPoint = new Point(-10234.1234, -10234.1234);
// cached values to detect position and size change
private Point _containerPivotPoint;
private Size _containerSize;
private UIElement _root;
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Input.Manipulations;
using System.Windows.Media;
using System.Windows.Threading;
using MS.Internal;
using MS.Internal.PresentationCore;
namespace System.Windows.Input
{
///
/// Handles detection of manipulations.
///
internal sealed class ManipulationLogic
{
///
/// Instantiates an instance of this class.
///
internal ManipulationLogic(ManipulationDevice manipulationDevice)
{
_manipulationDevice = manipulationDevice;
}
///
/// Hooked up to the manipulation processor and inertia processor's started event.
///
///
/// SecurityCrticial: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationStarted event, which does not need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnManipulationStarted(object sender, Manipulation2DStartedEventArgs e)
{
PushEvent(new ManipulationStartedEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY)));
}
///
/// Hooked up to the manipulation processor and inertia processor's delta event.
///
///
/// SecurityCrticial: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationDelta event, which does not need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnManipulationDelta(object sender, Manipulation2DDeltaEventArgs e)
{
var deltaArguments = new ManipulationDeltaEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY),
ConvertDelta(e.Delta, null),
ConvertDelta(e.Cumulative, _lastManipulationBeforeInertia),
ConvertVelocities(e.Velocities),
IsInertiaActive);
PushEvent(deltaArguments);
}
///
/// Hooked up to the manipulation processor's completed event.
///
///
/// SecurityCritical: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationInertiaStartingEventArgs event, which doesn't need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnManipulationCompleted(object sender, Manipulation2DCompletedEventArgs e)
{
// Manipulation portion completed.
if (_manualComplete && !_manualCompleteWithInertia)
{
// This is the last event in the sequence.
ManipulationCompletedEventArgs completedArguments = ConvertCompletedArguments(e);
RaiseManipulationCompleted(completedArguments);
}
else
{
// This event will configure inertia, which will start after this event.
_lastManipulationBeforeInertia = ConvertDelta(e.Total, null);
ManipulationInertiaStartingEventArgs inertiaArguments = new ManipulationInertiaStartingEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY),
ConvertVelocities(e.Velocities),
false);
PushEvent(inertiaArguments);
}
_manipulationProcessor = null;
}
///
/// Hooked up to the inertia processor's completed event.
///
///
/// SecurityCritical: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationInertiaStarting event, which doesn't need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnInertiaCompleted(object sender, Manipulation2DCompletedEventArgs e)
{
// Inertia portion completed.
ClearTimer();
if (_manualComplete && _manualCompleteWithInertia)
{
// Another inertia portion was requested
_lastManipulationBeforeInertia = ConvertDelta(e.Total, _lastManipulationBeforeInertia);
ManipulationInertiaStartingEventArgs inertiaArguments = new ManipulationInertiaStartingEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY),
ConvertVelocities(e.Velocities),
true);
PushEvent(inertiaArguments);
}
else
{
// This is the last event in the sequence.
ManipulationCompletedEventArgs completedArguments = ConvertCompletedArguments(e);
RaiseManipulationCompleted(completedArguments);
}
_inertiaProcessor = null;
}
///
/// SecurityCritical: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationCompleted event, which doesn't need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
private void RaiseManipulationCompleted(ManipulationCompletedEventArgs e)
{
PushEvent(e);
}
///
/// Called after a Completed event has been processed.
///
internal void OnCompleted()
{
_lastManipulationBeforeInertia = null;
SetContainer(null);
}
///
/// Converts an Affine2DOperationCompletedEventArgs object into a ManipulationCompletedEventArgs object.
///
private ManipulationCompletedEventArgs ConvertCompletedArguments(Manipulation2DCompletedEventArgs e)
{
return new ManipulationCompletedEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.OriginX, e.OriginY),
ConvertDelta(e.Total, _lastManipulationBeforeInertia),
ConvertVelocities(e.Velocities),
IsInertiaActive);
}
private static ManipulationDelta ConvertDelta(ManipulationDelta2D delta, ManipulationDelta add)
{
if (add != null)
{
return new ManipulationDelta(
new Vector(delta.TranslationX + add.Translation.X, delta.TranslationY + add.Translation.Y),
AngleUtil.RadiansToDegrees(delta.Rotation) + add.Rotation,
new Vector(delta.ScaleX * add.Scale.X, delta.ScaleY * add.Scale.Y),
new Vector(delta.ExpansionX + add.Expansion.X, delta.ExpansionY + add.Expansion.Y));
}
else
{
return new ManipulationDelta(
new Vector(delta.TranslationX, delta.TranslationY),
AngleUtil.RadiansToDegrees(delta.Rotation),
new Vector(delta.ScaleX, delta.ScaleY),
new Vector(delta.ExpansionX, delta.ExpansionY));
}
}
private static ManipulationVelocities ConvertVelocities(ManipulationVelocities2D velocities)
{
return new ManipulationVelocities(
new Vector(velocities.LinearVelocityX, velocities.LinearVelocityY),
AngleUtil.RadiansToDegrees(velocities.AngularVelocity),
new Vector(velocities.ExpansionVelocityX, velocities.ExpansionVelocityY));
}
///
/// Completes any pending manipulation or inerita processing.
///
///
/// If a manipulation is active, specifies whether to continue
/// to an inertia phase (true) or simply end the sequence (true).
///
internal void Complete(bool withInertia)
{
try
{
_manualComplete = true;
_manualCompleteWithInertia = withInertia;
if (IsManipulationActive)
{
_manipulationProcessor.CompleteManipulation(GetCurrentTimestamp());
}
else if (IsInertiaActive)
{
_inertiaProcessor.Complete(GetCurrentTimestamp());
}
}
finally
{
_manualComplete = false;
_manualCompleteWithInertia = false;
}
}
///
/// Gets ManipulationCompletedEventArgs object out of ManipulationInertiaStartingEventArgs
///
private ManipulationCompletedEventArgs GetManipulationCompletedArguments(ManipulationInertiaStartingEventArgs e)
{
Debug.Assert(_lastManipulationBeforeInertia != null);
return new ManipulationCompletedEventArgs(
_manipulationDevice,
LastTimestamp,
_currentContainer,
new Point(e.ManipulationOrigin.X, e.ManipulationOrigin.Y),
_lastManipulationBeforeInertia,
e.InitialVelocities,
IsInertiaActive);
}
///
/// Starts the inertia phase based on the results of a ManipulationInertiaStarting event.
///
internal void BeginInertia(ManipulationInertiaStartingEventArgs e)
{
if (e.CanBeginInertia())
{
_inertiaProcessor = new InertiaProcessor2D();
_inertiaProcessor.Delta += OnManipulationDelta;
_inertiaProcessor.Completed += OnInertiaCompleted;
e.ApplyParameters(_inertiaProcessor);
// Setup a timer to tick the inertia to completion
_inertiaTimer = new DispatcherTimer();
_inertiaTimer.Interval = TimeSpan.FromMilliseconds(15);
_inertiaTimer.Tick += new EventHandler(OnInertiaTick);
_inertiaTimer.Start();
}
else
{
// This is the last event in the sequence.
ManipulationCompletedEventArgs completedArguments = GetManipulationCompletedArguments(e);
RaiseManipulationCompleted(completedArguments);
PushEventsToDevice();
}
}
internal static Int64 GetCurrentTimestamp()
{
// Does QueryPerformanceCounter to get the current time in 100ns units
return MediaContext.CurrentTicks;
}
private void OnInertiaTick(object sender, EventArgs e)
{
// Tick the inertia
if (IsInertiaActive)
{
if (!_inertiaProcessor.Process(GetCurrentTimestamp()))
{
ClearTimer();
}
PushEventsToDevice();
}
else
{
ClearTimer();
}
}
private void ClearTimer()
{
if (_inertiaTimer != null)
{
_inertiaTimer.Stop();
_inertiaTimer = null;
}
}
///
/// Prepares and raises a manipulation event.
///
///
/// Critical: Adds an input event to a list that will eventually be added to the InputManager queue.
/// Accesses _generatedEvent.
///
[SecurityCritical]
private void PushEvent(InputEventArgs e)
{
// We only expect to generate one event at a time and should never need a queue.
Debug.Assert(_generatedEvent == null, "There is already a generated event waiting to be pushed.");
_generatedEvent = e;
}
///
/// Pushes generated events to the inertia input provider.
///
///
/// SecurityCritical: ProcessManipulationInput. Accesses _generatedEvent.
/// TreatAsSafe: OK to send manipulation and inertia events.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void PushEventsToDevice()
{
if (_generatedEvent != null)
{
InputEventArgs generatedEvent = _generatedEvent;
_generatedEvent = null;
_manipulationDevice.ProcessManipulationInput(generatedEvent);
}
}
///
/// Raises ManipulationBoundaryFeedback to allow handlers to provide feedback that manipulation has hit an edge.
///
/// The total unused manipulation.
///
/// SecurityCrticial: Calls PushEvent.
/// TreatAsSafe: Pushes a ManipulationBoundaryFeedbackEventArgs event, which does not need to be protected.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void RaiseBoundaryFeedback(ManipulationDelta unusedManipulation, bool requestedComplete)
{
bool hasUnusedManipulation = (unusedManipulation != null);
if ((!hasUnusedManipulation || requestedComplete) && HasPendingBoundaryFeedback)
{
// Create a "zero" message to end currently pending feedback
unusedManipulation = new ManipulationDelta(new Vector(), 0.0, new Vector(1.0, 1.0), new Vector());
HasPendingBoundaryFeedback = false;
}
else if (hasUnusedManipulation)
{
HasPendingBoundaryFeedback = true;
}
if (unusedManipulation != null)
{
PushEvent(new ManipulationBoundaryFeedbackEventArgs(_manipulationDevice, LastTimestamp, _currentContainer, unusedManipulation));
}
}
private bool HasPendingBoundaryFeedback
{
get;
set;
}
private int LastTimestamp
{
get;
set;
}
internal void ReportFrame(ICollection manipulators)
{
Int64 timestamp = GetCurrentTimestamp();
LastTimestamp = (int)timestamp; // InputEventArgs timestamps are Int32 while the processors take Int64
int numManipulators = manipulators.Count;
if (IsInertiaActive && (numManipulators > 0))
{
// Inertia is active but now there are fingers, stop inertia
_inertiaProcessor.Complete(timestamp);
PushEventsToDevice();
}
if (!IsManipulationActive && (numManipulators > 0))
{
// Time to start a new manipulation
ManipulationStartingEventArgs startingArgs = RaiseStarting();
if (!startingArgs.RequestedCancel && (startingArgs.Mode != ManipulationModes.None))
{
// Determine if we allow single-finger manipulation
if (startingArgs.IsSingleTouchEnabled || (numManipulators >= 2))
{
SetContainer(startingArgs.ManipulationContainer);
_mode = startingArgs.Mode;
_pivot = startingArgs.Pivot;
IList parameters = startingArgs.Parameters;
_manipulationProcessor = new ManipulationProcessor2D(ConvertMode(_mode), ConvertPivot(_pivot));
if (parameters != null)
{
int count = parameters.Count;
for (int i = 0; i < parameters.Count; i++)
{
_manipulationProcessor.SetParameters(parameters[i]);
}
}
_manipulationProcessor.Started += OnManipulationStarted;
_manipulationProcessor.Delta += OnManipulationDelta;
_manipulationProcessor.Completed += OnManipulationCompleted;
_currentManipulators.Clear();
}
}
}
if (IsManipulationActive)
{
// A manipulation process is available to process this frame of manipulators
UpdateManipulators(manipulators);
_manipulationProcessor.ProcessManipulators(timestamp, CurrentManipulators);
PushEventsToDevice();
}
}
///
/// Critical - Calls ProcessManipulationInput.
/// TreatAsSafe - Creates the event being raised itself, an event that is not considered critical.
///
[SecurityCritical, SecurityTreatAsSafe]
private ManipulationStartingEventArgs RaiseStarting()
{
ManipulationStartingEventArgs starting = new ManipulationStartingEventArgs(_manipulationDevice, Environment.TickCount);
starting.ManipulationContainer = _manipulationDevice.Target;
_manipulationDevice.ProcessManipulationInput(starting);
return starting;
}
internal IInputElement ManipulationContainer
{
get { return _currentContainer; }
set
{
//
SetContainer(value);
}
}
internal ManipulationModes ManipulationMode
{
get { return _mode; }
set
{
_mode = value;
if (_manipulationProcessor != null)
{
_manipulationProcessor.SupportedManipulations = ConvertMode(_mode);
}
}
}
private static Manipulations2D ConvertMode(ManipulationModes mode)
{
Manipulations2D manipulations = Manipulations2D.None;
if ((mode & ManipulationModes.TranslateX) != 0)
{
manipulations |= Manipulations2D.TranslateX;
}
if ((mode & ManipulationModes.TranslateY) != 0)
{
manipulations |= Manipulations2D.TranslateY;
}
if ((mode & ManipulationModes.Scale) != 0)
{
manipulations |= Manipulations2D.Scale;
}
if ((mode & ManipulationModes.Rotate) != 0)
{
manipulations |= Manipulations2D.Rotate;
}
return manipulations;
}
internal ManipulationPivot ManipulationPivot
{
get { return _pivot; }
set
{
_pivot = value;
if (_manipulationProcessor != null)
{
_manipulationProcessor.Pivot = ConvertPivot(value);
}
}
}
private static ManipulationPivot2D ConvertPivot(ManipulationPivot pivot)
{
if (pivot != null)
{
Point center = pivot.Center;
return new ManipulationPivot2D()
{
X = (float)center.X,
Y = (float)center.Y,
Radius = (float)Math.Max(1.0, pivot.Radius)
};
}
return null;
}
internal void SetManipulationParameters(ManipulationParameters2D parameter)
{
if (_manipulationProcessor != null)
{
_manipulationProcessor.SetParameters(parameter);
}
}
private void UpdateManipulators(ICollection updatedManipulators)
{
// Clear out the old removed collection and use it to store
// the new current collection. The old current collection
// will be used to generate the new removed collection.
_removedManipulators.Clear();
var temp = _removedManipulators;
_removedManipulators = _currentManipulators;
_currentManipulators = temp;
// End the manipulation if the element is not
// visible anymore
UIElement uie = _currentContainer as UIElement;
if (uie != null)
{
if (!uie.IsVisible)
{
return;
}
}
else
{
UIElement3D uie3D = _currentContainer as UIElement3D;
if (uie3D != null &&
!uie3D.IsVisible)
{
return;
}
}
// For each updated manipulator, convert it to the correct format in the
// current collection and remove it from the removed collection. What is left
// in the removed collection will be the manipulators that were removed.
foreach (IManipulator updatedManipulator in updatedManipulators)
{
//
int id = updatedManipulator.Id;
_removedManipulators.Remove(id); // This manipulator was not removed
Point position = updatedManipulator.GetPosition(_currentContainer);
position = _manipulationDevice.GetTransformedManipulatorPosition(position);
_currentManipulators[id] = new Manipulator2D(id, (float)position.X, (float)position.Y);
}
}
///
/// Critical - Calls PresentationSource.CriticalFromVisual.
/// TreatAsSafe - Does not expose PresentationSource itself.
///
[SecurityCritical, SecurityTreatAsSafe]
private void SetContainer(IInputElement newContainer)
{
// unsubscribe from LayoutUpdated
UnsubscribeFromLayoutUpdated();
// clear cached values
_containerPivotPoint = new Point();
_containerSize = new Size();
_root = null;
// remember the new container
_currentContainer = newContainer;
if (newContainer != null)
{
// get the new root
PresentationSource presentationSource = PresentationSource.CriticalFromVisual((Visual)newContainer);
if (presentationSource != null)
{
_root = presentationSource.RootVisual as UIElement;
}
// subscribe to LayoutUpdated
if (_containerLayoutUpdated != null)
{
SubscribeToLayoutUpdated();
}
}
}
internal event EventHandler ContainerLayoutUpdated
{
add
{
bool wasNull = _containerLayoutUpdated == null;
_containerLayoutUpdated += value;
// if this is the first handler, try to subscribe to LayoutUpdated event
if (wasNull && _containerLayoutUpdated != null)
{
SubscribeToLayoutUpdated();
}
}
remove
{
bool wasNull = _containerLayoutUpdated == null;
_containerLayoutUpdated -= value;
// if this is the last handler, unsubscribe from LayoutUpdated event
if (!wasNull && _containerLayoutUpdated == null)
{
UnsubscribeFromLayoutUpdated();
}
}
}
private void SubscribeToLayoutUpdated()
{
UIElement container = _currentContainer as UIElement;
if (container != null)
{
container.LayoutUpdated += OnLayoutUpdated;
}
}
private void UnsubscribeFromLayoutUpdated()
{
UIElement container = _currentContainer as UIElement;
if (container != null)
{
container.LayoutUpdated -= OnLayoutUpdated;
}
}
///
/// OnLayoutUpdated handler, raises ContainerLayoutUpdated event if container's position or size have been changed
/// since the last LayoutUpdate.
///
///
///
private void OnLayoutUpdated(object sender, EventArgs e)
{
Debug.Assert(_containerLayoutUpdated != null);
//check position and size and update the cached values
if (UpdateCachedPositionAndSize())
{
_containerLayoutUpdated(this, EventArgs.Empty);
}
}
private bool UpdateCachedPositionAndSize()
{
// Determine if the manipulation needs to be updated because of position or size change.
// * Size change is detected by comparing RenderSize
// * Position change is detected by translating PivotPoint to the element coordinate, in general
// this is not accurate because rotation over PivotPoint won't be detected but the PivotPoint is selected far outside
// of the Window bounds, so practically that should be a very rare case.
// The more accurate solution would require 2 or 3 points which is more expensive.
if (_root == null)
{
return false;
}
UIElement container = _currentContainer as UIElement;
if (container == null)
{
return false;
}
Size renderSize = container.RenderSize;
Point translatedPivotPoint = _root.TranslatePoint(LayoutUpdateDetectionPivotPoint, container);
bool changed = (!DoubleUtil.AreClose(renderSize, _containerSize) ||
!DoubleUtil.AreClose(translatedPivotPoint, _containerPivotPoint));
if (changed)
{
// update cached values
_containerSize = renderSize;
_containerPivotPoint = translatedPivotPoint;
}
return changed;
}
private IEnumerable CurrentManipulators
{
get { return (_currentManipulators.Count > 0) ? _currentManipulators.Values : null; }
}
internal bool IsManipulationActive
{
get { return _manipulationProcessor != null; }
}
private bool IsInertiaActive
{
get { return _inertiaProcessor != null; }
}
private ManipulationDevice _manipulationDevice;
private IInputElement _currentContainer;
private ManipulationPivot _pivot;
private ManipulationModes _mode;
private ManipulationProcessor2D _manipulationProcessor;
private InertiaProcessor2D _inertiaProcessor;
// A list of manipulators that are currently active (i.e. fingers touching the screen)
private Dictionary _currentManipulators = new Dictionary(2);
// A list of manipulators that have been removed (stored to avoid allocating each frame)
private Dictionary _removedManipulators = new Dictionary(2);
// When inertia starts, its values are relative to the end point specified in
// this event. WPF's API wants to expose inertia deltas relative to the first
// Started event. This Completed event provides enough information to convert
// the delta values so that they are relative to the Started event.
private ManipulationDelta _lastManipulationBeforeInertia;
///
/// Critical: This event is sent to the input manager queue -- possible spoofing vector.
///
[SecurityCritical]
private InputEventArgs _generatedEvent;
private DispatcherTimer _inertiaTimer;
private bool _manualComplete;
private bool _manualCompleteWithInertia;
private EventHandler _containerLayoutUpdated;
// pivot point to detect position and size change, see UpdateCachedPositionAndSize for more details
// The odd magic number is to make it more rare.
private static readonly Point LayoutUpdateDetectionPivotPoint = new Point(-10234.1234, -10234.1234);
// cached values to detect position and size change
private Point _containerPivotPoint;
private Size _containerSize;
private UIElement _root;
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlCountingReader.cs
- MediaContext.cs
- ParameterToken.cs
- GeometryDrawing.cs
- DataGridCaption.cs
- RuntimeResourceSet.cs
- HostingEnvironment.cs
- ComPlusAuthorization.cs
- WebPartChrome.cs
- ClientBuildManagerCallback.cs
- X509CertificateRecipientClientCredential.cs
- Trace.cs
- GlobalAclOperationRequirement.cs
- CheckBoxStandardAdapter.cs
- SHA384Managed.cs
- IndependentAnimationStorage.cs
- WindowProviderWrapper.cs
- _ScatterGatherBuffers.cs
- AdPostCacheSubstitution.cs
- RectangleConverter.cs
- StateItem.cs
- Positioning.cs
- ImageClickEventArgs.cs
- TimelineCollection.cs
- Nullable.cs
- StreamProxy.cs
- SQLMembershipProvider.cs
- ChangeBlockUndoRecord.cs
- TdsParser.cs
- CacheRequest.cs
- ExpressionNode.cs
- PrintDialog.cs
- JoinGraph.cs
- NullEntityWrapper.cs
- FlagsAttribute.cs
- PasswordDeriveBytes.cs
- ElementHostAutomationPeer.cs
- CodeIdentifiers.cs
- HtmlDocument.cs
- FunctionCommandText.cs
- MobileListItem.cs
- MatrixUtil.cs
- WriteTimeStream.cs
- RadialGradientBrush.cs
- TemplateControlBuildProvider.cs
- ViewBox.cs
- ParallelEnumerableWrapper.cs
- RenderCapability.cs
- DataObjectMethodAttribute.cs
- FamilyTypeface.cs
- UmAlQuraCalendar.cs
- CodeDirectoryCompiler.cs
- DispatcherOperation.cs
- NameSpaceExtractor.cs
- ReadOnlyObservableCollection.cs
- XmlSchemaAll.cs
- GlobalizationAssembly.cs
- SmiMetaData.cs
- SqlDataSourceQueryConverter.cs
- InstanceHandleReference.cs
- TraceRecords.cs
- SimpleFieldTemplateUserControl.cs
- AQNBuilder.cs
- ImageUrlEditor.cs
- SecurityContextSecurityTokenParameters.cs
- CompilerHelpers.cs
- SID.cs
- MethodRental.cs
- SourceChangedEventArgs.cs
- TextBreakpoint.cs
- ErrorEventArgs.cs
- TextAutomationPeer.cs
- StylusButton.cs
- ResXResourceSet.cs
- MobileControlBuilder.cs
- ScriptServiceAttribute.cs
- RightsManagementEncryptionTransform.cs
- PopupRoot.cs
- AppDomainUnloadedException.cs
- securitycriticaldataformultiplegetandset.cs
- RegistrationServices.cs
- Column.cs
- VectorAnimationBase.cs
- ListControlDesigner.cs
- DropTarget.cs
- ObjectSet.cs
- ParameterReplacerVisitor.cs
- CodeRegionDirective.cs
- MessageHeaderT.cs
- BindableTemplateBuilder.cs
- ExpressionVisitorHelpers.cs
- DataGridPageChangedEventArgs.cs
- TextEditorLists.cs
- Effect.cs
- HtmlHead.cs
- BaseParser.cs
- TableHeaderCell.cs
- BuildProvider.cs
- UnionCqlBlock.cs
- FlowDocumentPageViewerAutomationPeer.cs