ManipulationDevice.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Input / ManipulationDevice.cs / 1305600 / ManipulationDevice.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.Diagnostics; 
using System.Windows;
using System.Windows.Input;
using System.Windows.Input.Manipulations;
using System.Windows.Media; 
using System.Windows.Threading;
using System.Security; 
using MS.Internal; 
using MS.Internal.PresentationCore;
using MS.Utility; 


namespace System.Windows.Input
{ 
    /// 
    ///     Analyzes input events to be processed into manipulation and inertia events. 
    ///  
    internal sealed class ManipulationDevice : InputDevice
    { 
        /// 
        ///     Critical: Accesses PresentationSource.CriticalFromVisual.
        ///     TreatAsSafe: Stored in Critical field and exposed in Critical property.
        ///     Critical: Attaches to InputManager event handlers and stores a reference to the InputManager. 
        ///     TreatAsSafe: Does not expose the InputManager externally.
        ///  
        ///  
        ///     Created in AddManipulationDevice.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private ManipulationDevice(UIElement element) : base()
        {
            _target = element; 
            _activeSource = PresentationSource.CriticalFromVisual(element);
 
            _inputManager = InputManager.UnsecureCurrent; 
            _inputManager.PostProcessInput += new ProcessInputEventHandler(PostProcessInput);
 
            _manipulationLogic = new ManipulationLogic(this);
        }

        ///  
        ///     Critical: Detaches from InputManager event handlers.
        ///     TreatAsSafe: Does not expose the InputManager externally. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void DetachManipulationDevice() 
        {
            _inputManager.PostProcessInput -= new ProcessInputEventHandler(PostProcessInput);
        }
 
        /// 
        ///     Returns the element that input from this device is sent to. 
        ///  
        public override IInputElement Target
        { 
            get { return _target; }
        }

        ///  
        ///     Returns the PresentationSource of Target.
        ///  
        ///  
        ///     SecurityCritical: Exposes a PresentationSource.
        ///     PublicOK: There is a demand. 
        /// 
        public override PresentationSource ActiveSource
        {
            [SecurityCritical] 
            get
            { 
                SecurityHelper.DemandUIWindowPermission(); 
                return _activeSource;
            } 
        }

        /// 
        ///     Returns a ManipulationDevice associated with the given UIElement. 
        /// 
        /// The target of the ManipulationDevice. 
        ///  
        ///     A ManipulationDevice associated with the element.
        ///     If a device already exists for the element, a reference to that instance 
        ///     will be returned, otherwise a new instance will be created.
        /// 
        /// 
        ///     This function is thread-safe but should be called only on the 
        ///     same thread that 'element' is bound to, due to possibly calling
        ///     the ManipulationDevice constructor. 
        ///  
        internal static ManipulationDevice AddManipulationDevice(UIElement element)
        { 
            Debug.Assert(element != null, "element should be non-null.");

            element.VerifyAccess();
 
            ManipulationDevice device = GetManipulationDevice(element);
            if (device == null) 
            { 
                if (_manipulationDevices == null)
                { 
                    _manipulationDevices = new Dictionary(2);
                }

                device = new ManipulationDevice(element); 
                _manipulationDevices[element] = device;
            } 
 
            return device;
        } 

        /// 
        ///     Returns a ManipulationDevice associated with the given UIElement.
        ///  
        /// The target of the ManipulationDevice.
        ///  
        ///     A ManipulationDevice associated with the element. 
        ///     If a device does not already exists for the element, null is returned.
        ///  
        internal static ManipulationDevice GetManipulationDevice(UIElement element)
        {
            Debug.Assert(element != null, "element should be non-null.");
 
            if (_manipulationDevices != null)
            { 
                ManipulationDevice device; 
                _manipulationDevices.TryGetValue(element, out device);
                return device; 
            }

            return null;
        } 

