InputManager.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 / InputManager.cs / 1305600 / InputManager.cs

                            using System.Collections; 
using System.Collections.Generic;
using System.Windows.Threading;
using System.Threading;
using System.Windows; 
using System.Security;
using System.Security.Permissions; 
using MS.Win32; 
using MS.Internal;
using MS.Internal.PresentationCore;                        // SecurityHelper 
using System;
using System.Diagnostics;
using System.Windows.Automation;
 
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID; 
 
namespace System.Windows.Input
{ 
    /// 
    ///     The InputManager class is responsible for coordinating all of the
    ///     input system in Avalon.
    ///  
    public sealed class InputManager : DispatcherObject
    { 
        ///  
        ///     A routed event indicating that an input report arrived.
        ///  
        internal static readonly RoutedEvent PreviewInputReportEvent = GlobalEventManager.RegisterRoutedEvent("PreviewInputReport", RoutingStrategy.Tunnel, typeof(InputReportEventHandler), typeof(InputManager));

        /// 
        ///     A routed event indicating that an input report arrived. 
        /// 
        [FriendAccessAllowed] 
        internal static readonly RoutedEvent InputReportEvent = GlobalEventManager.RegisterRoutedEvent("InputReport", RoutingStrategy.Bubble, typeof(InputReportEventHandler), typeof(InputManager)); 

        ///  
        ///     Return the input manager associated with the current context.
        /// 
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        ///  
        ///     Critical: This class is not ok to expose since SEE apps 
        ///               should not have to deal with this directly and
        ///               it exposes methods that can be use for input spoofing 
        ///
        /// 
        public static InputManager Current
        { 
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            get 
            {
                return GetCurrentInputManagerImpl(); 
            }
        }

        /// 
        ///     Internal implementation of InputManager.Current.
        ///     Critical but not TAS - for internal's to use. 
        ///     Only exists for perf. The link demand check was causing perf in some XAF scenarios. 
        ///
        ///  
        ///     Critical: This class is not ok to expose since SEE apps
        ///               should not have to deal with this directly and
        ///               it exposes methods that can be use for input spoofing
        /// 
        /// 
        internal static InputManager UnsecureCurrent 
        { 
            [SecurityCritical]
            [FriendAccessAllowed] 
            get
            {
                return GetCurrentInputManagerImpl();
            } 
        }
 
        /// 
        /// When true indicates input processing is synchronized.
        /// 
        internal static bool IsSynchronizedInput
        {
            get
            { 
                return _isSynchronizedInput;
            } 
        } 

        /// 
        /// Synchronized input event type.
        ///
        internal static RoutedEvent SynchronizedInputEvent
        { 
            get
            { 
                return _synchronizedInputEvent; 
            }
        } 

        ///
        /// Synchronized input type, set by the client.
        /// 
        internal static SynchronizedInputType SynchronizeInputType
        { 
            get 
            {
                return _synchronizedInputType; 
            }
        }

        /// 
        /// Element on which StartListening was called.
        /// 
        internal static DependencyObject ListeningElement 
        {
            get 
            {
                return _listeningElement;
            }
        } 

        /// 
        /// Indicates state of the event during synchronized processing. 
        ///
        internal static SynchronizedInputStates SynchronizedInputState 
        {
            get { return _synchronizedInputState; }
            set { _synchronizedInputState = value; }
        } 

        /// 
        ///     Implementation of InputManager.Current 
        ///
        ///  
        ///     Critical: This class is not ok to expose since SEE apps
        ///               should not have to deal with this directly and
        ///               it exposes methods that can be use for input spoofing
        ///  
        [SecurityCritical]
        private static InputManager GetCurrentInputManagerImpl() 
        { 
            InputManager inputManager = null;
 
            Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
            inputManager = dispatcher.InputManager as InputManager;

            if (inputManager == null) 
            {
                inputManager = new InputManager(); 
                dispatcher.InputManager = inputManager; 
            }
 
            return inputManager;
        }

