Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / System / Windows / Input / MouseDevice.cs / 1 / MouseDevice.cs
using System.Diagnostics; using System.Collections; using System.Windows; using System.Windows.Media; using System.Windows.Interop; using System.Windows.Threading; using System.Security; using System.Security.Permissions; using MS.Internal; using MS.Internal.PresentationCore; // SecurityHelper using MS.Win32; // *NativeMethods using System.Runtime.InteropServices; using System; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; #pragma warning disable 1634, 1691 // suppressing PreSharp warnings // There's a choice of where to send MouseWheel events - to the element under // the mouse (like IE does) or to the element with keyboard focus (like Win32 // does). The latter choice lets you move the mouse away from the area you're // scrolling and still use the wheel. To get this effect, uncomment this line. //#define SEND_WHEEL_EVENTS_TO_FOCUS namespace System.Windows.Input { ////// The MouseDevice class represents the mouse device to the /// members of a context. /// public abstract class MouseDevice : InputDevice { ////// Critical - This is code that elevates AND creates the mouse device which /// happens to hold the callback to filter mouse messages /// TreatAsSafe: This constructor handles critical data but does not expose it /// It stores instance but there are demands on the instances. /// [SecurityCritical,SecurityTreatAsSafe] internal MouseDevice(InputManager inputManager) { _inputManager = new SecurityCriticalData(inputManager); _inputManager.Value.PreProcessInput += new PreProcessInputEventHandler(PreProcessInput); _inputManager.Value.PreNotifyInput += new NotifyInputEventHandler(PreNotifyInput); _inputManager.Value.PostProcessInput += new ProcessInputEventHandler(PostProcessInput); // Get information about how far two clicks of a double click can be considered // to be in the "same place and time". // // The call here goes into the safe helper calls, more of a consistency in approach // _doubleClickDeltaX = SafeSystemMetrics.DoubleClickDeltaX; _doubleClickDeltaY = SafeSystemMetrics.DoubleClickDeltaY; _doubleClickDeltaTime = SafeNativeMethods.GetDoubleClickTime(); _overIsEnabledChangedEventHandler = new DependencyPropertyChangedEventHandler(OnOverIsEnabledChanged); _overIsVisibleChangedEventHandler = new DependencyPropertyChangedEventHandler(OnOverIsVisibleChanged); _overIsHitTestVisibleChangedEventHandler = new DependencyPropertyChangedEventHandler(OnOverIsHitTestVisibleChanged); _reevaluateMouseOverDelegate = new DispatcherOperationCallback(ReevaluateMouseOverAsync); _reevaluateMouseOverOperation = null; _captureIsEnabledChangedEventHandler = new DependencyPropertyChangedEventHandler(OnCaptureIsEnabledChanged); _captureIsVisibleChangedEventHandler = new DependencyPropertyChangedEventHandler(OnCaptureIsVisibleChanged); _captureIsHitTestVisibleChangedEventHandler = new DependencyPropertyChangedEventHandler(OnCaptureIsHitTestVisibleChanged); _reevaluateCaptureDelegate = new DispatcherOperationCallback(ReevaluateCaptureAsync); _reevaluateCaptureOperation = null; _inputManager.Value.HitTestInvalidatedAsync += new EventHandler(OnHitTestInvalidatedAsync); } /// /// Gets the current state of the specified button from the device from either the underlying system or the StylusDevice /// /// /// The mouse button to get the state of /// ////// The state of the specified mouse button /// protected MouseButtonState GetButtonState(MouseButton mouseButton) { if ( _stylusDevice != null ) return _stylusDevice.GetMouseButtonState(mouseButton, this); else return GetButtonStateFromSystem(mouseButton); } ////// Gets the current position of the mouse in screen co-ords from either the underlying system or the StylusDevice /// ////// The current mouse location in screen co-ords /// protected Point GetScreenPosition() { if (_stylusDevice != null) return _stylusDevice.GetMouseScreenPosition(this); else return GetScreenPositionFromSystem(); } ////// Gets the current state of the specified button from the device from the underlying system /// /// /// The mouse button to get the state of /// ////// The state of the specified mouse button /// internal abstract MouseButtonState GetButtonStateFromSystem(MouseButton mouseButton); ////// Gets the current position of the mouse in screen co-ords from the underlying system /// ////// The current mouse location in screen co-ords /// ////// Critical: accesses critical data (_inputSource) /// TreatAsSafe: doesn't expose critical data, just returns screen coordinates of non-critical data /// [SecurityCritical, SecurityTreatAsSafe] internal Point GetScreenPositionFromSystem() { // Win32 has issues reliably returning where the mouse is. Until we figure // out a better way, just return the last mouse position in screen coordinates. Point ptScreen = new Point(0, 0); // Security Mitigation: do not give out input state if the device is not active. if (IsActive) { try { if (_inputSource != null && _inputSource.Value != null) { ptScreen = PointUtil.ClientToScreen(_lastPosition, _inputSource.Value); } } catch (System.ComponentModel.Win32Exception) { // The window could be shutting down, so just return (0,0). ptScreen = new Point(0, 0); } } return ptScreen; } ////// Gets the current position of the mouse in client co-ords of the current PresentationSource /// ////// The current mouse position in client co-ords /// ////// Critical: Accesses SecurityCritical data _inputSource /// TreatAsSafe: Only uses it to pass to through to overloaded version of this method which /// in turn only uses it to pass to PointUtil.ScreenToClient to convert mouse /// position from screen to client co-ords /// [SecurityCritical,SecurityTreatAsSafe] protected Point GetClientPosition() { Point ptClient = new Point(0, 0); try { if (_inputSource != null && _inputSource.Value != null) { ptClient = GetClientPosition(_inputSource.Value); } } catch (System.ComponentModel.Win32Exception) { // The window could be shutting down, so just return (0,0). ptClient = new Point(0, 0); } return ptClient; } ////// Gets the current position of the mouse in client co-ords of the specified PresentationSource /// ////// The current mouse position in client co-ords /// protected Point GetClientPosition(PresentationSource presentationSource) { Point ptScreen = GetScreenPosition(); Point ptClient = PointUtil.ScreenToClient(ptScreen, presentationSource); return ptClient; } ////// Returns the element that input from this device is sent to. /// public override IInputElement Target { get { // VerifyAccess(); // Return the element that the mouse is over. If the mouse // has been captured, the mouse will be considered "over" // the capture point if the mouse is outside of the // captured element (or subtree). return _mouseOver; } } ////// Returns the PresentationSource that is reporting input for this device. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical - accesses critical data ( _activeSource) /// PublicOK - there is a demand. /// public override PresentationSource ActiveSource { [SecurityCritical ] get { SecurityHelper.DemandUIWindowPermission(); if (_inputSource != null) { return _inputSource.Value; } return null; } } ////// Returns the PresentationSource that is reporting input for this device. /// ////// Critical - accesses critical data (_inputSource) and returns it. /// internal PresentationSource CriticalActiveSource { [SecurityCritical] get { if (_inputSource != null) { return _inputSource.Value; } return null; } } ////// Returns the element that the mouse is over. /// ////// The mouse is considered directly over an element if the mouse /// has been captured to that element. /// public IInputElement DirectlyOver { get { // VerifyAccess(); return _mouseOver; } } ////// Returns the element that the mouse is over regardless of /// its IsEnabled state. /// [FriendAccessAllowed] internal IInputElement RawDirectlyOver { get { if (_rawMouseOver != null) { IInputElement rawMouseOver = (IInputElement)_rawMouseOver.Target; if (rawMouseOver != null) { return rawMouseOver; } } return DirectlyOver; } } ////// Returns the element that has captured the mouse. /// public IInputElement Captured { get { // VerifyAccess(); return _mouseCapture; } } ////// Returns the element that has captured the mouse. /// internal CaptureMode CapturedMode { get { return _captureMode; } } ////// Captures the mouse to a particular element. /// public bool Capture(IInputElement element) { return Capture(element, CaptureMode.Element); } ////// Captures the mouse to a particular element. /// ////// Critical: This element acceses PresentationSource , MouseInputProvider (critical data) /// PublicOK: This operation is inherently safe and does not store or expose the critical data /// [SecurityCritical] public bool Capture(IInputElement element, CaptureMode captureMode) { int timeStamp = Environment.TickCount; // VerifyAccess(); if (!(captureMode == CaptureMode.None || captureMode == CaptureMode.Element || captureMode == CaptureMode.SubTree)) { throw new System.ComponentModel.InvalidEnumArgumentException("captureMode", (int)captureMode, typeof(CaptureMode)); } if (element == null) { captureMode = CaptureMode.None; } if (captureMode == CaptureMode.None) { element = null; } // Validate that elt is either a UIElement or a ContentElement DependencyObject eltDO = element as DependencyObject; if (eltDO != null && !InputElement.IsValid(element)) { throw new InvalidOperationException(SR.Get(SRID.Invalid_IInputElement, eltDO.GetType())); } bool success = false; // The element we are capturing to must be both enabled and visible. if (element is UIElement) { UIElement e = element as UIElement; #pragma warning suppress 6506 // e is obviously not null if(e.IsVisible && e.IsEnabled) { success = true; } } else if (element is ContentElement) { ContentElement ce = element as ContentElement; #pragma warning suppress 6506 // ce is obviosuly not null if(ce.IsEnabled) // There is no IsVisible property for ContentElement { success = true; } } else if (element is UIElement3D) { UIElement3D e = element as UIElement3D; #pragma warning suppress 6506 // e is obviously not null if(e.IsVisible && e.IsEnabled) { success = true; } } else { // Setting capture to null. success = true; } if(success) { success = false; // Find a mouse input provider that provides input for either // the new element (if taking capture) or the existing capture // element (if releasing capture). IMouseInputProvider mouseInputProvider = null; if (element != null) { DependencyObject containingVisual = InputElement.GetContainingVisual(eltDO); if (containingVisual != null) { PresentationSource captureSource = PresentationSource.CriticalFromVisual(containingVisual); if (captureSource != null) { mouseInputProvider = captureSource.GetInputProvider(typeof(MouseDevice)) as IMouseInputProvider; } } } else if (_mouseCapture != null) { mouseInputProvider = _providerCapture.Value; } // If we found a mouse input provider, ask it to either capture // or release the mouse for us. if(mouseInputProvider != null) { if (element != null) { success = mouseInputProvider.CaptureMouse(); if (success) { ChangeMouseCapture(element, mouseInputProvider, captureMode, timeStamp); } } else { mouseInputProvider.ReleaseMouseCapture(); // If we had capture, the input provider will release it. That will // cause a RawMouseAction.CancelCapture to be processed, which will // update our internal states. success = true; } } } return success; } // // Find an IMouseInputProvider on which the cursor can be set ////// Critical: This code accesses critical data (InputManager and InputProviders). /// It also returns critical data /// [SecurityCritical] private IMouseInputProvider FindMouseInputProviderForCursor( ) { // The shape of this API goes on the assumption that, like Win32, the cursor // is set for the whole desktop, not just a particular element or a particular // root visual. So instead of trying to find the IMouseInputProvider // that covers a particular element, we just find any IMouseInputProvider // and set the cursor on it. IMouseInputProvider mouseInputProvider = null; IEnumerator inputProviders = _inputManager.Value.UnsecureInputProviders.GetEnumerator(); while (inputProviders.MoveNext()) { IMouseInputProvider provider = inputProviders.Current as IMouseInputProvider; if (provider != null ) { mouseInputProvider = provider; break; } } return mouseInputProvider; } ////// The override cursor /// public Cursor OverrideCursor { get { // VerifyAccess(); return _overrideCursor; } set { // VerifyAccess(); _overrideCursor = value; UpdateCursorPrivate(); } } ////// Set the cursor /// /// The new cursor ///Note that this cursor doesn't apply any particular UIElement, it applies /// to the whole desktop. /// ////// Critical: Cause an elevation to unmanaged Code permission, also accesses critical data MouseInputProvider /// PublicOK: Calling SetCursor is a safe operation since it only affects current app.Also it does not /// expose critical data (IMouseInputProvider) /// [SecurityCritical] public bool SetCursor(Cursor cursor) { // VerifyAccess(); // Override the cursor if one is set. if (_overrideCursor != null) { cursor = _overrideCursor; } if (cursor == null) { cursor = Cursors.None; } // Get a mouse provider IMouseInputProvider mouseInputProvider = FindMouseInputProviderForCursor(); // If we found one, set the cursor if (mouseInputProvider != null) return mouseInputProvider.SetCursor(cursor); else return false; } ////// The state of the left button. /// public MouseButtonState LeftButton { get { return GetButtonState(MouseButton.Left); } } ////// The state of the right button. /// public MouseButtonState RightButton { get { return GetButtonState(MouseButton.Right); } } ////// The state of the middle button. /// public MouseButtonState MiddleButton { get { return GetButtonState(MouseButton.Middle); } } ////// The state of the first extended button. /// public MouseButtonState XButton1 { get { return GetButtonState(MouseButton.XButton1); } } ////// The state of the second extended button. /// public MouseButtonState XButton2 { get { return GetButtonState(MouseButton.XButton2); } } ////// Calculates the position of the mouse relative to /// a particular element. /// ////// Critical - accesses critical data _inputSource.Value /// PublicOK - we do the elevation of _inputSource to get RootVisual. /// [SecurityCritical ] public Point GetPosition(IInputElement relativeTo) { // VerifyAccess(); // Validate that relativeTo is either a UIElement or a ContentElement if (relativeTo != null && !InputElement.IsValid(relativeTo)) { throw new InvalidOperationException(SR.Get(SRID.Invalid_IInputElement, relativeTo.GetType())); } PresentationSource relativePresentationSource = null; if (relativeTo != null) { DependencyObject dependencyObject = relativeTo as DependencyObject; DependencyObject containingVisual = InputElement.GetContainingVisual(dependencyObject); if (containingVisual != null) { relativePresentationSource = PresentationSource.CriticalFromVisual(containingVisual); } } else { if (_inputSource != null) { relativePresentationSource = _inputSource.Value; } } // Verify that we have a valid PresentationSource with a valid RootVisual // - if we don't we won't be able to invoke ClientToRoot or TranslatePoint and // we will just return 0,0 if (relativePresentationSource == null || relativePresentationSource.RootVisual == null) { return new Point(0, 0); } Point ptClient; Point ptRoot; bool success; Point ptRelative; ptClient = GetClientPosition(relativePresentationSource); ptRoot = PointUtil.TryClientToRoot(ptClient, relativePresentationSource, false, out success); if (!success) { // ClientToRoot failed, usually because the client area is degenerate. // Just return 0,0 return new Point(0, 0); } ptRelative = InputElement.TranslatePoint(ptRoot, relativePresentationSource.RootVisual, (DependencyObject)relativeTo); return ptRelative; } ////// internal void ReevaluateMouseOver(DependencyObject element, DependencyObject oldParent, bool isCoreParent) { if (element != null) { if (isCoreParent) { MouseOverTreeState.SetCoreParent(element, oldParent); } else { MouseOverTreeState.SetLogicalParent(element, oldParent); } } // 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. // if (_reevaluateMouseOverOperation == null) { _reevaluateMouseOverOperation = Dispatcher.BeginInvoke(DispatcherPriority.Input, _reevaluateMouseOverDelegate, null); } } ////// /// ///private object ReevaluateMouseOverAsync(object arg) { _reevaluateMouseOverOperation = null; Synchronize(); // Refresh MouseOverProperty so that ReverseInherited Flags are updated. // // We only need to do this is there is any information about the old // tree state. This is because it is possible (even likely) that // Synchronize() would have already done this if we hit-tested to a // different element. if (_mouseOverTreeState != null && !_mouseOverTreeState.IsEmpty) { UIElement.MouseOverProperty.OnOriginValueChanged(_mouseOver as DependencyObject, _mouseOver as DependencyObject, ref _mouseOverTreeState); } return null; } /// /// internal void ReevaluateCapture(DependencyObject element, DependencyObject oldParent, bool isCoreParent) { if (element != null) { if (isCoreParent) { MouseCaptureWithinTreeState.SetCoreParent(element, oldParent); } else { MouseCaptureWithinTreeState.SetLogicalParent(element, oldParent); } } // We re-evaluate the captured element to be consistent with how // we re-evaluate the element the mouse is over. // // See ReevaluateMouseOver for details. // if (_reevaluateCaptureOperation == null) { _reevaluateCaptureOperation = Dispatcher.BeginInvoke(DispatcherPriority.Input, _reevaluateCaptureDelegate, null); } } ////// /// /// ///private object ReevaluateCaptureAsync(object arg) { _reevaluateCaptureOperation = null; if (_mouseCapture == null ) return null; bool killCapture = false; DependencyObject dependencyObject = _mouseCapture as DependencyObject; // // First, check things like IsEnabled, IsVisible, etc. on a // UIElement vs. ContentElement basis. // if (InputElement.IsUIElement(dependencyObject)) { killCapture = !ValidateUIElementForCapture((UIElement)_mouseCapture); } else if (InputElement.IsContentElement(dependencyObject)) { killCapture = !ValidateContentElementForCapture((ContentElement)_mouseCapture); } else if (InputElement.IsUIElement3D(dependencyObject)) { killCapture = !ValidateUIElement3DForCapture((UIElement3D)_mouseCapture); } // // Second, if we still haven't thought of a reason to kill capture, validate // it on a Visual basis for things like still being in the right tree. // if (killCapture == false) { DependencyObject containingVisual = InputElement.GetContainingVisual(dependencyObject); killCapture = !ValidateVisualForCapture(containingVisual); } // // Lastly, if we found any reason above, kill capture. // if (killCapture) { Capture(null); } // Refresh MouseCaptureWithinProperty so that ReverseInherited flags are updated. // // We only need to do this is there is any information about the old // tree state. This is because it is possible (even likely) that // we would have already killed capture if the capture criteria was // no longer met. if (_mouseCaptureWithinTreeState != null && !_mouseCaptureWithinTreeState.IsEmpty) { UIElement.MouseCaptureWithinProperty.OnOriginValueChanged(_mouseCapture as DependencyObject, _mouseCapture as DependencyObject, ref _mouseCaptureWithinTreeState); } return null; } private bool ValidateUIElementForCapture(UIElement element) { if (element.IsEnabled == false) return false; if (element.IsVisible == false) return false; if (element.IsHitTestVisible == false) return false; return true; } private bool ValidateUIElement3DForCapture(UIElement3D element) { if (element.IsEnabled == false) return false; if (element.IsVisible == false) return false; if (element.IsHitTestVisible == false) return false; return true; } private bool ValidateContentElementForCapture(ContentElement element) { if (element.IsEnabled == false) return false; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. return true; } /// /// Critical: This code accesses critical data(_activeSource) /// TreatAsSafe: Although it accesses critical data it does not modify or expose it, only compares against it. /// [SecurityCritical, SecurityTreatAsSafe] private bool ValidateVisualForCapture(DependencyObject visual) { if (visual == null) return false; PresentationSource presentationSource = PresentationSource.CriticalFromVisual(visual); if (presentationSource == null) return false; if (presentationSource != _inputSource.Value) return false; return true; } private void OnOverIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is over just became disabled. // // We need to resynchronize the mouse so that we can figure out who // the mouse is over now. ReevaluateMouseOver(null, null, true); } private void OnOverIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is over just became non-visible (collapsed or hidden). // // We need to resynchronize the mouse so that we can figure out who // the mouse is over now. ReevaluateMouseOver(null, null, true); } private void OnOverIsHitTestVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is over was affected by a change in hit-test visibility. // // We need to resynchronize the mouse so that we can figure out who // the mouse is over now. ReevaluateMouseOver(null, null, true); } private void OnCaptureIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is captured to just became disabled. // // We need to re-evaluate the element that has mouse capture since // we can't allow the mouse to remain captured by a disabled element. ReevaluateCapture(null, null, true); } private void OnCaptureIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is captured to just became non-visible (collapsed or hidden). // // We need to re-evaluate the element that has mouse capture since // we can't allow the mouse to remain captured by a non-visible element. ReevaluateCapture(null, null, true); } private void OnCaptureIsHitTestVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is captured to was affected by a change in hit-test visibility. // // We need to re-evaluate the element that has mouse capture since // we can't allow the mouse to remain captured by a non-hittest-visible element. ReevaluateCapture(null, null, true); } private void OnHitTestInvalidatedAsync(object sender, EventArgs e) { // The hit-test result may have changed. Synchronize(); } ////// Forces the mouse to resynchronize. /// ////// Critical - asserts HwndSource via _inputSource /// PublicOK: This code does not expose the HwndSource or store it, nor is /// this operation risky. /// [SecurityCritical] public void Synchronize() { // System.Console.WriteLine("Synchronize"); // VerifyAccess(); // Simulate a mouse move if (_inputSource != null && _inputSource.Value.CompositionTarget != null && !_inputSource.Value.CompositionTarget.IsDisposed) { int timeStamp = Environment.TickCount; Point ptClient = GetClientPosition(); RawMouseInputReport report = new RawMouseInputReport(InputMode.Foreground, timeStamp, _inputSource.Value, RawMouseActions.AbsoluteMove, (int) ptClient.X, (int) ptClient.Y, 0, IntPtr.Zero); report._isSynchronize = true; InputReportEventArgs inputReportEventArgs; if (_stylusDevice != null) { // if we have a current stylusdevice .. use it inputReportEventArgs = new InputReportEventArgs(_stylusDevice, report); } else { inputReportEventArgs = new InputReportEventArgs(this, report); } inputReportEventArgs.RoutedEvent=InputManager.PreviewInputReportEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(inputReportEventArgs); } } ////// Forces the mouse cursor to be updated. /// public void UpdateCursor() { // Call Forwarded UpdateCursorPrivate(); } ////// Forces the mouse cursor to be updated. /// ////// This method has been added just because changing the public /// API UpdateCursor will be a breaking change /// ////// Critical:This code access input manager and causes cursor to be updated /// TreatAsSafe: No risky critical data stored and inputs and outputs are safe. /// [SecurityCritical,SecurityTreatAsSafe] private bool UpdateCursorPrivate() { int timeStamp = Environment.TickCount; QueryCursorEventArgs queryCursor = new QueryCursorEventArgs(this, timeStamp); queryCursor.Cursor = Cursors.Arrow; queryCursor.RoutedEvent=Mouse.QueryCursorEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(queryCursor); return queryCursor.Handled; } ////// Critical: This code accesses link demanded method PresentationSource.AddSourcechangedhandler /// and remove for the same /// TreatAsSafe: This code does not expose the PresentationSource and simply changes the mouse over element /// [SecurityCritical,SecurityTreatAsSafe] private void ChangeMouseOver(IInputElement mouseOver, int timestamp) { DependencyObject o = null; if (_mouseOver != mouseOver) { // Console.WriteLine("ChangeMouseOver(" + mouseOver + ")"); // Update the critical piece of data. IInputElement oldMouseOver = _mouseOver; _mouseOver = mouseOver; using(Dispatcher.DisableProcessing()) // Disable reentrancy due to locks taken { // Adjust the handlers we use to track everything. if(oldMouseOver != null) { o = oldMouseOver as DependencyObject; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged -= _overIsEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged -= _overIsVisibleChangedEventHandler; ((UIElement)o).IsHitTestVisibleChanged -= _overIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged -= _overIsEnabledChangedEventHandler; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. // // ((ContentElement)o).IsVisibleChanged -= _overIsVisibleChangedEventHandler; // ((ContentElement)o).IsHitTestVisibleChanged -= _overIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsUIElement3D(o)) { ((UIElement3D)o).IsEnabledChanged -= _overIsEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged -= _overIsVisibleChangedEventHandler; ((UIElement3D)o).IsHitTestVisibleChanged -= _overIsHitTestVisibleChangedEventHandler; } } if(_mouseOver != null) { o = _mouseOver as DependencyObject; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged += _overIsEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged += _overIsVisibleChangedEventHandler; ((UIElement)o).IsHitTestVisibleChanged += _overIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged += _overIsEnabledChangedEventHandler; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. // // ((ContentElement)o).IsVisibleChanged += _overIsVisibleChangedEventHandler; // ((ContentElement)o).IsHitTestVisibleChanged += _overIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsUIElement3D(o)) { ((UIElement3D)o).IsEnabledChanged += _overIsEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged += _overIsVisibleChangedEventHandler; ((UIElement3D)o).IsHitTestVisibleChanged += _overIsHitTestVisibleChangedEventHandler; } } } // Oddly enough, update the IsMouseOver property first. This is // so any callbacks will see the more-common IsMouseOver property // set correctly. UIElement.MouseOverProperty.OnOriginValueChanged(oldMouseOver as DependencyObject, _mouseOver as DependencyObject, ref _mouseOverTreeState); // Invalidate the IsMouseDirectlyOver property. if (oldMouseOver != null) { o = oldMouseOver as DependencyObject; o.SetValue(UIElement.IsMouseDirectlyOverPropertyKey, false); // Same property for ContentElements } if (_mouseOver != null) { o = _mouseOver as DependencyObject; o.SetValue(UIElement.IsMouseDirectlyOverPropertyKey, true); // Same property for ContentElements } } } ////// Critical: This code acceses InputManager and MouseProvider which are critical data. /// It also calls into ProcessInput which is a critical method and could be used /// for input spoofing. /// TreatAsSafe: This operation is ok to expose since mouse capture is ok. Even if you get the /// source changed events you cannot get to the sources themselves /// [SecurityCritical,SecurityTreatAsSafe] private void ChangeMouseCapture(IInputElement mouseCapture, IMouseInputProvider providerCapture, CaptureMode captureMode, int timestamp) { DependencyObject o = null; if(mouseCapture != _mouseCapture) { // Console.WriteLine("ChangeMouseCapture(" + mouseCapture + ")"); // Update the critical pieces of data. IInputElement oldMouseCapture = _mouseCapture; _mouseCapture = mouseCapture; if (_mouseCapture != null) { _providerCapture = new SecurityCriticalDataClass(providerCapture); } else { _providerCapture = null; } _captureMode = captureMode; using (Dispatcher.DisableProcessing()) // Disable reentrancy due to locks taken { // Adjust the handlers we use to track everything. if (oldMouseCapture != null) { o = oldMouseCapture as DependencyObject; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged -= _captureIsEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged -= _captureIsVisibleChangedEventHandler; ((UIElement)o).IsHitTestVisibleChanged -= _captureIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged -= _captureIsEnabledChangedEventHandler; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. // // ((ContentElement)o).IsVisibleChanged -= _captureIsVisibleChangedEventHandler; // ((ContentElement)o).IsHitTestVisibleChanged -= _captureIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsUIElement3D(o)) { ((UIElement3D)o).IsEnabledChanged -= _captureIsEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged -= _captureIsVisibleChangedEventHandler; ((UIElement3D)o).IsHitTestVisibleChanged -= _captureIsHitTestVisibleChangedEventHandler; } } if (_mouseCapture != null) { o = _mouseCapture as DependencyObject; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged += _captureIsEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged += _captureIsVisibleChangedEventHandler; ((UIElement)o).IsHitTestVisibleChanged += _captureIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged += _captureIsEnabledChangedEventHandler; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. // // ((ContentElement)o).IsVisibleChanged += _captureIsVisibleChangedEventHandler; // ((ContentElement)o).IsHitTestVisibleChanged += _captureIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsUIElement3D(o)) { ((UIElement3D)o).IsEnabledChanged += _captureIsEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged += _captureIsVisibleChangedEventHandler; ((UIElement3D)o).IsHitTestVisibleChanged += _captureIsHitTestVisibleChangedEventHandler; } } } // Oddly enough, update the IsMouseCaptureWithin property first. This is // so any callbacks will see the more-common IsMouseCaptureWithin property // set correctly. UIElement.MouseCaptureWithinProperty.OnOriginValueChanged(oldMouseCapture as DependencyObject, _mouseCapture as DependencyObject, ref _mouseCaptureWithinTreeState); // Invalidate the IsMouseCaptured properties. if (oldMouseCapture != null) { o = oldMouseCapture as DependencyObject; o.SetValue(UIElement.IsMouseCapturedPropertyKey, false); // Same property for ContentElements } if (_mouseCapture != null) { o = _mouseCapture as DependencyObject; o.SetValue(UIElement.IsMouseCapturedPropertyKey, true); // Same property for ContentElements } // Send the LostMouseCapture and GotMouseCapture events. if (oldMouseCapture != null) { MouseEventArgs lostCapture = new MouseEventArgs(this, timestamp, _stylusDevice); lostCapture.RoutedEvent=Mouse.LostMouseCaptureEvent; lostCapture.Source= oldMouseCapture; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(lostCapture); } if (_mouseCapture != null) { MouseEventArgs gotCapture = new MouseEventArgs(this, timestamp, _stylusDevice); gotCapture.RoutedEvent=Mouse.GotMouseCaptureEvent; gotCapture.Source= _mouseCapture; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(gotCapture); } // Force a mouse move so we can update the mouse over. Synchronize(); } } /// /// Critical: This code acceses data that was got from an unsafe call (_doubleClickDeltaX,_doubleClickDeltaX) /// It also accesses HwndSource and InputManager and can be used for Input spoofing. /// accesses e.StagingItem.Input /// [SecurityCritical] private void PreProcessInput(object sender, PreProcessInputEventArgs e) { if (e.StagingItem.Input.RoutedEvent == InputManager.PreviewInputReportEvent) { InputReportEventArgs inputReportEventArgs = e.StagingItem.Input as InputReportEventArgs; if (!inputReportEventArgs.Handled && inputReportEventArgs.Report.Type == InputType.Mouse) { RawMouseInputReport rawMouseInputReport = (RawMouseInputReport)inputReportEventArgs.Report; // Normally we only process mouse input that is from our // active visual manager. The only exception to this is // the activate report, which is how we change the visual // manager that is active. if ((rawMouseInputReport.Actions & RawMouseActions.Activate) == RawMouseActions.Activate) { // Console.WriteLine("RawMouseActions.Activate"); // If other actions are being reported besides the // activate, separate them into different events. if ((rawMouseInputReport.Actions & ~RawMouseActions.Activate) != 0) { // Cancel this event. We'll push a new event for the activate. e.Cancel(); // Push a new RawMouseInputReport for the non-activate actions. RawMouseInputReport reportActions = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, rawMouseInputReport.Actions & ~RawMouseActions.Activate, rawMouseInputReport.X, rawMouseInputReport.Y, rawMouseInputReport.Wheel, rawMouseInputReport.ExtraInformation); InputReportEventArgs actionsArgs = new InputReportEventArgs(inputReportEventArgs.Device, reportActions); actionsArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(actionsArgs, null); // Create a new RawMouseInputReport for the activate. RawMouseInputReport reportActivate = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, RawMouseActions.Activate, rawMouseInputReport.X, rawMouseInputReport.Y, rawMouseInputReport.Wheel, rawMouseInputReport.ExtraInformation); // Push a new RawMouseInputReport for the activate. InputReportEventArgs activateArgs = new InputReportEventArgs(inputReportEventArgs.Device, reportActivate); activateArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(activateArgs, null); } } // Only process mouse input that is from our active PresentationSource. else if ((_inputSource != null) && (rawMouseInputReport.InputSource == _inputSource.Value)) { // We need to remember the StylusDevice that generated this input. Use the _tagStylusDevice // to store this in before we take over the inputReport Device and loose it. Any // input reports we re-push need to preserve this too. This is used to set the StylusDevice // property on MouseEventArgs. InputDevice inputDevice = e.StagingItem.GetData(_tagStylusDevice) as StylusDevice; if (inputDevice == null) { inputDevice = inputReportEventArgs.Device as StylusDevice; if (inputDevice != null) { e.StagingItem.SetData(_tagStylusDevice, inputDevice); } } // Claim the input for the mouse. inputReportEventArgs.Device = this; // If the input is reporting mouse deactivation, we need // to ensure that the element receives a final leave. // Note that activation could have been moved to another // visual manager in our app, which means that the leave // was already sent. So only do this if the deactivate // event is from the visual manager that we think is active. if ((rawMouseInputReport.Actions & RawMouseActions.Deactivate) == RawMouseActions.Deactivate) { if (_mouseOver != null) { // Push back this event, and cancel the current processing. e.PushInput(e.StagingItem); e.Cancel(); _isPhysicallyOver = false; ChangeMouseOver(null, e.StagingItem.Input.Timestamp); } } // If the input is reporting mouse movement, we need to check // if we need to update our sense of "mouse over". // if ((rawMouseInputReport.Actions & RawMouseActions.AbsoluteMove) == RawMouseActions.AbsoluteMove) { // If other actions are being reported besides the // move, separate them into different events. if ((rawMouseInputReport.Actions & ~(RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor)) != 0) { // Cancel this event. We'll push a new event for the move. e.Cancel(); // Push a new RawMouseInputReport for the non-move actions. RawMouseInputReport reportActions = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, rawMouseInputReport.Actions & ~(RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor), 0, 0, rawMouseInputReport.Wheel, rawMouseInputReport.ExtraInformation); InputReportEventArgs actionsArgs = new InputReportEventArgs(inputDevice, reportActions); actionsArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(actionsArgs, null); // Push a new RawMouseInputReport for the AbsoluteMove. RawMouseInputReport reportMove = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, rawMouseInputReport.Actions & (RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor), rawMouseInputReport.X, rawMouseInputReport.Y, 0, IntPtr.Zero); InputReportEventArgs moveArgs = new InputReportEventArgs(inputDevice, reportMove); moveArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(moveArgs, null); } else { // Convert the point from client coordinates into "root" coordinates. // We do this in the pre-process stage because it is possible that // this conversion will fail, in which case we want to cancel the // mouse move event. bool success = true; Point ptClient = new Point(rawMouseInputReport.X, rawMouseInputReport.Y); Point ptRoot = PointUtil.TryClientToRoot(ptClient, rawMouseInputReport.InputSource, false, out success); if(success) { e.StagingItem.SetData(_tagRootPoint, ptRoot); } else { e.Cancel(); } } } } } } else { // All mouse event processing should only happen if we still have an active input source. if (_inputSource != null) { if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseDownEvent) { MouseButtonEventArgs mouseButtonEventArgs = e.StagingItem.Input as MouseButtonEventArgs; if (_mouseCapture != null && !_isPhysicallyOver) { // The mouse is not physically over the capture point (or // subtree), so raise the PreviewMouseDownOutsideCapturedElement // event first. MouseButtonEventArgs clickThrough = new MouseButtonEventArgs(this, mouseButtonEventArgs.Timestamp, mouseButtonEventArgs.ChangedButton, GetStylusDevice(e.StagingItem)); clickThrough.RoutedEvent=Mouse.PreviewMouseDownOutsideCapturedElementEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(clickThrough); } } else if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseUpEvent) { MouseButtonEventArgs mouseButtonEventArgs = e.StagingItem.Input as MouseButtonEventArgs; if (_mouseCapture != null && !_isPhysicallyOver) { // The mouse is not physically over the capture point (or // subtree), so raise the PreviewMouseUpOutsideCapturedElement // event first. MouseButtonEventArgs clickThrough = new MouseButtonEventArgs(this, mouseButtonEventArgs.Timestamp, mouseButtonEventArgs.ChangedButton, GetStylusDevice(e.StagingItem)); clickThrough.RoutedEvent=Mouse.PreviewMouseUpOutsideCapturedElementEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(clickThrough); } } } } } ////// Critical: This code accesses data that was got off an unmanaged call (_doubleClickDeltaX,_doubleClickDeltaY) /// It also accesss Input Source and can be used for input spoofing /// accesses e.StagingItem.Input /// [SecurityCritical] private void PreNotifyInput(object sender, NotifyInputEventArgs e) { if ( e.StagingItem.Input.RoutedEvent == InputManager.PreviewInputReportEvent ) { InputReportEventArgs inputReportEventArgs = e.StagingItem.Input as InputReportEventArgs; if (!inputReportEventArgs.Handled && inputReportEventArgs.Report.Type == InputType.Mouse) { RawMouseInputReport rawMouseInputReport = (RawMouseInputReport) inputReportEventArgs.Report; // Generally, we need to check against redundant actions. // We never prevent the raw event from going through, but we // will only generate the high-level events for non-redundant // actions. We store the set of non-redundant actions in // the dictionary of this event. // Get the current Non-Redundant Actions for this event and // make a copy. We will compare the original value against the copy // at the end of this function and write it back in if changed. RawMouseActions actions = GetNonRedundantActions(e); RawMouseActions originalActions = actions; _stylusDevice = GetStylusDevice(e.StagingItem); // Normally we only process mouse input that is from our // active presentation source. The only exception to this is // the activate report, which is how we change the visual // manager that is active. if ((rawMouseInputReport.Actions & RawMouseActions.Activate) == RawMouseActions.Activate) { // System.Console.WriteLine("Initializing the mouse state."); actions |= RawMouseActions.Activate; _positionRelativeToOver.X = 0; _positionRelativeToOver.Y = 0; _lastPosition.X = rawMouseInputReport.X; _lastPosition.Y = rawMouseInputReport.Y; _forceUpdateLastPosition = true; _stylusDevice = inputReportEventArgs.Device as StylusDevice; // if the existing source is null, no need to do any special-case handling if (_inputSource == null) { _inputSource = new SecurityCriticalDataClass(rawMouseInputReport.InputSource); } // if the new source is the same as the old source, don't bother doing anything else if (_inputSource.Value != rawMouseInputReport.InputSource) { IMouseInputProvider toDeactivate = _inputSource.Value.GetInputProvider(typeof(MouseDevice)) as IMouseInputProvider; // All mouse information is now restricted to this presentation source. _inputSource = new SecurityCriticalDataClass (rawMouseInputReport.InputSource); if (toDeactivate != null) { toDeactivate.NotifyDeactivate(); } } } // Only process mouse input that is from our active presentation source. if ((_inputSource != null) && (rawMouseInputReport.InputSource == _inputSource.Value)) { // If the input is reporting mouse deactivation, we need // to break any capture we may have. Note that we only do // this if the presentation source associated with this event // is the same presentation source we are already over. if ((rawMouseInputReport.Actions & RawMouseActions.Deactivate) == RawMouseActions.Deactivate) { // Console.WriteLine("RawMouseActions.Deactivate"); Debug.Assert(_mouseOver == null, "_mouseOver should be null because we have called ChangeMouseOver(null) already."); _inputSource = null; ChangeMouseCapture(null, null, CaptureMode.None, e.StagingItem.Input.Timestamp); } if ((rawMouseInputReport.Actions & RawMouseActions.CancelCapture) == RawMouseActions.CancelCapture) { // Console.WriteLine("RawMouseActions.CancelCapture"); ChangeMouseCapture(null, null, CaptureMode.None, e.StagingItem.Input.Timestamp); } // If the input is reporting mouse movement, only update the // set of non-redundant actions if the position changed. if ((rawMouseInputReport.Actions & RawMouseActions.AbsoluteMove) == RawMouseActions.AbsoluteMove) // { //Console.WriteLine("RawMouseActions.AbsoluteMove: X=" + rawMouseInputReport.X + " Y=" + rawMouseInputReport.Y ); // Translate the mouse coordinates to both root relative and "mouseOver" relate. // - Note: "mouseOver" in this case is the element the mouse "was" over before this move. bool mouseOverAvailable = false; Point ptClient = new Point(rawMouseInputReport.X, rawMouseInputReport.Y); Point ptRoot = (Point) e.StagingItem.GetData(_tagRootPoint); Point ptRelativeToOver = InputElement.TranslatePoint(ptRoot, rawMouseInputReport.InputSource.RootVisual, (DependencyObject)_mouseOver, out mouseOverAvailable); IInputElement mouseOver = _mouseOver; // assume mouse is still over whatever it was before IInputElement rawMouseOver = (_rawMouseOver != null) ? (IInputElement)_rawMouseOver.Target : null; bool isPhysicallyOver = _isPhysicallyOver; bool isGlobalChange = ArePointsClose(ptClient, _lastPosition) == false; // determine if the mouse actually physically moved // Invoke Hit Test logic to determine what element the mouse will be over AFTER the move is processed. // - Only do this if: // - The mouse physcially moved (isGlobalChange) // - We are simulating a mouse move (_isSynchronize) // - mouseOver isn't availabe (!mouseOverAvailable) Could be caused by a degenerate transform. // - This is to mitigate the redundant AbsoluteMove notifications associated with QueryCursor if (isGlobalChange || rawMouseInputReport._isSynchronize || !mouseOverAvailable) { isPhysicallyOver = true; // assume mouse is physical over element, we'll set it false if it's due to capture switch (_captureMode) { // In this case there is no capture, so a simple hit test will determine which element becomes "mouseOver" case CaptureMode.None: { if (rawMouseInputReport._isSynchronize) { GlobalHitTest(ptClient, _inputSource.Value, out mouseOver, out rawMouseOver); } else { LocalHitTest(ptClient, _inputSource.Value, out mouseOver, out rawMouseOver); } if (mouseOver == rawMouseOver) { // Since they are the same, there is no reason to process rawMouseOver rawMouseOver = null; } // We understand UIElements and ContentElements. // If we are over something else (like a raw visual) // find the containing element. if (!InputElement.IsValid(mouseOver)) mouseOver = InputElement.GetContainingInputElement(mouseOver as DependencyObject); if ((rawMouseOver != null) && !InputElement.IsValid(rawMouseOver)) rawMouseOver = InputElement.GetContainingInputElement(rawMouseOver as DependencyObject); } break; // In this case, capture is to a specific element, so it will ALWAYS become "mouseOver" // - however, we do a hit test to see if the mouse is actually physically over the element, // - if it is not, we toggle isPhysicallyOver case CaptureMode.Element: if (rawMouseInputReport._isSynchronize) { mouseOver = GlobalHitTest(ptClient, _inputSource.Value); } else { mouseOver = LocalHitTest(ptClient, _inputSource.Value); } // There is no reason to process rawMouseOver when // the element should always be the one with mouse capture. rawMouseOver = null; if (mouseOver != _mouseCapture) { // Always consider the mouse over the capture point. mouseOver = _mouseCapture; isPhysicallyOver = false; } break; // In this case, capture is set to an entire subtree. We use simple hit testing to determine // which, if any element in the subtree it is over, and set "mouseOver to that element // If it is not over any specific subtree element, "mouseOver" is set to the root of the subtree. // - Note: a subtree can span multiple HWNDs case CaptureMode.SubTree: { IInputElement mouseCapture = InputElement.GetContainingInputElement(_mouseCapture as DependencyObject); if (mouseCapture != null) { // We need to re-hit-test to get the "real" UIElement we are over. // This allows us to have our capture-to-subtree span multiple windows. // GlobalHitTest always returns an IInputElement, so we are sure to have one. GlobalHitTest(ptClient, _inputSource.Value, out mouseOver, out rawMouseOver); } if (mouseOver != null && !InputElement.IsValid(mouseOver) ) mouseOver = InputElement.GetContainingInputElement(mouseOver as DependencyObject); // Make sure that the element we hit is acutally underneath // our captured element. Because we did a global hit test, we // could have hit an element in a completely different window. // // Note that we support the child being in a completely different window. // So we use the GetUIParent method instead of just looking at // visual/content parents. if (mouseOver != null) { IInputElement ieTest = mouseOver; UIElement eTest = null; ContentElement ceTest = null; UIElement3D e3DTest = null; while (ieTest != null && ieTest != mouseCapture) { eTest = ieTest as UIElement; if (eTest != null) { ieTest = InputElement.GetContainingInputElement(eTest.GetUIParent(true)); } else { ceTest = ieTest as ContentElement; if (ceTest != null) { ieTest = InputElement.GetContainingInputElement(ceTest.GetUIParent(true)); } else { e3DTest = ieTest as UIElement3D; // Should never fail. ieTest = InputElement.GetContainingInputElement(e3DTest.GetUIParent(true)); } } } // If we missed the capture point, we didn't hit anything. if (ieTest != mouseCapture) { mouseOver = _mouseCapture; isPhysicallyOver = false; // Since they are the same, there is no reason to process rawMouseOver rawMouseOver = null; } } else { // We didn't hit anything. Consider the mouse over the capture point. mouseOver = _mouseCapture; isPhysicallyOver = false; // Since they are the same, there is no reason to process rawMouseOver rawMouseOver = null; } if (rawMouseOver != null) { if (mouseOver == rawMouseOver) { // Since they are the same, there is no reason to process rawMouseOver rawMouseOver = null; } else if (!InputElement.IsValid(rawMouseOver)) { rawMouseOver = InputElement.GetContainingInputElement(rawMouseOver as DependencyObject); } } } break; } } _isPhysicallyOver = mouseOver == null ? false : isPhysicallyOver; // Now that we've determine what element the mouse is over now (mouseOver) // - we need to check if it's changed bool isMouseOverChange = mouseOver != _mouseOver; // If mouseOver changed, we need to recalculate the ptRelativeToOver, because "Over" changed! if (isMouseOverChange) { ptRelativeToOver = InputElement.TranslatePoint(ptRoot, rawMouseInputReport.InputSource.RootVisual, (DependencyObject)mouseOver); } //Console.WriteLine("RawMouseActions.AbsoluteMove: mouse moved over " + (isMouseOverChange ? "same" : "different") + " element. old=" + _mouseOver + " new=" + mouseOver); //Console.WriteLine("RawMouseActions.AbsoluteMove: capture=" + _mouseCapture); // Check to see if the local mouse position changed. This can be // caused by a change to the geometry of the // element we are over or a change in which element // we are over. // bool isLocalChange = isMouseOverChange || ArePointsClose(ptRelativeToOver, _positionRelativeToOver) == false; // Console.WriteLine("RawMouseActions.AbsoluteMove: isGlobalChange=" + isGlobalChange + " isLocalChange=" + isLocalChange); // We only update our cached position (_lastPosition & _positionRelativeToOver ) // if we have moved "far enough" allowing small incrementaly moves to accumulate if (isGlobalChange || isLocalChange || _forceUpdateLastPosition) { _forceUpdateLastPosition = false; _lastPosition = ptClient; _positionRelativeToOver = ptRelativeToOver; if (isMouseOverChange) { ChangeMouseOver(mouseOver, e.StagingItem.Input.Timestamp); } if ((_rawMouseOver == null) && (rawMouseOver != null)) { _rawMouseOver = new WeakReference(rawMouseOver); } else if (_rawMouseOver != null) { _rawMouseOver.Target = rawMouseOver; } // Console.WriteLine("RawMouseActions.AbsoluteMove: ptRoot=" + ptRoot); actions |= RawMouseActions.AbsoluteMove; // // In most cases the sequence of messages received from the system are HitTest, SetCursor & MouseMove. // The SetCursor message in this case will be traslated into an Avalon MouseMove & QueryCursor. // The MouseMove message to follow is redundant and is thrown away. // But imagine a case where Capture is taken. Here the system produces only two messages HitTest & MouseMove. // Hence we translate the MouseMove into an Avalon MouseMove & QueryCursor. // Logically MouseMove and QueryCursor go as a pair. actions |= RawMouseActions.QueryCursor; } } // Mouse wheel rotate events are never considered redundant. if ((rawMouseInputReport.Actions & RawMouseActions.VerticalWheelRotate) == RawMouseActions.VerticalWheelRotate) { // Console.WriteLine("RawMouseActions.VerticalWheelRotate"); actions |= RawMouseActions.VerticalWheelRotate; // Tell the InputManager that the MostRecentDevice is us. _inputManager.Value.MostRecentInputDevice = this; } // Mouse query cursor events are never considered redundant. if ((rawMouseInputReport.Actions & RawMouseActions.QueryCursor) == RawMouseActions.QueryCursor) { // Console.WriteLine("RawMouseActions.QueryCursor"); actions |= RawMouseActions.QueryCursor; } RawMouseActions[] ButtonPressActions = { RawMouseActions.Button1Press, RawMouseActions.Button2Press, RawMouseActions.Button3Press, RawMouseActions.Button4Press, RawMouseActions.Button5Press }; RawMouseActions[] ButtonReleaseActions = { RawMouseActions.Button1Release, RawMouseActions.Button2Release, RawMouseActions.Button3Release, RawMouseActions.Button4Release, RawMouseActions.Button5Release }; for (int iButton = 0; iButton < 5; iButton++) { if ((rawMouseInputReport.Actions & ButtonPressActions[iButton]) == ButtonPressActions[iButton]) { actions |= ButtonPressActions[iButton]; // Tell the InputManager that the MostRecentDevice is us. _inputManager.Value.MostRecentInputDevice = this; } if ((rawMouseInputReport.Actions & ButtonReleaseActions[iButton]) == ButtonReleaseActions[iButton]) { actions |= ButtonReleaseActions[iButton]; // Tell the InputManager that the MostRecentDevice is us. _inputManager.Value.MostRecentInputDevice = this; } } } if (actions != originalActions) { e.StagingItem.SetData(_tagNonRedundantActions, actions); } } } else { // All mouse event processing should only happen if we still have an active input source. if (_inputSource != null) { // During the PreviewMouseDown event, we update the click count, if there are // multiple "quick" clicks in approximately the "same" location (as defined // by the hosting environment, aka the registry). if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseDownEvent) { MouseButtonEventArgs mouseButtonArgs = e.StagingItem.Input as MouseButtonEventArgs; StylusDevice stylusDevice = GetStylusDevice(e.StagingItem); Point ptClient = GetClientPosition(); _clickCount = CalculateClickCount(mouseButtonArgs.ChangedButton, mouseButtonArgs.Timestamp, stylusDevice, ptClient); if (_clickCount == 1) { // we need to reset out data, since this is the start of the click count process... _lastClick = ptClient; _lastButton = mouseButtonArgs.ChangedButton; _lastClickTime = mouseButtonArgs.Timestamp; } // Put the updated count into the args. mouseButtonArgs.ClickCount = _clickCount; } } } } // Due to the inexactness of math calculations of // floating-point numbers, we use the AreClose method // to determine if the coordinates are close enough // to consider the same. private bool ArePointsClose(Point A, Point B) { return MS.Internal.DoubleUtil.AreClose(A.X, B.X) && MS.Internal.DoubleUtil.AreClose(A.Y, B.Y); } /// /// Critical: This code elevates permissions by calling unsafe native methods, also this /// function is not ok to expose publicly and hence the critical. /// accesses e.StagingItem.Input /// [SecurityCritical] private void PostProcessInput(object sender, ProcessInputEventArgs e) { // PreviewMouseWheel --> MouseWheel if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseWheelEvent) { if (!e.StagingItem.Input.Handled) { MouseWheelEventArgs previewWheel = (MouseWheelEventArgs) e.StagingItem.Input; MouseWheelEventArgs wheel = new MouseWheelEventArgs(this, previewWheel.Timestamp, previewWheel.Delta); wheel.RoutedEvent=Mouse.MouseWheelEvent; #if SEND_WHEEL_EVENTS_TO_FOCUS // wheel events are treated as if they came from the // element with keyboard focus wheel.Source = previewWheel.Source; #endif e.PushInput(wheel, e.StagingItem); } } // PreviewMouseDown --> MouseDown if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseDownEvent) { if (!e.StagingItem.Input.Handled) { MouseButtonEventArgs previewDown = (MouseButtonEventArgs) e.StagingItem.Input; MouseButtonEventArgs down = new MouseButtonEventArgs(this, previewDown.Timestamp, previewDown.ChangedButton, GetStylusDevice(e.StagingItem)); down.ClickCount = previewDown.ClickCount; down.RoutedEvent=Mouse.MouseDownEvent; e.PushInput(down, e.StagingItem); } } // PreviewMouseUp --> MouseUp if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseUpEvent) { if (!e.StagingItem.Input.Handled) { MouseButtonEventArgs previewUp = (MouseButtonEventArgs) e.StagingItem.Input; MouseButtonEventArgs up = new MouseButtonEventArgs(this, previewUp.Timestamp, previewUp.ChangedButton, GetStylusDevice(e.StagingItem)); up.RoutedEvent=Mouse.MouseUpEvent; e.PushInput(up, e.StagingItem); } } // PreviewMouseMove --> MouseMove if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseMoveEvent) { if (!e.StagingItem.Input.Handled) { MouseEventArgs previewMove = (MouseEventArgs) e.StagingItem.Input; MouseEventArgs move = new MouseEventArgs(this, previewMove.Timestamp, GetStylusDevice(e.StagingItem)); move.RoutedEvent=Mouse.MouseMoveEvent; e.PushInput(move, e.StagingItem); } } // We are finished processing the QueryCursor event. Just update the // mouse cursor to be what was decided during the event route. if (e.StagingItem.Input.RoutedEvent == Mouse.QueryCursorEvent) { QueryCursorEventArgs queryCursor = (QueryCursorEventArgs)e.StagingItem.Input; SetCursor(queryCursor.Cursor); } if (e.StagingItem.Input.RoutedEvent == InputManager.InputReportEvent) { InputReportEventArgs inputReportEventArgs = e.StagingItem.Input as InputReportEventArgs; if (!inputReportEventArgs.Handled && inputReportEventArgs.Report.Type == InputType.Mouse) { RawMouseInputReport rawMouseInputReport = (RawMouseInputReport) inputReportEventArgs.Report; // Only process mouse input that is from our active visual manager. if ((_inputSource != null) && (rawMouseInputReport.InputSource == _inputSource.Value)) { // In general, this is where we promote the non-redundant // reported actions to our premier events. RawMouseActions actions = GetNonRedundantActions(e); // Raw Activate --> Raw MouseMove // Whenever the mouse device is activated we need to // cause a mouse move so that elements realize that // the mouse is over them again. In most cases, the // action that caused the mouse to activate is a move, // but this is to guard against any other cases. if ((actions & RawMouseActions.Activate) == RawMouseActions.Activate) { Synchronize(); } // Raw --> PreviewMouseWheel if ((actions & RawMouseActions.VerticalWheelRotate) == RawMouseActions.VerticalWheelRotate) // { MouseWheelEventArgs previewWheel = new MouseWheelEventArgs(this, rawMouseInputReport.Timestamp, rawMouseInputReport.Wheel); previewWheel.RoutedEvent=Mouse.PreviewMouseWheelEvent; #if SEND_WHEEL_EVENTS_TO_FOCUS // wheel events are treated as if they came from the // element with keyboard focus DependencyObject focus = Keyboard.FocusedElement as DependencyObject; if (focus != null) { previewWheel.Source = focus; } #endif e.PushInput(previewWheel, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button1Press) == RawMouseActions.Button1Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Left, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button1Release) == RawMouseActions.Button1Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Left, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button2Press) == RawMouseActions.Button2Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Right, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button2Release) == RawMouseActions.Button2Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Right, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button3Press) == RawMouseActions.Button3Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Middle, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button3Release) == RawMouseActions.Button3Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Middle, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button4Press) == RawMouseActions.Button4Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.XButton1, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button4Release) == RawMouseActions.Button4Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.XButton1, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button5Press) == RawMouseActions.Button5Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.XButton2, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button5Release) == RawMouseActions.Button5Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.XButton2, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseMove if ((actions & RawMouseActions.AbsoluteMove) == RawMouseActions.AbsoluteMove) // { MouseEventArgs previewMove = new MouseEventArgs(this, rawMouseInputReport.Timestamp, GetStylusDevice(e.StagingItem)); previewMove.RoutedEvent=Mouse.PreviewMouseMoveEvent; e.PushInput(previewMove, e.StagingItem); } // Raw --> QueryCursor if ((actions & RawMouseActions.QueryCursor) == RawMouseActions.QueryCursor) { inputReportEventArgs.Handled = UpdateCursorPrivate(); } } } } } private RawMouseActions GetNonRedundantActions(NotifyInputEventArgs e) { RawMouseActions actions = new RawMouseActions(); // The CLR throws a null-ref exception if it tries to unbox a // null. So we have to special case that. object o = e.StagingItem.GetData(_tagNonRedundantActions); if (o != null) { actions = (RawMouseActions) o; } return actions; } internal static IInputElement GlobalHitTest(Point ptClient, PresentationSource inputSource) { IInputElement enabledHit; IInputElement originalHit; GlobalHitTest(ptClient, inputSource, out enabledHit, out originalHit); return enabledHit; } // Take a point relative the the specified visual manager, and translate // up to the screen, hit-test to a window, and then hit-test down to an // element. ////// Critical: This code accesses PresentationSource and returns element which is hittest /// TreatAsSafe: This function is safe to expose. Only the element being clicked on is returned. /// [SecurityCritical,SecurityTreatAsSafe] private static void GlobalHitTest(Point ptClient, PresentationSource inputSource, out IInputElement enabledHit, out IInputElement originalHit) { enabledHit = originalHit = null; // Note: this only works for HWNDs for now. HwndSource source = inputSource as HwndSource; if (source != null && source.CompositionTarget != null && !source.IsHandleNull) { Point ptScreen = PointUtil.ClientToScreen(ptClient, source); IntPtr hwndHit = IntPtr.Zero ; HwndSource sourceHit = null ; // Hit-test for a window. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert: try { // Find the HWND under the point. hwndHit = UnsafeNativeMethods.WindowFromPoint((int)ptScreen.X, (int)ptScreen.Y); // Make sure the window is enabled! if (!SafeNativeMethods.IsWindowEnabled(new HandleRef(null, hwndHit))) { hwndHit = IntPtr.Zero; } } finally { CodeAccessPermission.RevertAssert(); } if (hwndHit != IntPtr.Zero) { // See if this is one of our windows. sourceHit = HwndSource.CriticalFromHwnd(hwndHit); } if (sourceHit != null && sourceHit.Dispatcher == inputSource.CompositionTarget.Dispatcher) { Point ptClientHit = PointUtil.ScreenToClient(ptScreen, sourceHit); // Perform a local hit-test within this visual manager. LocalHitTest(ptClientHit, sourceHit, out enabledHit, out originalHit); } } } internal static IInputElement LocalHitTest(Point ptClient, PresentationSource inputSource) { IInputElement enabledHit; IInputElement originalHit; LocalHitTest(ptClient, inputSource, out enabledHit, out originalHit); return enabledHit; } // Take a point relative the the specified visual manager and hit-test // down to an element. ////// Critical: This code accesses PresentationSource and returns element which is hittest /// TreatAsSafe: This function is safe to expose. Only the element being clicked on is returned. /// [SecurityCritical,SecurityTreatAsSafe] private static void LocalHitTest(Point ptClient, PresentationSource inputSource, out IInputElement enabledHit, out IInputElement originalHit) { enabledHit = originalHit = null; // Hit-test starting from the root UIElement. // Note: this restricts us to windows with UIElement as the root (not just visuals). if (inputSource != null) { UIElement root = inputSource.RootVisual as UIElement; if(root != null) { Point pt = PointUtil.ClientToRoot(ptClient, inputSource); root.InputHitTest(pt, out enabledHit, out originalHit); } } } internal bool IsSameSpot(Point newPosition, StylusDevice stylusDevice) { int doubleClickDeltaX = (stylusDevice != null)?stylusDevice.DoubleTapDeltaX:_doubleClickDeltaX; int doubleClickDeltaY = (stylusDevice != null)?stylusDevice.DoubleTapDeltaY:_doubleClickDeltaY; // Is the delta coordinates of this click close enough to the last click? return (Math.Abs(newPosition.X - _lastClick.X) < doubleClickDeltaX) && (Math.Abs(newPosition.Y - _lastClick.Y) < doubleClickDeltaY); } internal int CalculateClickCount(MouseButton button, int timeStamp, StylusDevice stylusDevice, Point downPt) { // How long since the last click? int timeSpan = timeStamp - _lastClickTime; int doubleClickDeltaTime = (stylusDevice != null)?stylusDevice.DoubleTapDeltaTime:_doubleClickDeltaTime; // Is the delta coordinates of this click close enough to the last click? bool isSameSpot = IsSameSpot(downPt, stylusDevice); // Is this the same mouse button as the last click? bool isSameButton = (_lastButton == button); // Now check everything to see if this is a multi-click. if (timeSpan < doubleClickDeltaTime && isSameSpot && isSameButton) { // Yes, increment the count return _clickCount +1; } else { // No, not a multi-click. return 1; } } internal Point PositionRelativeToOver { get { return _positionRelativeToOver; } } internal Point NonRelativePosition { get { return _lastPosition; } } ////// Critical: accesses critical data (_inputSource) /// TreatAsSafe: doesn't expose critical data, just returns true/false. /// internal bool IsActive { [SecurityCritical, SecurityTreatAsSafe] get { return _inputSource != null && _inputSource.Value != null; } } // Helper to access the StylusDevice property. private StylusDevice GetStylusDevice(StagingAreaInputItem stagingItem) { return stagingItem.GetData(_tagStylusDevice) as StylusDevice; } internal StylusDevice StylusDevice { get { return _stylusDevice; } } private DeferredElementTreeState MouseOverTreeState { get { if (_mouseOverTreeState == null) { _mouseOverTreeState = new DeferredElementTreeState(); } return _mouseOverTreeState; } } private DeferredElementTreeState MouseCaptureWithinTreeState { get { if (_mouseCaptureWithinTreeState == null) { _mouseCaptureWithinTreeState = new DeferredElementTreeState(); } return _mouseCaptureWithinTreeState; } } ////// This data is not safe to expose as it holds refrence to PresentationSource /// private SecurityCriticalDataClass_inputSource; /// /// This data is not safe to expose as it holds refrence to PresentationSource /// private SecurityCriticalData_inputManager; private IInputElement _mouseOver; private DeferredElementTreeState _mouseOverTreeState; private bool _isPhysicallyOver; private WeakReference _rawMouseOver; private IInputElement _mouseCapture; private DeferredElementTreeState _mouseCaptureWithinTreeState; private SecurityCriticalDataClass _providerCapture; private CaptureMode _captureMode; private DependencyPropertyChangedEventHandler _overIsEnabledChangedEventHandler; private DependencyPropertyChangedEventHandler _overIsVisibleChangedEventHandler; private DependencyPropertyChangedEventHandler _overIsHitTestVisibleChangedEventHandler; private DispatcherOperationCallback _reevaluateMouseOverDelegate; private DispatcherOperation _reevaluateMouseOverOperation; private DependencyPropertyChangedEventHandler _captureIsEnabledChangedEventHandler; private DependencyPropertyChangedEventHandler _captureIsVisibleChangedEventHandler; private DependencyPropertyChangedEventHandler _captureIsHitTestVisibleChangedEventHandler; private DispatcherOperationCallback _reevaluateCaptureDelegate; private DispatcherOperation _reevaluateCaptureOperation; // Device state we track private Point _positionRelativeToOver = new Point(); private Point _lastPosition = new Point(); private bool _forceUpdateLastPosition = false; // Data tags for information we pass around the staging area. private object _tagNonRedundantActions = new object(); private object _tagStylusDevice = new object(); private object _tagRootPoint = new object(); // Information used to distinguish double-clicks (actually, multi clicks) from // multiple independent clicks. private Point _lastClick = new Point(); private MouseButton _lastButton; private int _clickCount; private int _lastClickTime; private int _doubleClickDeltaTime; private int _doubleClickDeltaX; private int _doubleClickDeltaY; private Cursor _overrideCursor; // Reference to StylusDevice to defer to for physical mouse state (position/button state) private StylusDevice _stylusDevice = null; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System.Diagnostics; using System.Collections; using System.Windows; using System.Windows.Media; using System.Windows.Interop; using System.Windows.Threading; using System.Security; using System.Security.Permissions; using MS.Internal; using MS.Internal.PresentationCore; // SecurityHelper using MS.Win32; // *NativeMethods using System.Runtime.InteropServices; using System; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; #pragma warning disable 1634, 1691 // suppressing PreSharp warnings // There's a choice of where to send MouseWheel events - to the element under // the mouse (like IE does) or to the element with keyboard focus (like Win32 // does). The latter choice lets you move the mouse away from the area you're // scrolling and still use the wheel. To get this effect, uncomment this line. //#define SEND_WHEEL_EVENTS_TO_FOCUS namespace System.Windows.Input { /// /// The MouseDevice class represents the mouse device to the /// members of a context. /// public abstract class MouseDevice : InputDevice { ////// Critical - This is code that elevates AND creates the mouse device which /// happens to hold the callback to filter mouse messages /// TreatAsSafe: This constructor handles critical data but does not expose it /// It stores instance but there are demands on the instances. /// [SecurityCritical,SecurityTreatAsSafe] internal MouseDevice(InputManager inputManager) { _inputManager = new SecurityCriticalData(inputManager); _inputManager.Value.PreProcessInput += new PreProcessInputEventHandler(PreProcessInput); _inputManager.Value.PreNotifyInput += new NotifyInputEventHandler(PreNotifyInput); _inputManager.Value.PostProcessInput += new ProcessInputEventHandler(PostProcessInput); // Get information about how far two clicks of a double click can be considered // to be in the "same place and time". // // The call here goes into the safe helper calls, more of a consistency in approach // _doubleClickDeltaX = SafeSystemMetrics.DoubleClickDeltaX; _doubleClickDeltaY = SafeSystemMetrics.DoubleClickDeltaY; _doubleClickDeltaTime = SafeNativeMethods.GetDoubleClickTime(); _overIsEnabledChangedEventHandler = new DependencyPropertyChangedEventHandler(OnOverIsEnabledChanged); _overIsVisibleChangedEventHandler = new DependencyPropertyChangedEventHandler(OnOverIsVisibleChanged); _overIsHitTestVisibleChangedEventHandler = new DependencyPropertyChangedEventHandler(OnOverIsHitTestVisibleChanged); _reevaluateMouseOverDelegate = new DispatcherOperationCallback(ReevaluateMouseOverAsync); _reevaluateMouseOverOperation = null; _captureIsEnabledChangedEventHandler = new DependencyPropertyChangedEventHandler(OnCaptureIsEnabledChanged); _captureIsVisibleChangedEventHandler = new DependencyPropertyChangedEventHandler(OnCaptureIsVisibleChanged); _captureIsHitTestVisibleChangedEventHandler = new DependencyPropertyChangedEventHandler(OnCaptureIsHitTestVisibleChanged); _reevaluateCaptureDelegate = new DispatcherOperationCallback(ReevaluateCaptureAsync); _reevaluateCaptureOperation = null; _inputManager.Value.HitTestInvalidatedAsync += new EventHandler(OnHitTestInvalidatedAsync); } /// /// Gets the current state of the specified button from the device from either the underlying system or the StylusDevice /// /// /// The mouse button to get the state of /// ////// The state of the specified mouse button /// protected MouseButtonState GetButtonState(MouseButton mouseButton) { if ( _stylusDevice != null ) return _stylusDevice.GetMouseButtonState(mouseButton, this); else return GetButtonStateFromSystem(mouseButton); } ////// Gets the current position of the mouse in screen co-ords from either the underlying system or the StylusDevice /// ////// The current mouse location in screen co-ords /// protected Point GetScreenPosition() { if (_stylusDevice != null) return _stylusDevice.GetMouseScreenPosition(this); else return GetScreenPositionFromSystem(); } ////// Gets the current state of the specified button from the device from the underlying system /// /// /// The mouse button to get the state of /// ////// The state of the specified mouse button /// internal abstract MouseButtonState GetButtonStateFromSystem(MouseButton mouseButton); ////// Gets the current position of the mouse in screen co-ords from the underlying system /// ////// The current mouse location in screen co-ords /// ////// Critical: accesses critical data (_inputSource) /// TreatAsSafe: doesn't expose critical data, just returns screen coordinates of non-critical data /// [SecurityCritical, SecurityTreatAsSafe] internal Point GetScreenPositionFromSystem() { // Win32 has issues reliably returning where the mouse is. Until we figure // out a better way, just return the last mouse position in screen coordinates. Point ptScreen = new Point(0, 0); // Security Mitigation: do not give out input state if the device is not active. if (IsActive) { try { if (_inputSource != null && _inputSource.Value != null) { ptScreen = PointUtil.ClientToScreen(_lastPosition, _inputSource.Value); } } catch (System.ComponentModel.Win32Exception) { // The window could be shutting down, so just return (0,0). ptScreen = new Point(0, 0); } } return ptScreen; } ////// Gets the current position of the mouse in client co-ords of the current PresentationSource /// ////// The current mouse position in client co-ords /// ////// Critical: Accesses SecurityCritical data _inputSource /// TreatAsSafe: Only uses it to pass to through to overloaded version of this method which /// in turn only uses it to pass to PointUtil.ScreenToClient to convert mouse /// position from screen to client co-ords /// [SecurityCritical,SecurityTreatAsSafe] protected Point GetClientPosition() { Point ptClient = new Point(0, 0); try { if (_inputSource != null && _inputSource.Value != null) { ptClient = GetClientPosition(_inputSource.Value); } } catch (System.ComponentModel.Win32Exception) { // The window could be shutting down, so just return (0,0). ptClient = new Point(0, 0); } return ptClient; } ////// Gets the current position of the mouse in client co-ords of the specified PresentationSource /// ////// The current mouse position in client co-ords /// protected Point GetClientPosition(PresentationSource presentationSource) { Point ptScreen = GetScreenPosition(); Point ptClient = PointUtil.ScreenToClient(ptScreen, presentationSource); return ptClient; } ////// Returns the element that input from this device is sent to. /// public override IInputElement Target { get { // VerifyAccess(); // Return the element that the mouse is over. If the mouse // has been captured, the mouse will be considered "over" // the capture point if the mouse is outside of the // captured element (or subtree). return _mouseOver; } } ////// Returns the PresentationSource that is reporting input for this device. /// ////// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API. /// ////// Critical - accesses critical data ( _activeSource) /// PublicOK - there is a demand. /// public override PresentationSource ActiveSource { [SecurityCritical ] get { SecurityHelper.DemandUIWindowPermission(); if (_inputSource != null) { return _inputSource.Value; } return null; } } ////// Returns the PresentationSource that is reporting input for this device. /// ////// Critical - accesses critical data (_inputSource) and returns it. /// internal PresentationSource CriticalActiveSource { [SecurityCritical] get { if (_inputSource != null) { return _inputSource.Value; } return null; } } ////// Returns the element that the mouse is over. /// ////// The mouse is considered directly over an element if the mouse /// has been captured to that element. /// public IInputElement DirectlyOver { get { // VerifyAccess(); return _mouseOver; } } ////// Returns the element that the mouse is over regardless of /// its IsEnabled state. /// [FriendAccessAllowed] internal IInputElement RawDirectlyOver { get { if (_rawMouseOver != null) { IInputElement rawMouseOver = (IInputElement)_rawMouseOver.Target; if (rawMouseOver != null) { return rawMouseOver; } } return DirectlyOver; } } ////// Returns the element that has captured the mouse. /// public IInputElement Captured { get { // VerifyAccess(); return _mouseCapture; } } ////// Returns the element that has captured the mouse. /// internal CaptureMode CapturedMode { get { return _captureMode; } } ////// Captures the mouse to a particular element. /// public bool Capture(IInputElement element) { return Capture(element, CaptureMode.Element); } ////// Captures the mouse to a particular element. /// ////// Critical: This element acceses PresentationSource , MouseInputProvider (critical data) /// PublicOK: This operation is inherently safe and does not store or expose the critical data /// [SecurityCritical] public bool Capture(IInputElement element, CaptureMode captureMode) { int timeStamp = Environment.TickCount; // VerifyAccess(); if (!(captureMode == CaptureMode.None || captureMode == CaptureMode.Element || captureMode == CaptureMode.SubTree)) { throw new System.ComponentModel.InvalidEnumArgumentException("captureMode", (int)captureMode, typeof(CaptureMode)); } if (element == null) { captureMode = CaptureMode.None; } if (captureMode == CaptureMode.None) { element = null; } // Validate that elt is either a UIElement or a ContentElement DependencyObject eltDO = element as DependencyObject; if (eltDO != null && !InputElement.IsValid(element)) { throw new InvalidOperationException(SR.Get(SRID.Invalid_IInputElement, eltDO.GetType())); } bool success = false; // The element we are capturing to must be both enabled and visible. if (element is UIElement) { UIElement e = element as UIElement; #pragma warning suppress 6506 // e is obviously not null if(e.IsVisible && e.IsEnabled) { success = true; } } else if (element is ContentElement) { ContentElement ce = element as ContentElement; #pragma warning suppress 6506 // ce is obviosuly not null if(ce.IsEnabled) // There is no IsVisible property for ContentElement { success = true; } } else if (element is UIElement3D) { UIElement3D e = element as UIElement3D; #pragma warning suppress 6506 // e is obviously not null if(e.IsVisible && e.IsEnabled) { success = true; } } else { // Setting capture to null. success = true; } if(success) { success = false; // Find a mouse input provider that provides input for either // the new element (if taking capture) or the existing capture // element (if releasing capture). IMouseInputProvider mouseInputProvider = null; if (element != null) { DependencyObject containingVisual = InputElement.GetContainingVisual(eltDO); if (containingVisual != null) { PresentationSource captureSource = PresentationSource.CriticalFromVisual(containingVisual); if (captureSource != null) { mouseInputProvider = captureSource.GetInputProvider(typeof(MouseDevice)) as IMouseInputProvider; } } } else if (_mouseCapture != null) { mouseInputProvider = _providerCapture.Value; } // If we found a mouse input provider, ask it to either capture // or release the mouse for us. if(mouseInputProvider != null) { if (element != null) { success = mouseInputProvider.CaptureMouse(); if (success) { ChangeMouseCapture(element, mouseInputProvider, captureMode, timeStamp); } } else { mouseInputProvider.ReleaseMouseCapture(); // If we had capture, the input provider will release it. That will // cause a RawMouseAction.CancelCapture to be processed, which will // update our internal states. success = true; } } } return success; } // // Find an IMouseInputProvider on which the cursor can be set ////// Critical: This code accesses critical data (InputManager and InputProviders). /// It also returns critical data /// [SecurityCritical] private IMouseInputProvider FindMouseInputProviderForCursor( ) { // The shape of this API goes on the assumption that, like Win32, the cursor // is set for the whole desktop, not just a particular element or a particular // root visual. So instead of trying to find the IMouseInputProvider // that covers a particular element, we just find any IMouseInputProvider // and set the cursor on it. IMouseInputProvider mouseInputProvider = null; IEnumerator inputProviders = _inputManager.Value.UnsecureInputProviders.GetEnumerator(); while (inputProviders.MoveNext()) { IMouseInputProvider provider = inputProviders.Current as IMouseInputProvider; if (provider != null ) { mouseInputProvider = provider; break; } } return mouseInputProvider; } ////// The override cursor /// public Cursor OverrideCursor { get { // VerifyAccess(); return _overrideCursor; } set { // VerifyAccess(); _overrideCursor = value; UpdateCursorPrivate(); } } ////// Set the cursor /// /// The new cursor ///Note that this cursor doesn't apply any particular UIElement, it applies /// to the whole desktop. /// ////// Critical: Cause an elevation to unmanaged Code permission, also accesses critical data MouseInputProvider /// PublicOK: Calling SetCursor is a safe operation since it only affects current app.Also it does not /// expose critical data (IMouseInputProvider) /// [SecurityCritical] public bool SetCursor(Cursor cursor) { // VerifyAccess(); // Override the cursor if one is set. if (_overrideCursor != null) { cursor = _overrideCursor; } if (cursor == null) { cursor = Cursors.None; } // Get a mouse provider IMouseInputProvider mouseInputProvider = FindMouseInputProviderForCursor(); // If we found one, set the cursor if (mouseInputProvider != null) return mouseInputProvider.SetCursor(cursor); else return false; } ////// The state of the left button. /// public MouseButtonState LeftButton { get { return GetButtonState(MouseButton.Left); } } ////// The state of the right button. /// public MouseButtonState RightButton { get { return GetButtonState(MouseButton.Right); } } ////// The state of the middle button. /// public MouseButtonState MiddleButton { get { return GetButtonState(MouseButton.Middle); } } ////// The state of the first extended button. /// public MouseButtonState XButton1 { get { return GetButtonState(MouseButton.XButton1); } } ////// The state of the second extended button. /// public MouseButtonState XButton2 { get { return GetButtonState(MouseButton.XButton2); } } ////// Calculates the position of the mouse relative to /// a particular element. /// ////// Critical - accesses critical data _inputSource.Value /// PublicOK - we do the elevation of _inputSource to get RootVisual. /// [SecurityCritical ] public Point GetPosition(IInputElement relativeTo) { // VerifyAccess(); // Validate that relativeTo is either a UIElement or a ContentElement if (relativeTo != null && !InputElement.IsValid(relativeTo)) { throw new InvalidOperationException(SR.Get(SRID.Invalid_IInputElement, relativeTo.GetType())); } PresentationSource relativePresentationSource = null; if (relativeTo != null) { DependencyObject dependencyObject = relativeTo as DependencyObject; DependencyObject containingVisual = InputElement.GetContainingVisual(dependencyObject); if (containingVisual != null) { relativePresentationSource = PresentationSource.CriticalFromVisual(containingVisual); } } else { if (_inputSource != null) { relativePresentationSource = _inputSource.Value; } } // Verify that we have a valid PresentationSource with a valid RootVisual // - if we don't we won't be able to invoke ClientToRoot or TranslatePoint and // we will just return 0,0 if (relativePresentationSource == null || relativePresentationSource.RootVisual == null) { return new Point(0, 0); } Point ptClient; Point ptRoot; bool success; Point ptRelative; ptClient = GetClientPosition(relativePresentationSource); ptRoot = PointUtil.TryClientToRoot(ptClient, relativePresentationSource, false, out success); if (!success) { // ClientToRoot failed, usually because the client area is degenerate. // Just return 0,0 return new Point(0, 0); } ptRelative = InputElement.TranslatePoint(ptRoot, relativePresentationSource.RootVisual, (DependencyObject)relativeTo); return ptRelative; } ////// internal void ReevaluateMouseOver(DependencyObject element, DependencyObject oldParent, bool isCoreParent) { if (element != null) { if (isCoreParent) { MouseOverTreeState.SetCoreParent(element, oldParent); } else { MouseOverTreeState.SetLogicalParent(element, oldParent); } } // 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. // if (_reevaluateMouseOverOperation == null) { _reevaluateMouseOverOperation = Dispatcher.BeginInvoke(DispatcherPriority.Input, _reevaluateMouseOverDelegate, null); } } ////// /// ///private object ReevaluateMouseOverAsync(object arg) { _reevaluateMouseOverOperation = null; Synchronize(); // Refresh MouseOverProperty so that ReverseInherited Flags are updated. // // We only need to do this is there is any information about the old // tree state. This is because it is possible (even likely) that // Synchronize() would have already done this if we hit-tested to a // different element. if (_mouseOverTreeState != null && !_mouseOverTreeState.IsEmpty) { UIElement.MouseOverProperty.OnOriginValueChanged(_mouseOver as DependencyObject, _mouseOver as DependencyObject, ref _mouseOverTreeState); } return null; } /// /// internal void ReevaluateCapture(DependencyObject element, DependencyObject oldParent, bool isCoreParent) { if (element != null) { if (isCoreParent) { MouseCaptureWithinTreeState.SetCoreParent(element, oldParent); } else { MouseCaptureWithinTreeState.SetLogicalParent(element, oldParent); } } // We re-evaluate the captured element to be consistent with how // we re-evaluate the element the mouse is over. // // See ReevaluateMouseOver for details. // if (_reevaluateCaptureOperation == null) { _reevaluateCaptureOperation = Dispatcher.BeginInvoke(DispatcherPriority.Input, _reevaluateCaptureDelegate, null); } } ////// /// /// ///private object ReevaluateCaptureAsync(object arg) { _reevaluateCaptureOperation = null; if (_mouseCapture == null ) return null; bool killCapture = false; DependencyObject dependencyObject = _mouseCapture as DependencyObject; // // First, check things like IsEnabled, IsVisible, etc. on a // UIElement vs. ContentElement basis. // if (InputElement.IsUIElement(dependencyObject)) { killCapture = !ValidateUIElementForCapture((UIElement)_mouseCapture); } else if (InputElement.IsContentElement(dependencyObject)) { killCapture = !ValidateContentElementForCapture((ContentElement)_mouseCapture); } else if (InputElement.IsUIElement3D(dependencyObject)) { killCapture = !ValidateUIElement3DForCapture((UIElement3D)_mouseCapture); } // // Second, if we still haven't thought of a reason to kill capture, validate // it on a Visual basis for things like still being in the right tree. // if (killCapture == false) { DependencyObject containingVisual = InputElement.GetContainingVisual(dependencyObject); killCapture = !ValidateVisualForCapture(containingVisual); } // // Lastly, if we found any reason above, kill capture. // if (killCapture) { Capture(null); } // Refresh MouseCaptureWithinProperty so that ReverseInherited flags are updated. // // We only need to do this is there is any information about the old // tree state. This is because it is possible (even likely) that // we would have already killed capture if the capture criteria was // no longer met. if (_mouseCaptureWithinTreeState != null && !_mouseCaptureWithinTreeState.IsEmpty) { UIElement.MouseCaptureWithinProperty.OnOriginValueChanged(_mouseCapture as DependencyObject, _mouseCapture as DependencyObject, ref _mouseCaptureWithinTreeState); } return null; } private bool ValidateUIElementForCapture(UIElement element) { if (element.IsEnabled == false) return false; if (element.IsVisible == false) return false; if (element.IsHitTestVisible == false) return false; return true; } private bool ValidateUIElement3DForCapture(UIElement3D element) { if (element.IsEnabled == false) return false; if (element.IsVisible == false) return false; if (element.IsHitTestVisible == false) return false; return true; } private bool ValidateContentElementForCapture(ContentElement element) { if (element.IsEnabled == false) return false; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. return true; } /// /// Critical: This code accesses critical data(_activeSource) /// TreatAsSafe: Although it accesses critical data it does not modify or expose it, only compares against it. /// [SecurityCritical, SecurityTreatAsSafe] private bool ValidateVisualForCapture(DependencyObject visual) { if (visual == null) return false; PresentationSource presentationSource = PresentationSource.CriticalFromVisual(visual); if (presentationSource == null) return false; if (presentationSource != _inputSource.Value) return false; return true; } private void OnOverIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is over just became disabled. // // We need to resynchronize the mouse so that we can figure out who // the mouse is over now. ReevaluateMouseOver(null, null, true); } private void OnOverIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is over just became non-visible (collapsed or hidden). // // We need to resynchronize the mouse so that we can figure out who // the mouse is over now. ReevaluateMouseOver(null, null, true); } private void OnOverIsHitTestVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is over was affected by a change in hit-test visibility. // // We need to resynchronize the mouse so that we can figure out who // the mouse is over now. ReevaluateMouseOver(null, null, true); } private void OnCaptureIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is captured to just became disabled. // // We need to re-evaluate the element that has mouse capture since // we can't allow the mouse to remain captured by a disabled element. ReevaluateCapture(null, null, true); } private void OnCaptureIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is captured to just became non-visible (collapsed or hidden). // // We need to re-evaluate the element that has mouse capture since // we can't allow the mouse to remain captured by a non-visible element. ReevaluateCapture(null, null, true); } private void OnCaptureIsHitTestVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { // The element that the mouse is captured to was affected by a change in hit-test visibility. // // We need to re-evaluate the element that has mouse capture since // we can't allow the mouse to remain captured by a non-hittest-visible element. ReevaluateCapture(null, null, true); } private void OnHitTestInvalidatedAsync(object sender, EventArgs e) { // The hit-test result may have changed. Synchronize(); } ////// Forces the mouse to resynchronize. /// ////// Critical - asserts HwndSource via _inputSource /// PublicOK: This code does not expose the HwndSource or store it, nor is /// this operation risky. /// [SecurityCritical] public void Synchronize() { // System.Console.WriteLine("Synchronize"); // VerifyAccess(); // Simulate a mouse move if (_inputSource != null && _inputSource.Value.CompositionTarget != null && !_inputSource.Value.CompositionTarget.IsDisposed) { int timeStamp = Environment.TickCount; Point ptClient = GetClientPosition(); RawMouseInputReport report = new RawMouseInputReport(InputMode.Foreground, timeStamp, _inputSource.Value, RawMouseActions.AbsoluteMove, (int) ptClient.X, (int) ptClient.Y, 0, IntPtr.Zero); report._isSynchronize = true; InputReportEventArgs inputReportEventArgs; if (_stylusDevice != null) { // if we have a current stylusdevice .. use it inputReportEventArgs = new InputReportEventArgs(_stylusDevice, report); } else { inputReportEventArgs = new InputReportEventArgs(this, report); } inputReportEventArgs.RoutedEvent=InputManager.PreviewInputReportEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(inputReportEventArgs); } } ////// Forces the mouse cursor to be updated. /// public void UpdateCursor() { // Call Forwarded UpdateCursorPrivate(); } ////// Forces the mouse cursor to be updated. /// ////// This method has been added just because changing the public /// API UpdateCursor will be a breaking change /// ////// Critical:This code access input manager and causes cursor to be updated /// TreatAsSafe: No risky critical data stored and inputs and outputs are safe. /// [SecurityCritical,SecurityTreatAsSafe] private bool UpdateCursorPrivate() { int timeStamp = Environment.TickCount; QueryCursorEventArgs queryCursor = new QueryCursorEventArgs(this, timeStamp); queryCursor.Cursor = Cursors.Arrow; queryCursor.RoutedEvent=Mouse.QueryCursorEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(queryCursor); return queryCursor.Handled; } ////// Critical: This code accesses link demanded method PresentationSource.AddSourcechangedhandler /// and remove for the same /// TreatAsSafe: This code does not expose the PresentationSource and simply changes the mouse over element /// [SecurityCritical,SecurityTreatAsSafe] private void ChangeMouseOver(IInputElement mouseOver, int timestamp) { DependencyObject o = null; if (_mouseOver != mouseOver) { // Console.WriteLine("ChangeMouseOver(" + mouseOver + ")"); // Update the critical piece of data. IInputElement oldMouseOver = _mouseOver; _mouseOver = mouseOver; using(Dispatcher.DisableProcessing()) // Disable reentrancy due to locks taken { // Adjust the handlers we use to track everything. if(oldMouseOver != null) { o = oldMouseOver as DependencyObject; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged -= _overIsEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged -= _overIsVisibleChangedEventHandler; ((UIElement)o).IsHitTestVisibleChanged -= _overIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged -= _overIsEnabledChangedEventHandler; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. // // ((ContentElement)o).IsVisibleChanged -= _overIsVisibleChangedEventHandler; // ((ContentElement)o).IsHitTestVisibleChanged -= _overIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsUIElement3D(o)) { ((UIElement3D)o).IsEnabledChanged -= _overIsEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged -= _overIsVisibleChangedEventHandler; ((UIElement3D)o).IsHitTestVisibleChanged -= _overIsHitTestVisibleChangedEventHandler; } } if(_mouseOver != null) { o = _mouseOver as DependencyObject; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged += _overIsEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged += _overIsVisibleChangedEventHandler; ((UIElement)o).IsHitTestVisibleChanged += _overIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged += _overIsEnabledChangedEventHandler; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. // // ((ContentElement)o).IsVisibleChanged += _overIsVisibleChangedEventHandler; // ((ContentElement)o).IsHitTestVisibleChanged += _overIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsUIElement3D(o)) { ((UIElement3D)o).IsEnabledChanged += _overIsEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged += _overIsVisibleChangedEventHandler; ((UIElement3D)o).IsHitTestVisibleChanged += _overIsHitTestVisibleChangedEventHandler; } } } // Oddly enough, update the IsMouseOver property first. This is // so any callbacks will see the more-common IsMouseOver property // set correctly. UIElement.MouseOverProperty.OnOriginValueChanged(oldMouseOver as DependencyObject, _mouseOver as DependencyObject, ref _mouseOverTreeState); // Invalidate the IsMouseDirectlyOver property. if (oldMouseOver != null) { o = oldMouseOver as DependencyObject; o.SetValue(UIElement.IsMouseDirectlyOverPropertyKey, false); // Same property for ContentElements } if (_mouseOver != null) { o = _mouseOver as DependencyObject; o.SetValue(UIElement.IsMouseDirectlyOverPropertyKey, true); // Same property for ContentElements } } } ////// Critical: This code acceses InputManager and MouseProvider which are critical data. /// It also calls into ProcessInput which is a critical method and could be used /// for input spoofing. /// TreatAsSafe: This operation is ok to expose since mouse capture is ok. Even if you get the /// source changed events you cannot get to the sources themselves /// [SecurityCritical,SecurityTreatAsSafe] private void ChangeMouseCapture(IInputElement mouseCapture, IMouseInputProvider providerCapture, CaptureMode captureMode, int timestamp) { DependencyObject o = null; if(mouseCapture != _mouseCapture) { // Console.WriteLine("ChangeMouseCapture(" + mouseCapture + ")"); // Update the critical pieces of data. IInputElement oldMouseCapture = _mouseCapture; _mouseCapture = mouseCapture; if (_mouseCapture != null) { _providerCapture = new SecurityCriticalDataClass(providerCapture); } else { _providerCapture = null; } _captureMode = captureMode; using (Dispatcher.DisableProcessing()) // Disable reentrancy due to locks taken { // Adjust the handlers we use to track everything. if (oldMouseCapture != null) { o = oldMouseCapture as DependencyObject; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged -= _captureIsEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged -= _captureIsVisibleChangedEventHandler; ((UIElement)o).IsHitTestVisibleChanged -= _captureIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged -= _captureIsEnabledChangedEventHandler; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. // // ((ContentElement)o).IsVisibleChanged -= _captureIsVisibleChangedEventHandler; // ((ContentElement)o).IsHitTestVisibleChanged -= _captureIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsUIElement3D(o)) { ((UIElement3D)o).IsEnabledChanged -= _captureIsEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged -= _captureIsVisibleChangedEventHandler; ((UIElement3D)o).IsHitTestVisibleChanged -= _captureIsHitTestVisibleChangedEventHandler; } } if (_mouseCapture != null) { o = _mouseCapture as DependencyObject; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged += _captureIsEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged += _captureIsVisibleChangedEventHandler; ((UIElement)o).IsHitTestVisibleChanged += _captureIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged += _captureIsEnabledChangedEventHandler; // NOTE: there are no IsVisible or IsHitTestVisible properties for ContentElements. // // ((ContentElement)o).IsVisibleChanged += _captureIsVisibleChangedEventHandler; // ((ContentElement)o).IsHitTestVisibleChanged += _captureIsHitTestVisibleChangedEventHandler; } else if (InputElement.IsUIElement3D(o)) { ((UIElement3D)o).IsEnabledChanged += _captureIsEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged += _captureIsVisibleChangedEventHandler; ((UIElement3D)o).IsHitTestVisibleChanged += _captureIsHitTestVisibleChangedEventHandler; } } } // Oddly enough, update the IsMouseCaptureWithin property first. This is // so any callbacks will see the more-common IsMouseCaptureWithin property // set correctly. UIElement.MouseCaptureWithinProperty.OnOriginValueChanged(oldMouseCapture as DependencyObject, _mouseCapture as DependencyObject, ref _mouseCaptureWithinTreeState); // Invalidate the IsMouseCaptured properties. if (oldMouseCapture != null) { o = oldMouseCapture as DependencyObject; o.SetValue(UIElement.IsMouseCapturedPropertyKey, false); // Same property for ContentElements } if (_mouseCapture != null) { o = _mouseCapture as DependencyObject; o.SetValue(UIElement.IsMouseCapturedPropertyKey, true); // Same property for ContentElements } // Send the LostMouseCapture and GotMouseCapture events. if (oldMouseCapture != null) { MouseEventArgs lostCapture = new MouseEventArgs(this, timestamp, _stylusDevice); lostCapture.RoutedEvent=Mouse.LostMouseCaptureEvent; lostCapture.Source= oldMouseCapture; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(lostCapture); } if (_mouseCapture != null) { MouseEventArgs gotCapture = new MouseEventArgs(this, timestamp, _stylusDevice); gotCapture.RoutedEvent=Mouse.GotMouseCaptureEvent; gotCapture.Source= _mouseCapture; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(gotCapture); } // Force a mouse move so we can update the mouse over. Synchronize(); } } /// /// Critical: This code acceses data that was got from an unsafe call (_doubleClickDeltaX,_doubleClickDeltaX) /// It also accesses HwndSource and InputManager and can be used for Input spoofing. /// accesses e.StagingItem.Input /// [SecurityCritical] private void PreProcessInput(object sender, PreProcessInputEventArgs e) { if (e.StagingItem.Input.RoutedEvent == InputManager.PreviewInputReportEvent) { InputReportEventArgs inputReportEventArgs = e.StagingItem.Input as InputReportEventArgs; if (!inputReportEventArgs.Handled && inputReportEventArgs.Report.Type == InputType.Mouse) { RawMouseInputReport rawMouseInputReport = (RawMouseInputReport)inputReportEventArgs.Report; // Normally we only process mouse input that is from our // active visual manager. The only exception to this is // the activate report, which is how we change the visual // manager that is active. if ((rawMouseInputReport.Actions & RawMouseActions.Activate) == RawMouseActions.Activate) { // Console.WriteLine("RawMouseActions.Activate"); // If other actions are being reported besides the // activate, separate them into different events. if ((rawMouseInputReport.Actions & ~RawMouseActions.Activate) != 0) { // Cancel this event. We'll push a new event for the activate. e.Cancel(); // Push a new RawMouseInputReport for the non-activate actions. RawMouseInputReport reportActions = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, rawMouseInputReport.Actions & ~RawMouseActions.Activate, rawMouseInputReport.X, rawMouseInputReport.Y, rawMouseInputReport.Wheel, rawMouseInputReport.ExtraInformation); InputReportEventArgs actionsArgs = new InputReportEventArgs(inputReportEventArgs.Device, reportActions); actionsArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(actionsArgs, null); // Create a new RawMouseInputReport for the activate. RawMouseInputReport reportActivate = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, RawMouseActions.Activate, rawMouseInputReport.X, rawMouseInputReport.Y, rawMouseInputReport.Wheel, rawMouseInputReport.ExtraInformation); // Push a new RawMouseInputReport for the activate. InputReportEventArgs activateArgs = new InputReportEventArgs(inputReportEventArgs.Device, reportActivate); activateArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(activateArgs, null); } } // Only process mouse input that is from our active PresentationSource. else if ((_inputSource != null) && (rawMouseInputReport.InputSource == _inputSource.Value)) { // We need to remember the StylusDevice that generated this input. Use the _tagStylusDevice // to store this in before we take over the inputReport Device and loose it. Any // input reports we re-push need to preserve this too. This is used to set the StylusDevice // property on MouseEventArgs. InputDevice inputDevice = e.StagingItem.GetData(_tagStylusDevice) as StylusDevice; if (inputDevice == null) { inputDevice = inputReportEventArgs.Device as StylusDevice; if (inputDevice != null) { e.StagingItem.SetData(_tagStylusDevice, inputDevice); } } // Claim the input for the mouse. inputReportEventArgs.Device = this; // If the input is reporting mouse deactivation, we need // to ensure that the element receives a final leave. // Note that activation could have been moved to another // visual manager in our app, which means that the leave // was already sent. So only do this if the deactivate // event is from the visual manager that we think is active. if ((rawMouseInputReport.Actions & RawMouseActions.Deactivate) == RawMouseActions.Deactivate) { if (_mouseOver != null) { // Push back this event, and cancel the current processing. e.PushInput(e.StagingItem); e.Cancel(); _isPhysicallyOver = false; ChangeMouseOver(null, e.StagingItem.Input.Timestamp); } } // If the input is reporting mouse movement, we need to check // if we need to update our sense of "mouse over". // if ((rawMouseInputReport.Actions & RawMouseActions.AbsoluteMove) == RawMouseActions.AbsoluteMove) { // If other actions are being reported besides the // move, separate them into different events. if ((rawMouseInputReport.Actions & ~(RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor)) != 0) { // Cancel this event. We'll push a new event for the move. e.Cancel(); // Push a new RawMouseInputReport for the non-move actions. RawMouseInputReport reportActions = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, rawMouseInputReport.Actions & ~(RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor), 0, 0, rawMouseInputReport.Wheel, rawMouseInputReport.ExtraInformation); InputReportEventArgs actionsArgs = new InputReportEventArgs(inputDevice, reportActions); actionsArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(actionsArgs, null); // Push a new RawMouseInputReport for the AbsoluteMove. RawMouseInputReport reportMove = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, rawMouseInputReport.Actions & (RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor), rawMouseInputReport.X, rawMouseInputReport.Y, 0, IntPtr.Zero); InputReportEventArgs moveArgs = new InputReportEventArgs(inputDevice, reportMove); moveArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(moveArgs, null); } else { // Convert the point from client coordinates into "root" coordinates. // We do this in the pre-process stage because it is possible that // this conversion will fail, in which case we want to cancel the // mouse move event. bool success = true; Point ptClient = new Point(rawMouseInputReport.X, rawMouseInputReport.Y); Point ptRoot = PointUtil.TryClientToRoot(ptClient, rawMouseInputReport.InputSource, false, out success); if(success) { e.StagingItem.SetData(_tagRootPoint, ptRoot); } else { e.Cancel(); } } } } } } else { // All mouse event processing should only happen if we still have an active input source. if (_inputSource != null) { if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseDownEvent) { MouseButtonEventArgs mouseButtonEventArgs = e.StagingItem.Input as MouseButtonEventArgs; if (_mouseCapture != null && !_isPhysicallyOver) { // The mouse is not physically over the capture point (or // subtree), so raise the PreviewMouseDownOutsideCapturedElement // event first. MouseButtonEventArgs clickThrough = new MouseButtonEventArgs(this, mouseButtonEventArgs.Timestamp, mouseButtonEventArgs.ChangedButton, GetStylusDevice(e.StagingItem)); clickThrough.RoutedEvent=Mouse.PreviewMouseDownOutsideCapturedElementEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(clickThrough); } } else if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseUpEvent) { MouseButtonEventArgs mouseButtonEventArgs = e.StagingItem.Input as MouseButtonEventArgs; if (_mouseCapture != null && !_isPhysicallyOver) { // The mouse is not physically over the capture point (or // subtree), so raise the PreviewMouseUpOutsideCapturedElement // event first. MouseButtonEventArgs clickThrough = new MouseButtonEventArgs(this, mouseButtonEventArgs.Timestamp, mouseButtonEventArgs.ChangedButton, GetStylusDevice(e.StagingItem)); clickThrough.RoutedEvent=Mouse.PreviewMouseUpOutsideCapturedElementEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(clickThrough); } } } } } ////// Critical: This code accesses data that was got off an unmanaged call (_doubleClickDeltaX,_doubleClickDeltaY) /// It also accesss Input Source and can be used for input spoofing /// accesses e.StagingItem.Input /// [SecurityCritical] private void PreNotifyInput(object sender, NotifyInputEventArgs e) { if ( e.StagingItem.Input.RoutedEvent == InputManager.PreviewInputReportEvent ) { InputReportEventArgs inputReportEventArgs = e.StagingItem.Input as InputReportEventArgs; if (!inputReportEventArgs.Handled && inputReportEventArgs.Report.Type == InputType.Mouse) { RawMouseInputReport rawMouseInputReport = (RawMouseInputReport) inputReportEventArgs.Report; // Generally, we need to check against redundant actions. // We never prevent the raw event from going through, but we // will only generate the high-level events for non-redundant // actions. We store the set of non-redundant actions in // the dictionary of this event. // Get the current Non-Redundant Actions for this event and // make a copy. We will compare the original value against the copy // at the end of this function and write it back in if changed. RawMouseActions actions = GetNonRedundantActions(e); RawMouseActions originalActions = actions; _stylusDevice = GetStylusDevice(e.StagingItem); // Normally we only process mouse input that is from our // active presentation source. The only exception to this is // the activate report, which is how we change the visual // manager that is active. if ((rawMouseInputReport.Actions & RawMouseActions.Activate) == RawMouseActions.Activate) { // System.Console.WriteLine("Initializing the mouse state."); actions |= RawMouseActions.Activate; _positionRelativeToOver.X = 0; _positionRelativeToOver.Y = 0; _lastPosition.X = rawMouseInputReport.X; _lastPosition.Y = rawMouseInputReport.Y; _forceUpdateLastPosition = true; _stylusDevice = inputReportEventArgs.Device as StylusDevice; // if the existing source is null, no need to do any special-case handling if (_inputSource == null) { _inputSource = new SecurityCriticalDataClass(rawMouseInputReport.InputSource); } // if the new source is the same as the old source, don't bother doing anything else if (_inputSource.Value != rawMouseInputReport.InputSource) { IMouseInputProvider toDeactivate = _inputSource.Value.GetInputProvider(typeof(MouseDevice)) as IMouseInputProvider; // All mouse information is now restricted to this presentation source. _inputSource = new SecurityCriticalDataClass (rawMouseInputReport.InputSource); if (toDeactivate != null) { toDeactivate.NotifyDeactivate(); } } } // Only process mouse input that is from our active presentation source. if ((_inputSource != null) && (rawMouseInputReport.InputSource == _inputSource.Value)) { // If the input is reporting mouse deactivation, we need // to break any capture we may have. Note that we only do // this if the presentation source associated with this event // is the same presentation source we are already over. if ((rawMouseInputReport.Actions & RawMouseActions.Deactivate) == RawMouseActions.Deactivate) { // Console.WriteLine("RawMouseActions.Deactivate"); Debug.Assert(_mouseOver == null, "_mouseOver should be null because we have called ChangeMouseOver(null) already."); _inputSource = null; ChangeMouseCapture(null, null, CaptureMode.None, e.StagingItem.Input.Timestamp); } if ((rawMouseInputReport.Actions & RawMouseActions.CancelCapture) == RawMouseActions.CancelCapture) { // Console.WriteLine("RawMouseActions.CancelCapture"); ChangeMouseCapture(null, null, CaptureMode.None, e.StagingItem.Input.Timestamp); } // If the input is reporting mouse movement, only update the // set of non-redundant actions if the position changed. if ((rawMouseInputReport.Actions & RawMouseActions.AbsoluteMove) == RawMouseActions.AbsoluteMove) // { //Console.WriteLine("RawMouseActions.AbsoluteMove: X=" + rawMouseInputReport.X + " Y=" + rawMouseInputReport.Y ); // Translate the mouse coordinates to both root relative and "mouseOver" relate. // - Note: "mouseOver" in this case is the element the mouse "was" over before this move. bool mouseOverAvailable = false; Point ptClient = new Point(rawMouseInputReport.X, rawMouseInputReport.Y); Point ptRoot = (Point) e.StagingItem.GetData(_tagRootPoint); Point ptRelativeToOver = InputElement.TranslatePoint(ptRoot, rawMouseInputReport.InputSource.RootVisual, (DependencyObject)_mouseOver, out mouseOverAvailable); IInputElement mouseOver = _mouseOver; // assume mouse is still over whatever it was before IInputElement rawMouseOver = (_rawMouseOver != null) ? (IInputElement)_rawMouseOver.Target : null; bool isPhysicallyOver = _isPhysicallyOver; bool isGlobalChange = ArePointsClose(ptClient, _lastPosition) == false; // determine if the mouse actually physically moved // Invoke Hit Test logic to determine what element the mouse will be over AFTER the move is processed. // - Only do this if: // - The mouse physcially moved (isGlobalChange) // - We are simulating a mouse move (_isSynchronize) // - mouseOver isn't availabe (!mouseOverAvailable) Could be caused by a degenerate transform. // - This is to mitigate the redundant AbsoluteMove notifications associated with QueryCursor if (isGlobalChange || rawMouseInputReport._isSynchronize || !mouseOverAvailable) { isPhysicallyOver = true; // assume mouse is physical over element, we'll set it false if it's due to capture switch (_captureMode) { // In this case there is no capture, so a simple hit test will determine which element becomes "mouseOver" case CaptureMode.None: { if (rawMouseInputReport._isSynchronize) { GlobalHitTest(ptClient, _inputSource.Value, out mouseOver, out rawMouseOver); } else { LocalHitTest(ptClient, _inputSource.Value, out mouseOver, out rawMouseOver); } if (mouseOver == rawMouseOver) { // Since they are the same, there is no reason to process rawMouseOver rawMouseOver = null; } // We understand UIElements and ContentElements. // If we are over something else (like a raw visual) // find the containing element. if (!InputElement.IsValid(mouseOver)) mouseOver = InputElement.GetContainingInputElement(mouseOver as DependencyObject); if ((rawMouseOver != null) && !InputElement.IsValid(rawMouseOver)) rawMouseOver = InputElement.GetContainingInputElement(rawMouseOver as DependencyObject); } break; // In this case, capture is to a specific element, so it will ALWAYS become "mouseOver" // - however, we do a hit test to see if the mouse is actually physically over the element, // - if it is not, we toggle isPhysicallyOver case CaptureMode.Element: if (rawMouseInputReport._isSynchronize) { mouseOver = GlobalHitTest(ptClient, _inputSource.Value); } else { mouseOver = LocalHitTest(ptClient, _inputSource.Value); } // There is no reason to process rawMouseOver when // the element should always be the one with mouse capture. rawMouseOver = null; if (mouseOver != _mouseCapture) { // Always consider the mouse over the capture point. mouseOver = _mouseCapture; isPhysicallyOver = false; } break; // In this case, capture is set to an entire subtree. We use simple hit testing to determine // which, if any element in the subtree it is over, and set "mouseOver to that element // If it is not over any specific subtree element, "mouseOver" is set to the root of the subtree. // - Note: a subtree can span multiple HWNDs case CaptureMode.SubTree: { IInputElement mouseCapture = InputElement.GetContainingInputElement(_mouseCapture as DependencyObject); if (mouseCapture != null) { // We need to re-hit-test to get the "real" UIElement we are over. // This allows us to have our capture-to-subtree span multiple windows. // GlobalHitTest always returns an IInputElement, so we are sure to have one. GlobalHitTest(ptClient, _inputSource.Value, out mouseOver, out rawMouseOver); } if (mouseOver != null && !InputElement.IsValid(mouseOver) ) mouseOver = InputElement.GetContainingInputElement(mouseOver as DependencyObject); // Make sure that the element we hit is acutally underneath // our captured element. Because we did a global hit test, we // could have hit an element in a completely different window. // // Note that we support the child being in a completely different window. // So we use the GetUIParent method instead of just looking at // visual/content parents. if (mouseOver != null) { IInputElement ieTest = mouseOver; UIElement eTest = null; ContentElement ceTest = null; UIElement3D e3DTest = null; while (ieTest != null && ieTest != mouseCapture) { eTest = ieTest as UIElement; if (eTest != null) { ieTest = InputElement.GetContainingInputElement(eTest.GetUIParent(true)); } else { ceTest = ieTest as ContentElement; if (ceTest != null) { ieTest = InputElement.GetContainingInputElement(ceTest.GetUIParent(true)); } else { e3DTest = ieTest as UIElement3D; // Should never fail. ieTest = InputElement.GetContainingInputElement(e3DTest.GetUIParent(true)); } } } // If we missed the capture point, we didn't hit anything. if (ieTest != mouseCapture) { mouseOver = _mouseCapture; isPhysicallyOver = false; // Since they are the same, there is no reason to process rawMouseOver rawMouseOver = null; } } else { // We didn't hit anything. Consider the mouse over the capture point. mouseOver = _mouseCapture; isPhysicallyOver = false; // Since they are the same, there is no reason to process rawMouseOver rawMouseOver = null; } if (rawMouseOver != null) { if (mouseOver == rawMouseOver) { // Since they are the same, there is no reason to process rawMouseOver rawMouseOver = null; } else if (!InputElement.IsValid(rawMouseOver)) { rawMouseOver = InputElement.GetContainingInputElement(rawMouseOver as DependencyObject); } } } break; } } _isPhysicallyOver = mouseOver == null ? false : isPhysicallyOver; // Now that we've determine what element the mouse is over now (mouseOver) // - we need to check if it's changed bool isMouseOverChange = mouseOver != _mouseOver; // If mouseOver changed, we need to recalculate the ptRelativeToOver, because "Over" changed! if (isMouseOverChange) { ptRelativeToOver = InputElement.TranslatePoint(ptRoot, rawMouseInputReport.InputSource.RootVisual, (DependencyObject)mouseOver); } //Console.WriteLine("RawMouseActions.AbsoluteMove: mouse moved over " + (isMouseOverChange ? "same" : "different") + " element. old=" + _mouseOver + " new=" + mouseOver); //Console.WriteLine("RawMouseActions.AbsoluteMove: capture=" + _mouseCapture); // Check to see if the local mouse position changed. This can be // caused by a change to the geometry of the // element we are over or a change in which element // we are over. // bool isLocalChange = isMouseOverChange || ArePointsClose(ptRelativeToOver, _positionRelativeToOver) == false; // Console.WriteLine("RawMouseActions.AbsoluteMove: isGlobalChange=" + isGlobalChange + " isLocalChange=" + isLocalChange); // We only update our cached position (_lastPosition & _positionRelativeToOver ) // if we have moved "far enough" allowing small incrementaly moves to accumulate if (isGlobalChange || isLocalChange || _forceUpdateLastPosition) { _forceUpdateLastPosition = false; _lastPosition = ptClient; _positionRelativeToOver = ptRelativeToOver; if (isMouseOverChange) { ChangeMouseOver(mouseOver, e.StagingItem.Input.Timestamp); } if ((_rawMouseOver == null) && (rawMouseOver != null)) { _rawMouseOver = new WeakReference(rawMouseOver); } else if (_rawMouseOver != null) { _rawMouseOver.Target = rawMouseOver; } // Console.WriteLine("RawMouseActions.AbsoluteMove: ptRoot=" + ptRoot); actions |= RawMouseActions.AbsoluteMove; // // In most cases the sequence of messages received from the system are HitTest, SetCursor & MouseMove. // The SetCursor message in this case will be traslated into an Avalon MouseMove & QueryCursor. // The MouseMove message to follow is redundant and is thrown away. // But imagine a case where Capture is taken. Here the system produces only two messages HitTest & MouseMove. // Hence we translate the MouseMove into an Avalon MouseMove & QueryCursor. // Logically MouseMove and QueryCursor go as a pair. actions |= RawMouseActions.QueryCursor; } } // Mouse wheel rotate events are never considered redundant. if ((rawMouseInputReport.Actions & RawMouseActions.VerticalWheelRotate) == RawMouseActions.VerticalWheelRotate) { // Console.WriteLine("RawMouseActions.VerticalWheelRotate"); actions |= RawMouseActions.VerticalWheelRotate; // Tell the InputManager that the MostRecentDevice is us. _inputManager.Value.MostRecentInputDevice = this; } // Mouse query cursor events are never considered redundant. if ((rawMouseInputReport.Actions & RawMouseActions.QueryCursor) == RawMouseActions.QueryCursor) { // Console.WriteLine("RawMouseActions.QueryCursor"); actions |= RawMouseActions.QueryCursor; } RawMouseActions[] ButtonPressActions = { RawMouseActions.Button1Press, RawMouseActions.Button2Press, RawMouseActions.Button3Press, RawMouseActions.Button4Press, RawMouseActions.Button5Press }; RawMouseActions[] ButtonReleaseActions = { RawMouseActions.Button1Release, RawMouseActions.Button2Release, RawMouseActions.Button3Release, RawMouseActions.Button4Release, RawMouseActions.Button5Release }; for (int iButton = 0; iButton < 5; iButton++) { if ((rawMouseInputReport.Actions & ButtonPressActions[iButton]) == ButtonPressActions[iButton]) { actions |= ButtonPressActions[iButton]; // Tell the InputManager that the MostRecentDevice is us. _inputManager.Value.MostRecentInputDevice = this; } if ((rawMouseInputReport.Actions & ButtonReleaseActions[iButton]) == ButtonReleaseActions[iButton]) { actions |= ButtonReleaseActions[iButton]; // Tell the InputManager that the MostRecentDevice is us. _inputManager.Value.MostRecentInputDevice = this; } } } if (actions != originalActions) { e.StagingItem.SetData(_tagNonRedundantActions, actions); } } } else { // All mouse event processing should only happen if we still have an active input source. if (_inputSource != null) { // During the PreviewMouseDown event, we update the click count, if there are // multiple "quick" clicks in approximately the "same" location (as defined // by the hosting environment, aka the registry). if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseDownEvent) { MouseButtonEventArgs mouseButtonArgs = e.StagingItem.Input as MouseButtonEventArgs; StylusDevice stylusDevice = GetStylusDevice(e.StagingItem); Point ptClient = GetClientPosition(); _clickCount = CalculateClickCount(mouseButtonArgs.ChangedButton, mouseButtonArgs.Timestamp, stylusDevice, ptClient); if (_clickCount == 1) { // we need to reset out data, since this is the start of the click count process... _lastClick = ptClient; _lastButton = mouseButtonArgs.ChangedButton; _lastClickTime = mouseButtonArgs.Timestamp; } // Put the updated count into the args. mouseButtonArgs.ClickCount = _clickCount; } } } } // Due to the inexactness of math calculations of // floating-point numbers, we use the AreClose method // to determine if the coordinates are close enough // to consider the same. private bool ArePointsClose(Point A, Point B) { return MS.Internal.DoubleUtil.AreClose(A.X, B.X) && MS.Internal.DoubleUtil.AreClose(A.Y, B.Y); } /// /// Critical: This code elevates permissions by calling unsafe native methods, also this /// function is not ok to expose publicly and hence the critical. /// accesses e.StagingItem.Input /// [SecurityCritical] private void PostProcessInput(object sender, ProcessInputEventArgs e) { // PreviewMouseWheel --> MouseWheel if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseWheelEvent) { if (!e.StagingItem.Input.Handled) { MouseWheelEventArgs previewWheel = (MouseWheelEventArgs) e.StagingItem.Input; MouseWheelEventArgs wheel = new MouseWheelEventArgs(this, previewWheel.Timestamp, previewWheel.Delta); wheel.RoutedEvent=Mouse.MouseWheelEvent; #if SEND_WHEEL_EVENTS_TO_FOCUS // wheel events are treated as if they came from the // element with keyboard focus wheel.Source = previewWheel.Source; #endif e.PushInput(wheel, e.StagingItem); } } // PreviewMouseDown --> MouseDown if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseDownEvent) { if (!e.StagingItem.Input.Handled) { MouseButtonEventArgs previewDown = (MouseButtonEventArgs) e.StagingItem.Input; MouseButtonEventArgs down = new MouseButtonEventArgs(this, previewDown.Timestamp, previewDown.ChangedButton, GetStylusDevice(e.StagingItem)); down.ClickCount = previewDown.ClickCount; down.RoutedEvent=Mouse.MouseDownEvent; e.PushInput(down, e.StagingItem); } } // PreviewMouseUp --> MouseUp if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseUpEvent) { if (!e.StagingItem.Input.Handled) { MouseButtonEventArgs previewUp = (MouseButtonEventArgs) e.StagingItem.Input; MouseButtonEventArgs up = new MouseButtonEventArgs(this, previewUp.Timestamp, previewUp.ChangedButton, GetStylusDevice(e.StagingItem)); up.RoutedEvent=Mouse.MouseUpEvent; e.PushInput(up, e.StagingItem); } } // PreviewMouseMove --> MouseMove if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseMoveEvent) { if (!e.StagingItem.Input.Handled) { MouseEventArgs previewMove = (MouseEventArgs) e.StagingItem.Input; MouseEventArgs move = new MouseEventArgs(this, previewMove.Timestamp, GetStylusDevice(e.StagingItem)); move.RoutedEvent=Mouse.MouseMoveEvent; e.PushInput(move, e.StagingItem); } } // We are finished processing the QueryCursor event. Just update the // mouse cursor to be what was decided during the event route. if (e.StagingItem.Input.RoutedEvent == Mouse.QueryCursorEvent) { QueryCursorEventArgs queryCursor = (QueryCursorEventArgs)e.StagingItem.Input; SetCursor(queryCursor.Cursor); } if (e.StagingItem.Input.RoutedEvent == InputManager.InputReportEvent) { InputReportEventArgs inputReportEventArgs = e.StagingItem.Input as InputReportEventArgs; if (!inputReportEventArgs.Handled && inputReportEventArgs.Report.Type == InputType.Mouse) { RawMouseInputReport rawMouseInputReport = (RawMouseInputReport) inputReportEventArgs.Report; // Only process mouse input that is from our active visual manager. if ((_inputSource != null) && (rawMouseInputReport.InputSource == _inputSource.Value)) { // In general, this is where we promote the non-redundant // reported actions to our premier events. RawMouseActions actions = GetNonRedundantActions(e); // Raw Activate --> Raw MouseMove // Whenever the mouse device is activated we need to // cause a mouse move so that elements realize that // the mouse is over them again. In most cases, the // action that caused the mouse to activate is a move, // but this is to guard against any other cases. if ((actions & RawMouseActions.Activate) == RawMouseActions.Activate) { Synchronize(); } // Raw --> PreviewMouseWheel if ((actions & RawMouseActions.VerticalWheelRotate) == RawMouseActions.VerticalWheelRotate) // { MouseWheelEventArgs previewWheel = new MouseWheelEventArgs(this, rawMouseInputReport.Timestamp, rawMouseInputReport.Wheel); previewWheel.RoutedEvent=Mouse.PreviewMouseWheelEvent; #if SEND_WHEEL_EVENTS_TO_FOCUS // wheel events are treated as if they came from the // element with keyboard focus DependencyObject focus = Keyboard.FocusedElement as DependencyObject; if (focus != null) { previewWheel.Source = focus; } #endif e.PushInput(previewWheel, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button1Press) == RawMouseActions.Button1Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Left, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button1Release) == RawMouseActions.Button1Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Left, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button2Press) == RawMouseActions.Button2Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Right, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button2Release) == RawMouseActions.Button2Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Right, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button3Press) == RawMouseActions.Button3Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Middle, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button3Release) == RawMouseActions.Button3Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.Middle, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button4Press) == RawMouseActions.Button4Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.XButton1, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button4Release) == RawMouseActions.Button4Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.XButton1, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseDown if ((actions & RawMouseActions.Button5Press) == RawMouseActions.Button5Press) { MouseButtonEventArgs previewDown = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.XButton2, GetStylusDevice(e.StagingItem)); previewDown.RoutedEvent=Mouse.PreviewMouseDownEvent; e.PushInput(previewDown, e.StagingItem); } // Raw --> PreviewMouseUp if ((actions & RawMouseActions.Button5Release) == RawMouseActions.Button5Release) { MouseButtonEventArgs previewUp = new MouseButtonEventArgs(this, rawMouseInputReport.Timestamp, MouseButton.XButton2, GetStylusDevice(e.StagingItem)); previewUp.RoutedEvent=Mouse.PreviewMouseUpEvent; e.PushInput(previewUp, e.StagingItem); } // Raw --> PreviewMouseMove if ((actions & RawMouseActions.AbsoluteMove) == RawMouseActions.AbsoluteMove) // { MouseEventArgs previewMove = new MouseEventArgs(this, rawMouseInputReport.Timestamp, GetStylusDevice(e.StagingItem)); previewMove.RoutedEvent=Mouse.PreviewMouseMoveEvent; e.PushInput(previewMove, e.StagingItem); } // Raw --> QueryCursor if ((actions & RawMouseActions.QueryCursor) == RawMouseActions.QueryCursor) { inputReportEventArgs.Handled = UpdateCursorPrivate(); } } } } } private RawMouseActions GetNonRedundantActions(NotifyInputEventArgs e) { RawMouseActions actions = new RawMouseActions(); // The CLR throws a null-ref exception if it tries to unbox a // null. So we have to special case that. object o = e.StagingItem.GetData(_tagNonRedundantActions); if (o != null) { actions = (RawMouseActions) o; } return actions; } internal static IInputElement GlobalHitTest(Point ptClient, PresentationSource inputSource) { IInputElement enabledHit; IInputElement originalHit; GlobalHitTest(ptClient, inputSource, out enabledHit, out originalHit); return enabledHit; } // Take a point relative the the specified visual manager, and translate // up to the screen, hit-test to a window, and then hit-test down to an // element. ////// Critical: This code accesses PresentationSource and returns element which is hittest /// TreatAsSafe: This function is safe to expose. Only the element being clicked on is returned. /// [SecurityCritical,SecurityTreatAsSafe] private static void GlobalHitTest(Point ptClient, PresentationSource inputSource, out IInputElement enabledHit, out IInputElement originalHit) { enabledHit = originalHit = null; // Note: this only works for HWNDs for now. HwndSource source = inputSource as HwndSource; if (source != null && source.CompositionTarget != null && !source.IsHandleNull) { Point ptScreen = PointUtil.ClientToScreen(ptClient, source); IntPtr hwndHit = IntPtr.Zero ; HwndSource sourceHit = null ; // Hit-test for a window. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert: try { // Find the HWND under the point. hwndHit = UnsafeNativeMethods.WindowFromPoint((int)ptScreen.X, (int)ptScreen.Y); // Make sure the window is enabled! if (!SafeNativeMethods.IsWindowEnabled(new HandleRef(null, hwndHit))) { hwndHit = IntPtr.Zero; } } finally { CodeAccessPermission.RevertAssert(); } if (hwndHit != IntPtr.Zero) { // See if this is one of our windows. sourceHit = HwndSource.CriticalFromHwnd(hwndHit); } if (sourceHit != null && sourceHit.Dispatcher == inputSource.CompositionTarget.Dispatcher) { Point ptClientHit = PointUtil.ScreenToClient(ptScreen, sourceHit); // Perform a local hit-test within this visual manager. LocalHitTest(ptClientHit, sourceHit, out enabledHit, out originalHit); } } } internal static IInputElement LocalHitTest(Point ptClient, PresentationSource inputSource) { IInputElement enabledHit; IInputElement originalHit; LocalHitTest(ptClient, inputSource, out enabledHit, out originalHit); return enabledHit; } // Take a point relative the the specified visual manager and hit-test // down to an element. ////// Critical: This code accesses PresentationSource and returns element which is hittest /// TreatAsSafe: This function is safe to expose. Only the element being clicked on is returned. /// [SecurityCritical,SecurityTreatAsSafe] private static void LocalHitTest(Point ptClient, PresentationSource inputSource, out IInputElement enabledHit, out IInputElement originalHit) { enabledHit = originalHit = null; // Hit-test starting from the root UIElement. // Note: this restricts us to windows with UIElement as the root (not just visuals). if (inputSource != null) { UIElement root = inputSource.RootVisual as UIElement; if(root != null) { Point pt = PointUtil.ClientToRoot(ptClient, inputSource); root.InputHitTest(pt, out enabledHit, out originalHit); } } } internal bool IsSameSpot(Point newPosition, StylusDevice stylusDevice) { int doubleClickDeltaX = (stylusDevice != null)?stylusDevice.DoubleTapDeltaX:_doubleClickDeltaX; int doubleClickDeltaY = (stylusDevice != null)?stylusDevice.DoubleTapDeltaY:_doubleClickDeltaY; // Is the delta coordinates of this click close enough to the last click? return (Math.Abs(newPosition.X - _lastClick.X) < doubleClickDeltaX) && (Math.Abs(newPosition.Y - _lastClick.Y) < doubleClickDeltaY); } internal int CalculateClickCount(MouseButton button, int timeStamp, StylusDevice stylusDevice, Point downPt) { // How long since the last click? int timeSpan = timeStamp - _lastClickTime; int doubleClickDeltaTime = (stylusDevice != null)?stylusDevice.DoubleTapDeltaTime:_doubleClickDeltaTime; // Is the delta coordinates of this click close enough to the last click? bool isSameSpot = IsSameSpot(downPt, stylusDevice); // Is this the same mouse button as the last click? bool isSameButton = (_lastButton == button); // Now check everything to see if this is a multi-click. if (timeSpan < doubleClickDeltaTime && isSameSpot && isSameButton) { // Yes, increment the count return _clickCount +1; } else { // No, not a multi-click. return 1; } } internal Point PositionRelativeToOver { get { return _positionRelativeToOver; } } internal Point NonRelativePosition { get { return _lastPosition; } } ////// Critical: accesses critical data (_inputSource) /// TreatAsSafe: doesn't expose critical data, just returns true/false. /// internal bool IsActive { [SecurityCritical, SecurityTreatAsSafe] get { return _inputSource != null && _inputSource.Value != null; } } // Helper to access the StylusDevice property. private StylusDevice GetStylusDevice(StagingAreaInputItem stagingItem) { return stagingItem.GetData(_tagStylusDevice) as StylusDevice; } internal StylusDevice StylusDevice { get { return _stylusDevice; } } private DeferredElementTreeState MouseOverTreeState { get { if (_mouseOverTreeState == null) { _mouseOverTreeState = new DeferredElementTreeState(); } return _mouseOverTreeState; } } private DeferredElementTreeState MouseCaptureWithinTreeState { get { if (_mouseCaptureWithinTreeState == null) { _mouseCaptureWithinTreeState = new DeferredElementTreeState(); } return _mouseCaptureWithinTreeState; } } ////// This data is not safe to expose as it holds refrence to PresentationSource /// private SecurityCriticalDataClass_inputSource; /// /// This data is not safe to expose as it holds refrence to PresentationSource /// private SecurityCriticalData_inputManager; private IInputElement _mouseOver; private DeferredElementTreeState _mouseOverTreeState; private bool _isPhysicallyOver; private WeakReference _rawMouseOver; private IInputElement _mouseCapture; private DeferredElementTreeState _mouseCaptureWithinTreeState; private SecurityCriticalDataClass _providerCapture; private CaptureMode _captureMode; private DependencyPropertyChangedEventHandler _overIsEnabledChangedEventHandler; private DependencyPropertyChangedEventHandler _overIsVisibleChangedEventHandler; private DependencyPropertyChangedEventHandler _overIsHitTestVisibleChangedEventHandler; private DispatcherOperationCallback _reevaluateMouseOverDelegate; private DispatcherOperation _reevaluateMouseOverOperation; private DependencyPropertyChangedEventHandler _captureIsEnabledChangedEventHandler; private DependencyPropertyChangedEventHandler _captureIsVisibleChangedEventHandler; private DependencyPropertyChangedEventHandler _captureIsHitTestVisibleChangedEventHandler; private DispatcherOperationCallback _reevaluateCaptureDelegate; private DispatcherOperation _reevaluateCaptureOperation; // Device state we track private Point _positionRelativeToOver = new Point(); private Point _lastPosition = new Point(); private bool _forceUpdateLastPosition = false; // Data tags for information we pass around the staging area. private object _tagNonRedundantActions = new object(); private object _tagStylusDevice = new object(); private object _tagRootPoint = new object(); // Information used to distinguish double-clicks (actually, multi clicks) from // multiple independent clicks. private Point _lastClick = new Point(); private MouseButton _lastButton; private int _clickCount; private int _lastClickTime; private int _doubleClickDeltaTime; private int _doubleClickDeltaX; private int _doubleClickDeltaY; private Cursor _overrideCursor; // Reference to StylusDevice to defer to for physical mouse state (position/button state) private StylusDevice _stylusDevice = null; } } // 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
- TemplatePagerField.cs
- UIPropertyMetadata.cs
- MenuItem.cs
- XmlDictionaryReaderQuotas.cs
- LinkConverter.cs
- TypeValidationEventArgs.cs
- SymbolType.cs
- XmlSchemaAttributeGroupRef.cs
- RotateTransform3D.cs
- SessionParameter.cs
- SchemaTableOptionalColumn.cs
- NextPreviousPagerField.cs
- DataGridViewHitTestInfo.cs
- Pkcs7Signer.cs
- StringWriter.cs
- SharedStatics.cs
- AttachedPropertyBrowsableWhenAttributePresentAttribute.cs
- IncrementalReadDecoders.cs
- XhtmlTextWriter.cs
- ReadOnlyNameValueCollection.cs
- SqlCacheDependencySection.cs
- EntityTypeEmitter.cs
- AppDomainManager.cs
- CustomAssemblyResolver.cs
- ButtonBaseAutomationPeer.cs
- LineSegment.cs
- util.cs
- FastEncoder.cs
- ConfigurationManagerHelper.cs
- TableRow.cs
- RecordsAffectedEventArgs.cs
- SlotInfo.cs
- SchemaImporterExtensionElementCollection.cs
- HttpContext.cs
- DataGridSortingEventArgs.cs
- SiteMapHierarchicalDataSourceView.cs
- _ReceiveMessageOverlappedAsyncResult.cs
- SymmetricCryptoHandle.cs
- SiteMapSection.cs
- QueuePathDialog.cs
- DataBoundLiteralControl.cs
- SqlRetyper.cs
- WebBrowserEvent.cs
- ReferentialConstraint.cs
- DataGridColumnEventArgs.cs
- StaticResourceExtension.cs
- FileLevelControlBuilderAttribute.cs
- SqlWorkflowPersistenceService.cs
- ImageEditor.cs
- Vector3dCollection.cs
- ConnectionInterfaceCollection.cs
- ButtonFlatAdapter.cs
- HorizontalAlignConverter.cs
- UniqueConstraint.cs
- PermissionToken.cs
- IConvertible.cs
- HtmlImageAdapter.cs
- CodeAttachEventStatement.cs
- CqlBlock.cs
- BuildResultCache.cs
- DiagnosticsConfiguration.cs
- SmtpFailedRecipientsException.cs
- RtfToXamlLexer.cs
- WebPartTransformerCollection.cs
- SystemBrushes.cs
- Collection.cs
- DispatcherFrame.cs
- Roles.cs
- CipherData.cs
- RemotingSurrogateSelector.cs
- TextServicesCompartmentEventSink.cs
- PasswordDeriveBytes.cs
- FontFamilyIdentifier.cs
- XmlDictionaryReaderQuotas.cs
- LinqDataSourceView.cs
- Tokenizer.cs
- HttpProfileGroupBase.cs
- TraceProvider.cs
- EnterpriseServicesHelper.cs
- RequestCacheValidator.cs
- DataGridCellAutomationPeer.cs
- InstanceKey.cs
- SeparatorAutomationPeer.cs
- HMACSHA1.cs
- UnsafeNativeMethods.cs
- AnimationTimeline.cs
- CollectionViewGroupRoot.cs
- IdentityHolder.cs
- IApplicationTrustManager.cs
- XmlSchemaAll.cs
- SystemException.cs
- DiagnosticTrace.cs
- Method.cs
- BatchWriter.cs
- EventData.cs
- SafeMILHandle.cs
- RoleGroup.cs
- OutArgumentConverter.cs
- LogSwitch.cs
- ImageIndexConverter.cs