        ///  
        ///     When a ManipulationDevice is no longer needed, remove it 
        ///     from the global list of devices.
        ///  
        private void RemoveManipulationDevice()
        {
            _wasTicking = false;
            StopTicking(); 
            DetachManipulationDevice();
            _compensateForBoundaryFeedback = null; 
 
            RemoveAllManipulators();
 
            if (_manipulationDevices != null)
            {
                _manipulationDevices.Remove(_target);
            } 
        }
 
        private void RemoveAllManipulators() 
        {
            if (_manipulators != null) 
            {
                for (int i = _manipulators.Count - 1; i >= 0; i--)
                {
                    _manipulators[i].Updated -= OnManipulatorUpdated; 
                }
                _manipulators.Clear(); 
            } 
        }
 
        internal void AddManipulator(IManipulator manipulator)
        {
            Debug.Assert(manipulator != null);
            VerifyAccess(); 
            _manipulationEnded = false;
 
            if (_manipulators == null) 
            {
                _manipulators = new List(2); 
            }

            _manipulators.Add(manipulator);
            manipulator.Updated += OnManipulatorUpdated; 

            // Adding a manipulator counts as an update 
            OnManipulatorUpdated(manipulator, EventArgs.Empty); 
        }
 
        internal void RemoveManipulator(IManipulator manipulator)
        {
            Debug.Assert(manipulator != null);
            VerifyAccess(); 

            manipulator.Updated -= OnManipulatorUpdated; 
            if (_manipulators != null) 
            {
                _manipulators.Remove(manipulator); 
            }

            // Removing a manipulator counts as an update
            OnManipulatorUpdated(manipulator, EventArgs.Empty); 
            if (!_manipulationEnded)
            { 
                if (_manipulators == null || _manipulators.Count == 0) 
                {
                    // cache the last removed manipulator 
                    _removedManipulator = manipulator;
                }
                // Call ReportFrame so that ManipulationInertiaStarting / ManipulationCompleted
                // gets called synchronously if needed 
                ReportFrame();
                _removedManipulator = null; 
            } 
        }
 
        /// 
        ///     Critical - Accesses _manipulationLogic.
        ///     TreatAsSafe - OK to pass this information
        ///  
        internal ManipulationModes ManipulationMode
        { 
            [SecurityCritical, SecurityTreatAsSafe] 
            get { return _manipulationLogic.ManipulationMode; }
            [SecurityCritical, SecurityTreatAsSafe] 
            set { _manipulationLogic.ManipulationMode = value; }
        }

        ///  
        ///     Critical - Accesses _manipulationLogic.
        ///     TreatAsSafe - OK to pass this information 
        ///  
        internal ManipulationPivot ManipulationPivot
        { 
            [SecurityCritical, SecurityTreatAsSafe]
            get { return _manipulationLogic.ManipulationPivot; }
            [SecurityCritical, SecurityTreatAsSafe]
            set { _manipulationLogic.ManipulationPivot = value; } 
        }
 
        ///  
        ///     Critical - Accesses _manipulationLogic.
        ///     TreatAsSafe - OK to pass this information 
        /// 
        internal IInputElement ManipulationContainer
        {
            [SecurityCritical, SecurityTreatAsSafe] 
            get { return _manipulationLogic.ManipulationContainer; }
            [SecurityCritical, SecurityTreatAsSafe] 
            set { _manipulationLogic.ManipulationContainer = value; } 
        }
 
        internal IEnumerable GetManipulatorsReadOnly()
        {
            if (_manipulators != null)
            { 
                return new ReadOnlyCollection(_manipulators);
            } 
            else 
            {
                return new ReadOnlyCollection(new List(2)); 
            }
        }