        ///  
        ///     Critical: This code causes critical data (inputmanager to be instantiated)
        ///     This class should not be exposed in the SEE as it can be used for input spoofing. 
        ///  
        [SecurityCritical]
        private InputManager() 
        {
            // STA Requirement
            //
            // Avalon doesn't necessarily require STA, but many components do.  Examples 
            // include Cicero, OLE, COM, etc.  So we throw an exception here if the
            // thread is not STA. 
            if(Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) 
            {
                throw new InvalidOperationException(SR.Get(SRID.RequiresSTA)); 
            }

            _stagingArea = new Stack();
 
            _primaryKeyboardDevice = new Win32KeyboardDevice(this);
            _primaryMouseDevice = new Win32MouseDevice(this); 
            _primaryCommandDevice = new CommandDevice(this); 

            _stylusLogic = new StylusLogic(this); 

            _continueProcessingStagingAreaCallback = new DispatcherOperationCallback(ContinueProcessingStagingArea);

            _hitTestInvalidatedAsyncOperation = null; 
            _hitTestInvalidatedAsyncCallback = new DispatcherOperationCallback(HitTestInvalidatedAsyncCallback);
 
            _layoutUpdatedCallback = new EventHandler(OnLayoutUpdated); //need to cache it, LM only keeps weak ref 
            ContextLayoutManager.From(Dispatcher).LayoutEvents.Add(_layoutUpdatedCallback);
 
            // Timer used to synchronize the input devices periodically
            _inputTimer = new DispatcherTimer(DispatcherPriority.Background);
            _inputTimer.Tick += new EventHandler(ValidateInputDevices);
            _inputTimer.Interval = TimeSpan.FromMilliseconds(125); 
        }
 
        ///  
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        /// 
        ///     Critical: This event lets people subscribe to all events in the system
        ///     PublicOk: Method is link demanded. 
        /// 
        public event PreProcessInputEventHandler PreProcessInput 
        { 
            [SecurityCritical ]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            add
            {
                _preProcessInput += value;
            } 
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            remove 
            {
                _preProcessInput -= value; 
            }
        }

 
        /// 
        ///  
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        ///  
        ///     Critical: This event lets people subscribe to all events in the system
        ///     Not safe to expose.
        ///     PublicOk: Method is link demanded.
        ///  
        public event NotifyInputEventHandler PreNotifyInput
        { 
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            add 
            {
                _preNotifyInput += value;
            }
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            remove 
            { 
                _preNotifyInput -= value;
            } 

        }
        /// 
        ///  
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        ///  
        ///  
        ///     Critical: This event lets people subscribe to all events in the system
        ///     Not safe to expose. 
        ///     PublicOk: Method is link demanded.
        /// 
        public event NotifyInputEventHandler PostNotifyInput
        { 
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            add 
            {
                _postNotifyInput += value; 
            }
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            remove 
            {
                _postNotifyInput -= value; 
 
            }
        } 

        /// 
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        ///  
        ///     Critical: This event lets people subscribe to all events in the system 
        ///     Not safe to expose.
        ///     PublicOk: Method is link demanded. 
        /// 
        public event ProcessInputEventHandler PostProcessInput
        {
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            add 
            { 
                _postProcessInput += value;
            } 
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            remove
            { 
                _postProcessInput -= value;
            } 
        } 

        ///  
        /// This event is raised by the HwndSource.CriticalTranslateAccelerator
        /// on descendent HwndSource instances. The only subscriber to this event
        /// is KeyboardNavigation.
        ///  
        /// 
        ///     Critical: This event lets people subscribe to all input notifications 
        ///  
        internal event KeyEventHandler TranslateAccelerator
        { 
            [FriendAccessAllowed] // Used by KeyboardNavigation.cs in Framework
            [SecurityCritical]
            add
            { 
                _translateAccelerator += value;
            } 
            [FriendAccessAllowed] // Used by KeyboardNavigation.cs in Framework 
            [SecurityCritical]
            remove 
            {
                _translateAccelerator -= value;
            }
        } 

        ///  
        /// Raises the TranslateAccelerator event 
        /// 
        ///  
        ///     Critical: Accesses critical _translateAccelerator.
        /// 
        [SecurityCritical]
        internal void RaiseTranslateAccelerator(KeyEventArgs e) 
        {
            if (_translateAccelerator != null) 
            { 
                _translateAccelerator(this, e);
            } 
        }

        /// 
        ///     Registers an input provider with the input manager. 
        /// 
        ///  
        ///     The input provider to register. 
        /// 
        ///  
        ///     This class will not be available in internet zone.
        ///     Critical: This code acceses and stores critical data (InputProvider)
        ///     TreatAsSafe: This code demands UIPermission.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal InputProviderSite RegisterInputProvider(IInputProvider inputProvider) 
        { 
            SecurityHelper.DemandUnrestrictedUIPermission();
//             VerifyAccess(); 


            // Create a site for this provider, and keep track of it.
            InputProviderSite site = new InputProviderSite(this, inputProvider); 
            _inputProviders[inputProvider] = site;
 
            return site; 
        }
 
        /// 
        ///     This class will not be available in internet zone.
        ///     Critical: This code acceses critical data in the form of InputProvider
        ///  
        [SecurityCritical]
        internal void UnregisterInputProvider(IInputProvider inputProvider) 
        { 
            _inputProviders.Remove(inputProvider);
        } 

        /// 
        ///     Returns a collection of input providers registered with the input manager.
        ///  
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        ///  
        /// 
        ///     This class will not be available in internet zone. 
        ///     Critical: This code exposes InputProviders which are
        ///               considered as critical data
        ///     PublicOK: This code has a demand on it
        ///  
        public ICollection InputProviders
        { 
            [SecurityCritical] 
            get
            { 
                SecurityHelper.DemandUnrestrictedUIPermission();
                return UnsecureInputProviders;
            }
        } 

 
        ///  
        ///     Returns a collection of input providers registered with the input manager.
        ///  
        /// 
        ///     Critical: This code exposes InputProviders which are considered as critical data
        ///     This overload exists for perf. improvements in the internet zone since this function is called
        ///     quite often 
        /// 
        internal ICollection UnsecureInputProviders 
        { 
            [SecurityCritical]
            get 
            {
                return _inputProviders.Keys;
            }
        } 
        /// 
        ///     Read-only access to the primary keyboard device. 
        ///  
        public KeyboardDevice PrimaryKeyboardDevice
        { 
            //
            get {return _primaryKeyboardDevice;}
        }
 
        /// 
        ///     Read-only access to the primary mouse device. 
        ///  
        public MouseDevice PrimaryMouseDevice
        { 
            //
            get {return _primaryMouseDevice;}
        }
        ///  
        ///     Critical, accesses critical member _stylusLogic
        ///  
        internal StylusLogic StylusLogic 
        {
            [SecurityCritical, FriendAccessAllowed] 
            get { return _stylusLogic; }
        }

        ///  
        ///     Read-only access to the primary keyboard device.
        ///  
        internal CommandDevice PrimaryCommandDevice 
        {
            get {return _primaryCommandDevice;} 
        }

        /// 
        ///     The InDragDrop property represents whether we are currently inside 
        ///     a OLE DragDrop operation.
        ///  
        internal bool InDragDrop 
        {
            get { return _inDragDrop; } 
            set { _inDragDrop = value; }
        }

        ///  
        ///     The MostRecentInputDevice represents the last input device to
        ///     report an "interesting" user action.  What exactly constitutes 
        ///     such an action is up to each device to implement. 
        /// 
        public InputDevice MostRecentInputDevice 
        {
            get { return _mostRecentInputDevice; }
            internal set { _mostRecentInputDevice = value; }
        } 

        ///  
        ///     Controls call this to enter menu mode. 
        ///
        public void PushMenuMode(PresentationSource menuSite) 
        {
            if (menuSite == null)
            {
                throw new ArgumentNullException("menuSite"); 
            }
            menuSite.VerifyAccess(); 
 
            menuSite.PushMenuMode();
            _menuModeCount += 1; 

            if (1 == _menuModeCount)
            {
                EventHandler enterMenuMode = EnterMenuMode; 
                if (null != enterMenuMode)
                { 
                    enterMenuMode(null, EventArgs.Empty); 
                }
            } 
        }

        /// 
        ///     Controls call this to leave menu mode. 
        ///
        public void PopMenuMode(PresentationSource menuSite) 
        { 
            if (menuSite == null)
            { 
                throw new ArgumentNullException("menuSite");
            }
            menuSite.VerifyAccess();
 
            if (_menuModeCount <= 0)
            { 
                throw new InvalidOperationException(); 
            }
 
            menuSite.PopMenuMode();
            _menuModeCount -= 1;

            if (0 == _menuModeCount) 
            {
                EventHandler leaveMenuMode = LeaveMenuMode; 
                if (null != leaveMenuMode) 
                {
                    leaveMenuMode(null, EventArgs.Empty); 
                }
            }
        }
 
        /// 
        ///     Returns whether or not the input manager is in menu mode. 
        /// 
        public bool IsInMenuMode
        { 
            get
            {
                return (_menuModeCount > 0);
            } 
        }
 