        internal void OnManipulatorUpdated(object sender, EventArgs e) 
        {
            // After a period of inactivity, the ManipulationDevice will stop polling at the screen framerate 
            // to stop wasting CPU usage. This notification will tell the device that activity is happening 
            // so that it can know to poll.
 
            LastUpdatedTimestamp = ManipulationLogic.GetCurrentTimestamp();
            ResumeAllTicking(); // Resumes the ticking of all the suspended devices on the thread
            StartTicking(); // Ensures that we continue ticking or restart ticking for this device
        } 

        internal Point GetTransformedManipulatorPosition(Point point) 
        { 
            if (_compensateForBoundaryFeedback != null)
            { 
                return _compensateForBoundaryFeedback(point);
            }
            return point;
        } 

        ///  
        ///     Starts the ticking for all the ManipulationDevices 
        ///     on the thread only if they were ticking earlier.
        ///  
        private static void ResumeAllTicking()
        {
            if (_manipulationDevices != null)
            { 
                foreach (UIElement element in _manipulationDevices.Keys)
                { 
                    ManipulationDevice device = _manipulationDevices[element]; 
                    if (device != null && device._wasTicking)
                    { 
                        device.StartTicking();
                        device._wasTicking = false;
                    }
                } 
            }
        } 
 
        private void StartTicking()
        { 
            if (!_ticking)
            {
                _ticking = true;
                CompositionTarget.Rendering += new EventHandler(OnRendering); 
                SubscribeToLayoutUpdate();
            } 
        } 

        private void StopTicking() 
        {
            if (_ticking)
            {
                CompositionTarget.Rendering -= new EventHandler(OnRendering); 
                _ticking = false;
                UnsubscribeFromLayoutUpdate(); 
            } 
        }
 
        [SecurityCritical, SecurityTreatAsSafe]
        private void SubscribeToLayoutUpdate()
        {
            _manipulationLogic.ContainerLayoutUpdated += OnContainerLayoutUpdated; 
        }
 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void UnsubscribeFromLayoutUpdate()
        { 
            _manipulationLogic.ContainerLayoutUpdated -= OnContainerLayoutUpdated;
        }

        private void OnContainerLayoutUpdated(object sender, EventArgs e) 
        {
            ReportFrame(); 
        } 

        private void OnRendering(object sender, EventArgs e) 
        {
            ReportFrame();

            // If Manipulation didn't activate or becomes disabled, then stop ticking. 
            // If we've exceeded the timeout without any manipulators updating, then stop ticking
            // to save energy. If a manipulator updates, we'll start ticking again. 
            if (!IsManipulationActive || 
                (ManipulationLogic.GetCurrentTimestamp() - LastUpdatedTimestamp) > ThrottleTimeout)
            { 
                _wasTicking = _ticking; // ReportFrame could have stopped the ticking, hence take the latest value.
                StopTicking();
            }
        } 

        ///  
        ///     Critical - Accesses _manipulationLogic. 
        ///     TreatAsSafe - Prods the logic object to do its work. Does not expose the object.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void ReportFrame()
        {
            if (!_manipulationEnded) 
            {
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, EventTrace.Event.ManipulationReportFrame, 0); 
 
                _manipulationLogic.ReportFrame(_manipulators);
            } 
        }

        /// 
        ///     Critical - Accesses _manipulationLogic. 
        ///     TreatAsSafe - OK to report if manipulation is enabled.
        ///  
        internal bool IsManipulationActive 
        {
            [SecurityCritical, SecurityTreatAsSafe] 
            get
            {
                return _manipulationLogic.IsManipulationActive;
            } 
        }
 
        ///  
        ///     Critical: This method can be used for input spoofing.
        ///  
        [SecurityCritical]
        private void PostProcessInput(object sender, ProcessInputEventArgs e)
        {
            InputEventArgs inputEventArgs = e.StagingItem.Input; 
            if (inputEventArgs.Device == this)
            { 
                RoutedEvent routedEvent = inputEventArgs.RoutedEvent; 
                if (routedEvent == Manipulation.ManipulationDeltaEvent)
                { 
                    ManipulationDeltaEventArgs deltaEventArgs = inputEventArgs as ManipulationDeltaEventArgs;
                    if (deltaEventArgs != null)
                    {
                        // During deltas, see if panning feedback is needed on the window 
                        ManipulationDelta unusedManipulation = deltaEventArgs.UnusedManipulation;
                        _manipulationLogic.RaiseBoundaryFeedback(unusedManipulation, deltaEventArgs.RequestedComplete); 
                        _manipulationLogic.PushEventsToDevice(); 

                        // If a Complete is requested, then pass it along to the manipulation processor 
                        if (deltaEventArgs.RequestedComplete)
                        {
                            _manipulationLogic.Complete(/* withInertia = */ deltaEventArgs.RequestedInertia);
                            _manipulationLogic.PushEventsToDevice(); 
                        }
                        else if (deltaEventArgs.RequestedCancel) 
                        { 
                            Debug.Assert(!deltaEventArgs.IsInertial);
                            OnManipulationCancel(); 
                        }
                    }
                }
                else if (routedEvent == Manipulation.ManipulationStartingEvent) 
                {
                    ManipulationStartingEventArgs startingEventArgs = inputEventArgs as ManipulationStartingEventArgs; 
                    if (startingEventArgs != null && startingEventArgs.RequestedCancel) 
                    {
                        OnManipulationCancel(); 
                    }
                }
                else if (routedEvent == Manipulation.ManipulationStartedEvent)
                { 
                    ManipulationStartedEventArgs startedEventArgs = inputEventArgs as ManipulationStartedEventArgs;
                    if (startedEventArgs != null) 
                    { 
                        if (startedEventArgs.RequestedComplete)
                        { 
                            // If a Complete is requested, pass it along to the manipulation processor
                            _manipulationLogic.Complete(/* withInertia = */ false);
                            _manipulationLogic.PushEventsToDevice();
                        } 
                        else if (startedEventArgs.RequestedCancel)
                        { 
                            OnManipulationCancel(); 
                        }
                        else 
                        {
                            // Start ticking to produce delta events
                            ResumeAllTicking(); // Resumes the ticking of all the suspended devices on the thread
                            StartTicking(); // Ensures that we continue ticking or restart ticking for this device 
                        }
                    } 
                } 
                else if (routedEvent == Manipulation.ManipulationInertiaStartingEvent)
                { 
                    // Switching from using rendering for ticking to a timer at lower priority (handled by ManipulationLogic)
                    StopTicking();

                    // Remove all the manipulators so that we dont re-start manipulations accidentally 
                    RemoveAllManipulators();
 
                    // Initialize inertia 
                    ManipulationInertiaStartingEventArgs inertiaEventArgs = inputEventArgs as ManipulationInertiaStartingEventArgs;
                    if (inertiaEventArgs != null) 
                    {
                        if (inertiaEventArgs.RequestedCancel)
                        {
                            OnManipulationCancel(); 
                        }
                        else 
                        { 
                            _manipulationLogic.BeginInertia(inertiaEventArgs);
                        } 
                    }
                }
                else if (routedEvent == Manipulation.ManipulationCompletedEvent)
                { 
                    _manipulationLogic.OnCompleted();
                    ManipulationCompletedEventArgs completedEventArgs = inputEventArgs as ManipulationCompletedEventArgs; 
                    if (completedEventArgs != null) 
                    {
                        if (completedEventArgs.RequestedCancel) 
                        {
                            Debug.Assert(!completedEventArgs.IsInertial);
                            OnManipulationCancel();
                        } 
                        else if (!(completedEventArgs.IsInertial && _ticking))
                        { 
                            // Remove the manipulation device only if 
                            // another manipulation didnot start
                            OnManipulationComplete(); 
                        }
                    }
                }
                else if (routedEvent == Manipulation.ManipulationBoundaryFeedbackEvent) 
                {
                    ManipulationBoundaryFeedbackEventArgs boundaryEventArgs = inputEventArgs as ManipulationBoundaryFeedbackEventArgs; 
                    if (boundaryEventArgs != null) 
                    {
                        _compensateForBoundaryFeedback = boundaryEventArgs.CompensateForBoundaryFeedback; 
                    }
                }
            }
        } 