        ///  
        ///     This event notifies when the input manager enters menu mode.
        /// 
        public event EventHandler EnterMenuMode;

        /// 
        ///     This event notifies when the input manager leaves menu mode. 
        ///
        public event EventHandler LeaveMenuMode; 
 
        private int _menuModeCount;
 
        /// 
        ///     An event that is raised whenever the result of a hit-test may
        ///     have changed.
        ///  
        public event EventHandler HitTestInvalidatedAsync;
 
        internal void NotifyHitTestInvalidated() 
        {
            // The HitTest result may have changed for someone somewhere. 
            // Raise the HitTestInvalidatedAsync event after the next layout.
            if(_hitTestInvalidatedAsyncOperation == null)
            {
                // It would be best to re-evaluate anything dependent on the hit-test results 
                // immediately after layout & rendering are complete.  Unfortunately this can
                // lead to an infinite loop.  Consider the following scenario: 
                // 
                // If the mouse is over an element, hide it.
                // 
                // This never resolves to a "correct" state.  When the mouse moves over the
                // element, the element is hidden, so the mouse is no longer over it, so the
                // element is shown, but that means the mouse is over it again.  Repeat.
                // 
                // We push our re-evaluation to a priority lower than input processing so that
                // the user can change the input device to avoid the infinite loops, or close 
                // the app if nothing else works. 
                //
                _hitTestInvalidatedAsyncOperation = Dispatcher.BeginInvoke(DispatcherPriority.Input, 
                                                                        _hitTestInvalidatedAsyncCallback,
                                                                        null);
            }
            else if (_hitTestInvalidatedAsyncOperation.Priority == DispatcherPriority.Inactive) 
            {
                // This means that we are currently waiting for the timer to expire so 
                // that we can promote the current queue item to Input prority. Since 
                // we are now being told that we need to re-hittest, we simply stop the
                // timer and promote the queue item right now instead of waiting for expiry. 

                ValidateInputDevices(this, EventArgs.Empty);
            }
        } 

 
        /// 
        ///     Critical - calls UnsecureCurrent
        ///     TreatAsSafe - notifying the input manager that hit test information needs to be recalced. 
        ///                   is considered safe ( and currently this code is transparent).
        ///
        [SecurityCritical, SecurityTreatAsSafe]
        internal static void SafeCurrentNotifyHitTestInvalidated() 
        {
            UnsecureCurrent.NotifyHitTestInvalidated(); 
        } 

        private object HitTestInvalidatedAsyncCallback(object arg) 
        {
            _hitTestInvalidatedAsyncOperation = null;
            if (HitTestInvalidatedAsync != null)
            { 
                HitTestInvalidatedAsync(this, EventArgs.Empty);
            } 
 
            return null;
        } 

        private void OnLayoutUpdated(object sender, EventArgs e)
        {
            NotifyHitTestInvalidated(); 
        }
 
        ///  
        /// Start the timer that will kick off synchronize
        /// operation on all the input devices upon expiry 
        /// 
        internal void InvalidateInputDevices()
        {
            // If there is no pending ansyc hittest operation 

            if (_hitTestInvalidatedAsyncOperation == null) 
            { 
                // Post an inactive item to the queue. When the timer expires
                // we will promote this queue item to Input priority. 

                _hitTestInvalidatedAsyncOperation = Dispatcher.BeginInvoke(DispatcherPriority.Inactive,
                                                                        _hitTestInvalidatedAsyncCallback,
                                                                        null); 

                // Start the input timer 
 
                _inputTimer.IsEnabled = true;
            } 
        }

        /// 
        /// Synchronize the  input devices 
        /// 
        private void ValidateInputDevices(object sender, EventArgs e) 
        { 
            // This null check was necessary as a fix for Dev10 bug #453002. It turns out that
            // somehow we get here after the DispatcherOperation has been dispatched and we 
            // need to no-op on that.

            if (_hitTestInvalidatedAsyncOperation != null)
            { 

                // Promote the pending DispatcherOperation to Input Priority 
 
                _hitTestInvalidatedAsyncOperation.Priority = DispatcherPriority.Input;
 
            }

            // Stop the input timer
 
            _inputTimer.IsEnabled = false;
        } 
 
        /// 
        ///     Synchronously processes the specified input. 
        /// 
        /// 
        ///     The specified input is processed by all of the filters and
        ///     monitors, and is finally dispatched to the appropriate 
        ///     element as an input event.
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        ///  
        /// 
        ///     Whether or not any event generated as a consequence of this 
        ///     event was handled.
        /// 
        /// 
        ///     Critical: This code can cause input to be processed. 
        ///     PublicOK: This code link demands.
        ///  
        [SecurityCritical] 
        [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
        public bool ProcessInput(InputEventArgs input) 
        {
//             VerifyAccess();

            if(input == null) 
            {
                throw new ArgumentNullException("input"); 
            } 

            // Push a marker indicating the portion of the staging area 
            // that needs to be processed.
            PushMarker();

            // Push the input to be processed onto the staging area. 
            PushInput(input, null);
 
            // Post a work item to continue processing the staging area 
            // in case someone pushes a dispatcher frame in the middle
            // of input processing. 
            RequestContinueProcessingStagingArea();

            // Now drain the staging area up to the marker we pushed.
            bool handled = ProcessStagingArea(); 
            return handled;
        } 
 
        ///
        /// Critical - accesses critical data ( _stagingArea) 
        ///
        [SecurityCritical]
        internal StagingAreaInputItem PushInput(StagingAreaInputItem inputItem)
        { 
            _stagingArea.Push(inputItem);
            return inputItem; 
        } 

        /// 
        /// Critical - accesses critical function ( PushInput)
        ///
        [SecurityCritical]
        internal StagingAreaInputItem PushInput(InputEventArgs input, StagingAreaInputItem promote) 
        {
            StagingAreaInputItem item = new StagingAreaInputItem(false); 
            item.Reset(input, promote); 

            return PushInput(item); 
        }

        ///
        /// Critical - calls a critical function ( PushInput). 
        ///
        [SecurityCritical] 
        internal StagingAreaInputItem PushMarker() 
        {
            StagingAreaInputItem item = new StagingAreaInputItem(true); 

            return PushInput(item);
        }
 
        ///
        /// Critical - accesses critical data _stagingArea. 
        /// 
        [SecurityCritical]
        internal StagingAreaInputItem PopInput() 
        {
            object input = null;

            if(_stagingArea.Count > 0) 
            {
                input = _stagingArea.Pop(); 
            } 

            return input as StagingAreaInputItem; 
        }


        /// 
        /// Critical - accesses the _stagingArea critical data.
        /// 
        [SecurityCritical] 
        internal StagingAreaInputItem PeekInput()
        { 
            object input = null;

            if(_stagingArea.Count > 0)
            { 
                input = _stagingArea.Peek();
            } 
 
            return input as StagingAreaInputItem;
        } 

        ///
        /// Critical - accesses critical data ( _stagingArea.Count) and calls a critical function - ProcessStagingArea
        /// 
        [SecurityCritical ]
        internal object ContinueProcessingStagingArea(object unused) 
        { 
            _continueProcessingStagingArea = false;
 
            // It is possible that we can be re-entered by a nested
            // dispatcher frame.  Continue processing the staging
            // area if we need to.
            if(_stagingArea.Count > 0) 
            {
                // Before we actually start to drain the staging area, we need 
                // to post a work item to process more input.  This enables us 
                // to process more input if we enter a nested pump.
                RequestContinueProcessingStagingArea(); 

                // Now synchronously drain the staging area.
                ProcessStagingArea();
            } 

            return null; 
        } 

        // When called, InputManager will get into synchronized input processing mode. 
        internal static bool StartListeningSynchronizedInput(DependencyObject d, SynchronizedInputType inputType)
        {
            lock (_synchronizedInputLock)
            { 
                if (_isSynchronizedInput)
                { 
                    return false; 
                }
                else 
                {
                    _isSynchronizedInput = true;
                    _synchronizedInputState = SynchronizedInputStates.NoOpportunity;
                    _listeningElement = d; 
                    _synchronizedInputType = inputType;
                    _synchronizedInputEvent = SynchronizedInputHelper.MapInputTypeToRoutedEvent(inputType); 
                    return true; 
                }
            } 
        }

        // This method is used to cancel synchronized input processing.
        internal static void CancelSynchronizedInput() 
        {
            lock (_synchronizedInputLock) 
            { 
                _isSynchronizedInput = false;
                _synchronizedInputState = SynchronizedInputStates.NoOpportunity; 
                _listeningElement = null;
                _synchronizedInputEvent = null;
            }
        } 

 
        /// 
        /// Critical: accesses critical data ( PopInput()) and raises events with the user-initiated flag
        /// 
        [SecurityCritical]
        private bool ProcessStagingArea()
        {
            bool handled = false; 

            // For performance reasons, try to reuse the input event args. 
            // If we are reentrered, we have to start over with fresh event 
            // args, so we clear the member variables before continuing.
            // Also, we cannot simply make an single instance of the 
            // PreProcessedInputEventArgs and cast it to NotifyInputEventArgs
            // or ProcessInputEventArgs because a malicious user could upcast
            // the object and call inappropriate methods.
            NotifyInputEventArgs notifyInputEventArgs = (_notifyInputEventArgs != null) ? _notifyInputEventArgs : new NotifyInputEventArgs(); 
            ProcessInputEventArgs processInputEventArgs = (_processInputEventArgs != null) ? _processInputEventArgs : new ProcessInputEventArgs();
            PreProcessInputEventArgs preProcessInputEventArgs = (_preProcessInputEventArgs != null) ? _preProcessInputEventArgs : new PreProcessInputEventArgs(); 
            _notifyInputEventArgs = null; 
            _processInputEventArgs = null;
            _preProcessInputEventArgs = null; 

            // Because we can be reentered, we can't just enumerate over the
            // staging area - that could throw an exception if the queue
            // changes underneath us.  Instead, just loop until we find a 
            // frame marker or until the staging area is empty.
            StagingAreaInputItem item = null; 
            while((item = PopInput()) != null) 
            {
                // If we found a marker, we have reached the end of a 
                // "section" of the staging area.  We just return from
                // the synchronous processing of the staging area.
                // If a dispatcher frame has been pushed by someone, this
                // will not return to the original ProcessInput.  Instead 
                // it will unwind to the dispatcher and since we have
                // already pushed a work item to continue processing the 
                // input, it will simply call back into us to do more 
                // processing.  At which point we will continue to drain
                // the staging area.  This could cause strage behavior, 
                // but it is deemed more acceptable than stalling input
                // processing.
                //
 

 
 
                if(item.IsMarker)
                { 
                    break;
                }

                // Pre-Process the input.  This could modify the staging 
                // area, and it could cancel the processing of this
                // input event. 
                // 
                // Because we use multi-cast delegates, we always have to
                // create a new multi-cast delegate when we add or remove 
                // a handler.  This means we can just call the current
                // multi-cast delegate instance, and it is safe to iterate
                // over, even if we get reentered.
                if (_preProcessInput != null) 
                {
                    preProcessInputEventArgs.Reset(item, this); 
 
                    // Invoke the handlers in reverse order so that handlers that
                    // users add are invoked before handlers in the system. 
                    Delegate[] handlers = _preProcessInput.GetInvocationList();
                    for(int i = (handlers.Length - 1); i >= 0; i--)
                    {
                        PreProcessInputEventHandler handler = (PreProcessInputEventHandler) handlers[i]; 
                        handler(this, preProcessInputEventArgs);
                    } 
                } 

                if(!preProcessInputEventArgs.Canceled) 
                {
                    // Pre-Notify the input.
                    //
                    // Because we use multi-cast delegates, we always have to 
                    // create a new multi-cast delegate when we add or remove
                    // a handler.  This means we can just call the current 
                    // multi-cast delegate instance, and it is safe to iterate 
                    // over, even if we get reentered.
                    if(_preNotifyInput != null) 
                    {
                        notifyInputEventArgs.Reset(item, this);

                        // Invoke the handlers in reverse order so that handlers that 
                        // users add are invoked before handlers in the system.
                        Delegate[] handlers = _preNotifyInput.GetInvocationList(); 
                        for(int i = (handlers.Length - 1); i >= 0; i--) 
                        {
                            NotifyInputEventHandler handler = (NotifyInputEventHandler) handlers[i]; 
                            handler(this, notifyInputEventArgs);
                        }
                    }
 
                    // Raise the input event being processed.
                    InputEventArgs input = item.Input; 
 
                    // Some input events are explicitly associated with
                    // an element.  Those that are not are associated with 
                    // the target of the input device for this event.
                    DependencyObject eventSource = input.Source as DependencyObject;
                    if(eventSource == null || !InputElement.IsValid(eventSource as IInputElement))
                    { 
                        if (input.Device != null)
                        { 
                            eventSource = input.Device.Target as DependencyObject; 
                        }
                    } 

                    // During synchronized input processing, event should be discarded if the lsitening element is not in the route.
                    if (_isSynchronizedInput && SynchronizedInputHelper.IsListening(_listeningElement, input) &&
                        ((eventSource == null) || !SynchronizedInputHelper.IsElementInEventRoute(_listeningElement, eventSource))) 
                    {
                        // Discard the event 
                        _synchronizedInputState = SynchronizedInputStates.Discarded; 
                        SynchronizedInputHelper.RaiseAutomationEvents();
                        CancelSynchronizedInput(); 

                    }
                    else
                    { 
                        if (eventSource != null)
                        { 
                            if (InputElement.IsUIElement(eventSource)) 
                            {
                                UIElement e = (UIElement)eventSource; 

                                e.RaiseEvent(input, true); // Call the "trusted" flavor of RaiseEvent.
                            }
                            else if (InputElement.IsContentElement(eventSource)) 
                            {
                                ContentElement ce = (ContentElement)eventSource; 
 
                                ce.RaiseEvent(input, true);// Call the "trusted" flavor of RaiseEvent.
                            } 
                            else if (InputElement.IsUIElement3D(eventSource))
                            {
                                UIElement3D e3D = (UIElement3D)eventSource;
 
                                e3D.RaiseEvent(input, true); // Call the "trusted" flavor of RaiseEvent
                            } 
 
                            // If synchronized input raise appropriate automation event.
                            if (_isSynchronizedInput && SynchronizedInputHelper.IsListening(_listeningElement, input)) 
                            {
                                SynchronizedInputHelper.RaiseAutomationEvents();
                                CancelSynchronizedInput();
                            } 
                        }
                    } 
 
                    // Post-Notify the input.
                    // 
                    // Because we use multi-cast delegates, we always have to
                    // create a new multi-cast delegate when we add or remove
                    // a handler.  This means we can just call the current
                    // multi-cast delegate instance, and it is safe to iterate 
                    // over, even if we get reentered.
                    if(_postNotifyInput != null) 
                    { 
                        notifyInputEventArgs.Reset(item, this);
 
                        // Invoke the handlers in reverse order so that handlers that
                        // users add are invoked before handlers in the system.
                        Delegate[] handlers = _postNotifyInput.GetInvocationList();
                        for(int i = (handlers.Length - 1); i >= 0; i--) 
                        {
                            NotifyInputEventHandler handler = (NotifyInputEventHandler) handlers[i]; 
                            handler(this, notifyInputEventArgs); 
                        }
                    } 

                    // Post-Process the input.  This could modify the staging
                    // area.
                    // 
                    // Because we use multi-cast delegates, we always have to
                    // create a new multi-cast delegate when we add or remove 
                    // a handler.  This means we can just call the current 
                    // multi-cast delegate instance, and it is safe to iterate
                    // over, even if we get reentered. 
                    if(_postProcessInput != null)
                    {
                        processInputEventArgs.Reset(item, this);
 
                        RaiseProcessInputEventHandlers(_postProcessInput, processInputEventArgs);
 
                        // PreviewInputReport --> InputReport 
                        if(item.Input.RoutedEvent == InputManager.PreviewInputReportEvent)
                        { 
                            if(!item.Input.Handled)
                            {
                                InputReportEventArgs previewInputReport = (InputReportEventArgs) item.Input;
 
                                InputReportEventArgs inputReport = new InputReportEventArgs(previewInputReport.Device, previewInputReport.Report);
                                inputReport.RoutedEvent=InputManager.InputReportEvent; 
                                PushInput(inputReport, item); 
                            }
                        } 
                    }

                    if(input.Handled)
                    { 
                        handled = true;
                    } 
                } 
            }
 
            // Store our input event args so that we can use them again, and
            // avoid having to allocate more.
            _notifyInputEventArgs = notifyInputEventArgs;
            _processInputEventArgs = processInputEventArgs; 
            _preProcessInputEventArgs = preProcessInputEventArgs;
 
            // Make sure to throw away the contents of the event args so 
            // we don't keep refs around to things we don't mean to.
            _notifyInputEventArgs.Reset(null, null); 
            _processInputEventArgs.Reset(null, null);
            _preProcessInputEventArgs.Reset(null, null);

            return handled; 
        }
 
        /// 
        ///  Critical - sets the MarkAsUserInitiated bit.
        /// 
        [SecurityCritical]
        [MS.Internal.Permissions.UserInitiatedRoutedEventPermissionAttribute(SecurityAction.Assert)]
        private void RaiseProcessInputEventHandlers(ProcessInputEventHandler postProcessInput, ProcessInputEventArgs processInputEventArgs)
        { 
            processInputEventArgs.StagingItem.Input.MarkAsUserInitiated();
 
            try 
            {
                // Invoke the handlers in reverse order so that handlers that 
                // users add are invoked before handlers in the system.
                Delegate[] handlers = postProcessInput.GetInvocationList();
                for(int i = (handlers.Length - 1); i >= 0; i--)
                { 
                    ProcessInputEventHandler handler = (ProcessInputEventHandler) handlers[i];
                    handler(this, processInputEventArgs); 
                } 
            }
            finally // we do this in a finally block in case of exceptions 
            {
                processInputEventArgs.StagingItem.Input.ClearUserInitiated();
            }
        } 

 
        private void RequestContinueProcessingStagingArea() 
        {
            if(!_continueProcessingStagingArea) 
            {
                Dispatcher.BeginInvoke(DispatcherPriority.Input, _continueProcessingStagingAreaCallback, null);
                _continueProcessingStagingArea = true;
            } 
        }
 
        private DispatcherOperationCallback _continueProcessingStagingAreaCallback; 
        private bool _continueProcessingStagingArea;
 
        private NotifyInputEventArgs _notifyInputEventArgs;
        private ProcessInputEventArgs _processInputEventArgs;
        private PreProcessInputEventArgs _preProcessInputEventArgs;
 
        //these four events introduced for secutiy purposes
        ///  
        ///     Critical: This code can be used to spoof input considered critical 
        /// 
        [method: SecurityCritical] 
        private event PreProcessInputEventHandler _preProcessInput;

        /// 
        ///     Critical: This code can be used to spoof input considered critical 
        /// 
        [method: SecurityCritical] 
        private event NotifyInputEventHandler _preNotifyInput; 

        ///  
        ///     Critical: This code can be used to spoof input considered critical
        /// 
        [method: SecurityCritical]
        private event NotifyInputEventHandler _postNotifyInput; 

        ///  
        ///     Critical: This code can be used to spoof input considered critical 
        /// 
        [method: SecurityCritical] 
        private event ProcessInputEventHandler _postProcessInput;

        /// 
        ///     Critical: This code can be used to spoof input considered critical 
        /// 
        [method: SecurityCritical] 
        private event KeyEventHandler _translateAccelerator; 

        ///  
        ///     This table holds critical data not ok to expose
        /// 
        [SecurityCritical]
        private Hashtable _inputProviders = new Hashtable(); 

        private KeyboardDevice _primaryKeyboardDevice; 
        private MouseDevice    _primaryMouseDevice; 
        private CommandDevice  _primaryCommandDevice;
 
        /// 
        ///     Critical to prevent accidental spread to transparent code
        /// 
        [SecurityCritical] 
        private StylusLogic     _stylusLogic;
 
        private bool            _inDragDrop; 

        private DispatcherOperationCallback _hitTestInvalidatedAsyncCallback; 
        private DispatcherOperation _hitTestInvalidatedAsyncOperation;
        private EventHandler _layoutUpdatedCallback;

        ///  
        ///     Critical: This stack holds all the input events and can be used
        ///     to spoof or force arbitrary input 
        ///  
        [SecurityCritical]
        private Stack _stagingArea; 

        private InputDevice _mostRecentInputDevice;

        // Timer used to synchronize the input devices periodically 
        private DispatcherTimer _inputTimer;
 
        // Synchronized input automation related fields 

        // Used to indicate whether any element is currently listening for synchronized input. 
        private static bool _isSynchronizedInput;

        // Element listening for synchronized input.
        private static DependencyObject _listeningElement; 

        // Input event the element is listening on. 
        private static RoutedEvent _synchronizedInputEvent; 

        // Input type the element is listening on. 
        private static SynchronizedInputType _synchronizedInputType;

        // Used to track state of synchronized input.
        private static SynchronizedInputStates _synchronizedInputState = SynchronizedInputStates.NoOpportunity; 

        // Lock used to serialize access to synchronized input related static fields. 
        private static object _synchronizedInputLock = new object(); 

    } 
}


// 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