        ///  
        ///     Critical: Calls IManipulator.ManipulationEnded which can 
        ///               potentially do mouse promotions.
        ///  
        [SecurityCritical]
        private void OnManipulationCancel()
        {
            _manipulationEnded = true; 
            if (_manipulators != null)
            { 
                if (_removedManipulator != null) 
                {
                    Debug.Assert(_manipulators == null || _manipulators.Count == 0); 
                    // Report Manipulation Cancel to last removed manipulator
                    _removedManipulator.ManipulationEnded(true);
                }
                else 
                {
                    // Report Manipulation Cancel to all the remaining manipulators 
                    List manipulators = new List(_manipulators); 
                    foreach (IManipulator manipulator in manipulators)
                    { 
                        manipulator.ManipulationEnded(true);
                    }
                }
            } 
            RemoveManipulationDevice();
        } 
 
        /// 
        ///     Critical: Calls IManipulator.ManipulationEnded which can 
        ///               potentially do mouse promotions.
        /// 
        [SecurityCritical]
        private void OnManipulationComplete() 
        {
            _manipulationEnded = true; 
            if (_manipulators != null) 
            {
                // Report Manipulation Complete to all the remaining manipulators 
                List manipulators = new List(_manipulators);
                foreach (IManipulator manipulator in manipulators)
                {
                    manipulator.ManipulationEnded(false); 
                }
            } 
            RemoveManipulationDevice(); 
        }
 
        /// 
        ///     Critical - Accesses _manipulationLogic.
        ///     TreatAsSafe - Does not expose the object.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal void SetManipulationParameters(ManipulationParameters2D parameter) 
        { 
            _manipulationLogic.SetManipulationParameters(parameter);
        } 

        /// 
        ///     Completes the pending manipulation or inertia.
        ///  
        /// 
        ///     Critical - Accesses _manipulationLogic. 
        ///     TreatAsSafe - Does not expose the object. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void CompleteManipulation(bool withInertia)
        {
            if (_manipulationLogic != null)
            { 
                _manipulationLogic.Complete(withInertia);
                _manipulationLogic.PushEventsToDevice(); 
            } 
        }
 
        /// 
        ///     Critical - Accesses _inputManager.
        /// 
        [SecurityCritical] 
        internal void ProcessManipulationInput(InputEventArgs e)
        { 
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, EventTrace.Event.ManipulationEventRaised, 0); 

            _inputManager.ProcessInput(e); 
        }

        /// 
        ///     Critical: This data is not safe to expose as it holds refrence to PresentationSource. 
        /// 
        [SecurityCritical] 
        private InputManager _inputManager; 

        ///  
        ///     Critical: Holds the the current manipulation state. Trusted to give manipulation events (and not spoofed input).
        /// 
        [SecurityCritical]
        private ManipulationLogic _manipulationLogic; 

        ///  
        ///     PresentationSource is protected data. 
        /// 
        [SecurityCritical] 
        private PresentationSource _activeSource;

        private UIElement _target;
        private List _manipulators; 
        private bool _ticking;
        private bool _wasTicking; // boolean used to track suspended manipulation devices 
        private Func _compensateForBoundaryFeedback; 
        private bool _manipulationEnded = false;
        IManipulator _removedManipulator = null; 

        [ThreadStatic]
        private static Int64 LastUpdatedTimestamp;
        private const Int64 ThrottleTimeout = TimeSpan.TicksPerSecond * 5; // 5 seconds (in 100ns units) of no activity will throttle down 

        [ThreadStatic] 
        private static Dictionary _manipulationDevices; 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK