/// <summary> /// Analyzes stylus input and determines if a SystemGesture should be generated. /// </summary> /// <param name="stylusInputReport">The latest stylus input report.</param> /// <returns>The SystemGesture to generate, null otherwise.</returns> internal SystemGesture?GenerateStaticGesture(RawStylusInputReport stylusInputReport) { switch (stylusInputReport.Actions) { case RawStylusActions.Down: OnTouchDown(stylusInputReport); return(null); case RawStylusActions.Up: return(OnTouchUp(stylusInputReport)); case RawStylusActions.SystemGesture: OnSystemGesture((RawStylusSystemGestureInputReport)stylusInputReport); return(null); /* * case RawStylusActions.Move: * case RawStylusActions.Activate: * case RawStylusActions.Deactivate: * case RawStylusActions.OutOfRange: * return null; */ default: return(null); } }
/// <summary> /// Analyzes stylus input and determines if a SystemGesture should be generated. /// </summary> /// <param name="stylusInputReport">The latest stylus input report.</param> /// <returns>The SystemGesture to generate, null otherwise.</returns> internal SystemGesture? GenerateStaticGesture(RawStylusInputReport stylusInputReport) { switch (stylusInputReport.Actions) { case RawStylusActions.Down: OnTouchDown(stylusInputReport); return null; case RawStylusActions.Up: return OnTouchUp(stylusInputReport); case RawStylusActions.SystemGesture: OnSystemGesture((RawStylusSystemGestureInputReport)stylusInputReport); return null; /* case RawStylusActions.Move: case RawStylusActions.Activate: case RawStylusActions.Deactivate: case RawStylusActions.OutOfRange: return null; */ default: return null; } }
private SystemGesture? OnTouchUp(RawStylusInputReport stylusInputReport) { switch (_currentState) { case State.TwoFingersDown: if (IsTrackedStylusId(stylusInputReport.StylusDeviceId)) { // One of the two fingers released _firstUpTime = Environment.TickCount; _currentState = State.OneFingerInStaticGesture; } break; case State.OneFingerDown: if (IsTrackedStylusId(stylusInputReport.StylusDeviceId)) { // The single finger that was down was released _currentState = State.Idle; } break; case State.OneFingerInStaticGesture: // The last of two fingers released _currentState = State.Idle; // Check the times and see if a TwoFingerTap or Rollover should be generated. // TwoFingerTap is more constrained than Rollover, so check it first. if (IsTwoFingerTap()) { return SystemGesture.TwoFingerTap; } #if ROLLOVER_IMPLEMENTED else if (IsRollover()) { return SystemGesture.Rollover; } #endif break; case State.TwoFingersInWisptisGesture: if (IsTrackedStylusId(stylusInputReport.StylusDeviceId)) { // One of the two fingers released. _currentState = State.OneFingerInWisptisGesture; } break; case State.OneFingerInWisptisGesture: if (IsTrackedStylusId(stylusInputReport.StylusDeviceId)) { // The last finger released _currentState = State.Idle; } break; } return null; }
internal StylusPlugInCollection InvokeStylusPluginCollectionForMouse(RawStylusInputReport inputReport, IInputElement directlyOver, StylusPlugInCollection currentPlugInCollection) { StylusPlugInCollection newPlugInCollection = null; // lock to make sure only one event is processed at a time and no changes to state can // be made until we finish routing this event. lock (__rtiLock) { //Debug.Assert(inputReport.Actions == RawStylusActions.Down || // inputReport.Actions == RawStylusActions.Up || // inputReport.Actions == RawStylusActions.Move); // Find new target plugin collection if (directlyOver != null) { UIElement uiElement = InputElement.GetContainingUIElement(directlyOver as DependencyObject) as UIElement; if (uiElement != null) { newPlugInCollection = FindPlugInCollection(uiElement); } } // Fire Leave event to old pluginCollection if we need to. if (currentPlugInCollection != null && currentPlugInCollection != newPlugInCollection) { // NOTE: input report points for mouse are in avalon measured units and not device! RawStylusInput tempRawStylusInput = new RawStylusInput(inputReport, currentPlugInCollection.ViewToElement, currentPlugInCollection); currentPlugInCollection.FireEnterLeave(false, tempRawStylusInput, true); // Indicate we've used a stylus plugin _stylusLogic.Statistics.FeaturesUsed |= Tracing.StylusTraceLogger.FeatureFlags.StylusPluginsUsed; } if (newPlugInCollection != null) { // NOTE: input report points for mouse are in avalon measured units and not device! RawStylusInput rawStylusInput = new RawStylusInput(inputReport, newPlugInCollection.ViewToElement, newPlugInCollection); inputReport.RawStylusInput = rawStylusInput; if (newPlugInCollection != currentPlugInCollection) { newPlugInCollection.FireEnterLeave(true, rawStylusInput, true); } // We are on the pen thread, just call directly. newPlugInCollection.FireRawStylusInput(rawStylusInput); // Indicate we've used a stylus plugin _stylusLogic.Statistics.FeaturesUsed |= Tracing.StylusTraceLogger.FeatureFlags.StylusPluginsUsed; // Fire custom data events (always confirmed for mouse) foreach (RawStylusInputCustomData customData in rawStylusInput.CustomDataList) { customData.Owner.FireCustomData(customData.Data, inputReport.Actions, true); } } } return(newPlugInCollection); }
private SystemGesture?OnTouchUp(RawStylusInputReport stylusInputReport) { switch (_currentState) { case State.TwoFingersDown: if (IsTrackedStylusId(stylusInputReport.StylusDeviceId)) { // One of the two fingers released _firstUpTime = Environment.TickCount; _currentState = State.OneFingerInStaticGesture; } break; case State.OneFingerDown: if (IsTrackedStylusId(stylusInputReport.StylusDeviceId)) { // The single finger that was down was released _currentState = State.Idle; } break; case State.OneFingerInStaticGesture: // The last of two fingers released _currentState = State.Idle; // Check the times and see if a TwoFingerTap or Rollover should be generated. // TwoFingerTap is more constrained than Rollover, so check it first. if (IsTwoFingerTap()) { return(SystemGesture.TwoFingerTap); } #if ROLLOVER_IMPLEMENTED else if (IsRollover()) { return(SystemGesture.Rollover); } #endif break; case State.TwoFingersInWisptisGesture: if (IsTrackedStylusId(stylusInputReport.StylusDeviceId)) { // One of the two fingers released. _currentState = State.OneFingerInWisptisGesture; } break; case State.OneFingerInWisptisGesture: if (IsTrackedStylusId(stylusInputReport.StylusDeviceId)) { // The last finger released _currentState = State.Idle; } break; } return(null); }
internal SystemGesture?GenerateStaticGesture(RawStylusInputReport stylusInputReport) { if (_multiTouchSystemGestureLogic != null) { return(_multiTouchSystemGestureLogic.GenerateStaticGesture(stylusInputReport)); } return(null); }
internal Point GetLastTabletPoint(RawStylusInputReport stylusInputReport) { var lastPoint = stylusInputReport.GetLastTabletPoint(); if (!_tabletToView.HasValue) { // _tabletToView = InputManager.Current.StylusLogic.GetTabletToViewTransform(this); } return(_tabletToView.Value.Transform(lastPoint)); }
internal StylusPlugInCollection TargetPlugInCollection(RawStylusInputReport inputReport) { // Caller must make call to this routine inside of lock(__rtiLock)! StylusPlugInCollection pic = null; // We should only be called when not on the application thread! System.Diagnostics.Debug.Assert(!inputReport.StylusDevice.CheckAccess()); WispStylusDevice stylusDevice = inputReport.StylusDevice.As <WispStylusDevice>(); // We're on the pen thread so can't touch visual tree. Use capturedPlugIn (if capture on) or cached rects. bool elementHasCapture = false; pic = stylusDevice.GetCapturedPlugInCollection(ref elementHasCapture); int pointLength = inputReport.PenContext.StylusPointDescription.GetInputArrayLengthPerPoint(); // Make sure that the captured Plugin Collection is still in the list. CaptureChanges are // deferred so there is a window where the stylus device is not updated yet. This protects us // from using a bogus plugin collecton for an element that is in an invalid state. if (elementHasCapture && !_plugInCollectionList.Contains(pic)) { elementHasCapture = false; // force true hittesting to be done! } if (!elementHasCapture && inputReport.Data != null && inputReport.Data.Length >= pointLength) { int[] data = inputReport.Data; System.Diagnostics.Debug.Assert(data.Length % pointLength == 0); Point ptTablet = new Point(data[data.Length - pointLength], data[data.Length - pointLength + 1]); // Note: the StylusLogic data inside DeviceUnitsFromMeasurUnits is protected by __rtiLock. ptTablet = ptTablet * stylusDevice.TabletDevice.TabletDeviceImpl.TabletToScreen; ptTablet.X = (int)Math.Round(ptTablet.X); // Make sure we snap to whole window pixels. ptTablet.Y = (int)Math.Round(ptTablet.Y); ptTablet = _stylusLogic.MeasureUnitsFromDeviceUnits(ptTablet); // change to measured units now. pic = HittestPlugInCollection(ptTablet); // Use cached rectangles for UIElements. } return(pic); }
private void OnTouchDown(RawStylusInputReport stylusInputReport) { switch (_currentState) { case State.Idle: // The first finger came down Reset(); // Clear old settings _firstStylusDeviceId = stylusInputReport.StylusDeviceId; _currentState = State.OneFingerDown; _firstDownTime = Environment.TickCount; break; case State.OneFingerDown: // The second finger came down Debug.Assert(_firstStylusDeviceId != null && _firstStylusDeviceId != stylusInputReport.StylusDeviceId); Debug.Assert(_secondStylusDeviceId == null); _secondStylusDeviceId = stylusInputReport.StylusDeviceId; _currentState = State.TwoFingersDown; break; // Ignoring fingers beyond two } }
void CallPlugInsForMouse(ProcessInputEventArgs e) { if (!e.StagingItem.Input.Handled) { // if we see a preview mouse event that is not generated by a stylus // then send on to plugin if ((e.StagingItem.Input.RoutedEvent != Mouse.PreviewMouseDownEvent) && (e.StagingItem.Input.RoutedEvent != Mouse.PreviewMouseUpEvent) && (e.StagingItem.Input.RoutedEvent != Mouse.PreviewMouseMoveEvent) && (e.StagingItem.Input.RoutedEvent != InputManager.InputReportEvent)) return; // record the mouse capture for later reference.. MouseDevice mouseDevice; PresentationSource source; bool leftButtonDown; bool rightButtonDown; RawStylusActions stylusActions = RawStylusActions.None; int timestamp; // See if we need to deal sending a leave due to this PresentationSource being Deactivated // If not then we just return and do nothing. if (e.StagingItem.Input.RoutedEvent == InputManager.InputReportEvent) { if (_activeMousePlugInCollection == null || _activeMousePlugInCollection.Element == null) return; InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; if (input.Report.Type != InputType.Mouse) return; RawMouseInputReport mouseInputReport = (RawMouseInputReport) input.Report; if ((mouseInputReport.Actions & RawMouseActions.Deactivate) != RawMouseActions.Deactivate) return; mouseDevice = _inputManager.Value.PrimaryMouseDevice; // Mouse set directly over to null when truly deactivating. if (mouseDevice == null || mouseDevice.DirectlyOver != null) return; leftButtonDown = mouseDevice.LeftButton == MouseButtonState.Pressed; rightButtonDown = mouseDevice.RightButton == MouseButtonState.Pressed; timestamp = mouseInputReport.Timestamp; // Get presentationsource from element. source = PresentationSource.CriticalFromVisual(_activeMousePlugInCollection.Element as Visual); } else { MouseEventArgs mouseEventArgs = e.StagingItem.Input as MouseEventArgs; mouseDevice = mouseEventArgs.MouseDevice; leftButtonDown = mouseDevice.LeftButton == MouseButtonState.Pressed; rightButtonDown = mouseDevice.RightButton == MouseButtonState.Pressed; // Only look at mouse input reports that truly come from a mouse (and is not an up or deactivate) and it // must be pressed state if a move (we don't fire stylus inair moves currently) if (mouseEventArgs.StylusDevice != null && e.StagingItem.Input.RoutedEvent != Mouse.PreviewMouseUpEvent) return; if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseMoveEvent) { if (!leftButtonDown) return; stylusActions = RawStylusActions.Move; } if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseDownEvent) { MouseButtonEventArgs mouseButtonEventArgs = mouseEventArgs as MouseButtonEventArgs; if (mouseButtonEventArgs.ChangedButton != MouseButton.Left) return; stylusActions = RawStylusActions.Down; } if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseUpEvent) { MouseButtonEventArgs mouseButtonEventArgs = mouseEventArgs as MouseButtonEventArgs; if (mouseButtonEventArgs.ChangedButton != MouseButton.Left) return; stylusActions = RawStylusActions.Up; } timestamp = mouseEventArgs.Timestamp; Visual directlyOverVisual = mouseDevice.DirectlyOver as Visual; if ( directlyOverVisual == null ) { return; } // NTRAID:WINDOWSOS#1536432-2006/03/15-WAYNEZEN, // Take the presentation source which is associated to the directly over element. source = PresentationSource.CriticalFromVisual(directlyOverVisual); } PenContexts penContexts = GetPenContextsFromHwnd(source); if ((penContexts != null) && (source != null) && (source.CompositionTarget != null) && !source.CompositionTarget.IsDisposed) { IInputElement directlyOver = mouseDevice.DirectlyOver; int packetStatus = (leftButtonDown ? 1 : 0) | (rightButtonDown ? 9 : 0); // pen tip down == 1, barrel = 8 Point ptClient = mouseDevice.GetPosition(source.RootVisual as IInputElement); ptClient = source.CompositionTarget.TransformToDevice.Transform(ptClient); int buttons = (leftButtonDown ? 1 : 0) | (rightButtonDown ? 3 : 0); int [] data = {(int)ptClient.X, (int)ptClient.Y, packetStatus, buttons}; RawStylusInputReport inputReport = new RawStylusInputReport( InputMode.Foreground, timestamp, source, stylusActions, GetMousePointDescription, data); // Avoid re-entrancy due to lock() being used. using (Dispatcher.DisableProcessing()) { // Call plugins (does enter/leave and FireCustomData as well) _activeMousePlugInCollection = penContexts.InvokeStylusPluginCollectionForMouse(inputReport, directlyOver, _activeMousePlugInCollection); } } } }
internal void ProcessInput( RawStylusActions actions, PenContext penContext, int tabletDeviceId, int stylusDeviceId, int[] data, int timestamp, PresentationSource inputSource) { RawStylusInputReport inputReport = new RawStylusInputReport(InputMode.Foreground, timestamp, inputSource, penContext, actions, tabletDeviceId, stylusDeviceId, data); ProcessInputReport(inputReport); }
internal void InvokeStylusPluginCollection(RawStylusInputReport inputReport) { // Find PenContexts object for this inputReport. StylusPlugInCollection pic = null; // lock to make sure only one event is processed at a time and no changes to state can // be made until we finish routing this event. lock (__rtiLock) { switch (inputReport.Actions) { case RawStylusActions.Down: case RawStylusActions.Move: case RawStylusActions.Up: // Figure out current target plugincollection. pic = TargetPlugInCollection(inputReport); break; default: return; // Nothing to do unless one of the above events } WispStylusDevice stylusDevice = inputReport.StylusDevice.As <WispStylusDevice>(); StylusPlugInCollection currentPic = stylusDevice.CurrentNonVerifiedTarget; // Fire Leave event if we need to. if (currentPic != null && currentPic != pic) { // Create new RawStylusInput to send GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(_stylusLogic.GetTabletToViewTransform(stylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(currentPic.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. RawStylusInput tempRawStylusInput = new RawStylusInput(inputReport, transformTabletToView, currentPic); currentPic.FireEnterLeave(false, tempRawStylusInput, false); // Indicate we've used a stylus plugin _stylusLogic.Statistics.FeaturesUsed |= Tracing.StylusTraceLogger.FeatureFlags.StylusPluginsUsed; stylusDevice.CurrentNonVerifiedTarget = null; } if (pic != null) { // NOTE: PenContext info will not change (it gets rebuilt instead so keeping ref is fine) // The transformTabletToView matrix and plugincollection rects though can change based // off of layout events which is why we need to lock this. GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(_stylusLogic.GetTabletToViewTransform(stylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(pic.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. RawStylusInput rawStylusInput = new RawStylusInput(inputReport, transformTabletToView, pic); inputReport.RawStylusInput = rawStylusInput; if (pic != currentPic) { stylusDevice.CurrentNonVerifiedTarget = pic; pic.FireEnterLeave(true, rawStylusInput, false); } // We are on the pen thread, just call directly. pic.FireRawStylusInput(rawStylusInput); // Indicate we've used a stylus plugin _stylusLogic.Statistics.FeaturesUsed |= Tracing.StylusTraceLogger.FeatureFlags.StylusPluginsUsed; } } // lock(__rtiLock) }
private void GenerateInRange(RawStylusInputReport rawStylusInputReport) { StylusDevice stylusDevice = rawStylusInputReport.StylusDevice; RawStylusInputReport inputReport = new RawStylusInputReport(rawStylusInputReport.Mode, rawStylusInputReport.Timestamp, rawStylusInputReport.InputSource, rawStylusInputReport.PenContext, RawStylusActions.InRange, stylusDevice.TabletDevice.Id, stylusDevice.Id, rawStylusInputReport.Data); InputReportEventArgs input = new InputReportEventArgs(stylusDevice, inputReport); input.RoutedEvent=InputManager.PreviewInputReportEvent; _inputManager.Value.ProcessInput(input); }
internal abstract void UpdateEventStylusPoints(RawStylusInputReport report, bool resetIfNoOverride);
internal void InvokeStylusPluginCollection(RawStylusInputReport inputReport) { if (inputReport.StylusDevice != null) { inputReport.PenContext.Contexts.InvokeStylusPluginCollection(inputReport); } }
public void Synchronize() { // Simulate a stylus move (if we are current stylus, inrange, visuals still valid to update // and has moved). if (InRange && _inputSource != null && _inputSource.Value != null && _inputSource.Value.CompositionTarget != null && !_inputSource.Value.CompositionTarget.IsDisposed) { Point ptDevice = PointUtil.ScreenToClient(_lastScreenLocation, _inputSource.Value); // GlobalHitTest always returns an IInputElement, so we are sure to have one. IInputElement stylusOver = GlobalHitTest(_inputSource.Value, ptDevice); bool fOffsetChanged = false; if (_stylusOver == stylusOver) { Point ptOffset = GetPosition(stylusOver); fOffsetChanged = MS.Internal.DoubleUtil.AreClose(ptOffset.X, _rawElementRelativePosition.X) == false || MS.Internal.DoubleUtil.AreClose(ptOffset.Y, _rawElementRelativePosition.Y) == false; } if(fOffsetChanged || _stylusOver != stylusOver) { int timeStamp = Environment.TickCount; PenContext penContext = _stylusLogic.GetStylusPenContextForHwnd(_inputSource.Value,TabletDevice.Id); if (_eventStylusPoints != null && _eventStylusPoints.Count > 0 && StylusPointDescription.AreCompatible(penContext.StylusPointDescription, _eventStylusPoints.Description)) { StylusPoint stylusPoint = _eventStylusPoints[_eventStylusPoints.Count - 1]; int[] data = stylusPoint.GetPacketData(); // get back to the correct coordinate system Matrix m = TabletDevice.TabletToScreen; m.Invert(); Point ptTablet = ptDevice * m; data[0] = (int)ptTablet.X; data[1] = (int)ptTablet.Y; RawStylusInputReport report = new RawStylusInputReport(InputMode.Foreground, timeStamp, _inputSource.Value, penContext, InAir?RawStylusActions.InAirMove:RawStylusActions.Move, TabletDevice.Id, Id, data); report.Synchronized = true; InputReportEventArgs inputReportEventArgs = new InputReportEventArgs(this, report); inputReportEventArgs.RoutedEvent=InputManager.PreviewInputReportEvent; _stylusLogic.InputManagerProcessInputEventArgs(inputReportEventArgs); } } } }
bool IsValidStylusAction(RawStylusInputReport rawStylusInputReport) { bool allowEvent = true; StylusDevice stylusDevice = rawStylusInputReport.StylusDevice; // See if we have the correct PenContext we are receiving input from. We can get two // different PenContext objects actually sending us input simultaneously. We lock onto the // the first one we see come in range and keep locked on that till we see it go out of range. // If we go out of range and have overlapping inrange from another PenContext we will // force that PenContext to be the current one by forcing a InRange stylus event. // Now check for proper state of the device for the given Stylus event. switch (rawStylusInputReport.Actions) { case RawStylusActions.InRange: // only process inrange if currently out of range and the inputsource is not disposed. allowEvent = !stylusDevice.InRange && !rawStylusInputReport.InputSource.IsDisposed; break; case RawStylusActions.InAirMove: if (!stylusDevice.InRange && !rawStylusInputReport.InputSource.IsDisposed) { // Force InRange if stylus is out of range. Debug.Assert(stylusDevice.InAir); GenerateInRange(rawStylusInputReport); } else { // If InAir and either inputSource matches current devices input source or // the last down input source then it is OK to process and we can allow this. allowEvent = rawStylusInputReport.PenContext == stylusDevice.ActivePenContext; } break; case RawStylusActions.Down: if (!stylusDevice.InRange) { Debug.Assert(stylusDevice.InAir); GenerateInRange(rawStylusInputReport); } else { allowEvent = rawStylusInputReport.PenContext == stylusDevice.ActivePenContext; } break; case RawStylusActions.Move: allowEvent = rawStylusInputReport.PenContext == stylusDevice.ActivePenContext; break; case RawStylusActions.Up: allowEvent = rawStylusInputReport.PenContext == stylusDevice.ActivePenContext; break; case RawStylusActions.SystemGesture: allowEvent = rawStylusInputReport.PenContext == stylusDevice.ActivePenContext; if (allowEvent) { RawStylusSystemGestureInputReport systemGestureReport = (RawStylusSystemGestureInputReport)rawStylusInputReport; // If we see a Tap gesture that is sent when we are not in the down state then // ignore this (it's a double tap issue) since we will have generated one when // we see the up. if (systemGestureReport.SystemGesture == SystemGesture.Tap && stylusDevice.InAir) { allowEvent = false; } } break; case RawStylusActions.OutOfRange: allowEvent = rawStylusInputReport.PenContext == stylusDevice.ActivePenContext; break; } return allowEvent; }
internal void UpdateEventStylusPoints(RawStylusInputReport report, bool resetIfNoOverride) { if (report.RawStylusInput != null && report.RawStylusInput.StylusPointsModified) { GeneralTransform transformToElement = report.RawStylusInput.Target.ViewToElement.Inverse; //note that RawStylusInput.Target (of type StylusPluginCollection) //guarantees that ViewToElement is invertible Debug.Assert(transformToElement != null); _eventStylusPoints = report.RawStylusInput.GetStylusPoints(transformToElement); } else if (resetIfNoOverride) { _eventStylusPoints = new StylusPointCollection(report.StylusPointDescription, report.GetRawPacketData(), GetTabletToElementTransform(null), Matrix.Identity); } }
internal Point GetLastTabletPoint(RawStylusInputReport stylusInputReport) { var lastPoint = stylusInputReport.GetLastTabletPoint(); if (!_tabletToView.HasValue) { // _tabletToView = InputManager.Current.StylusLogic.GetTabletToViewTransform(this); } return _tabletToView.Value.Transform(lastPoint); }
internal SystemGesture? GenerateStaticGesture(RawStylusInputReport stylusInputReport) { if (_multiTouchSystemGestureLogic != null) { return _multiTouchSystemGestureLogic.GenerateStaticGesture(stylusInputReport); } return null; }
private void VerifyStylusPlugInCollectionTarget(RawStylusInputReport rawStylusInputReport) { switch (rawStylusInputReport.Actions) { case RawStylusActions.Down: case RawStylusActions.Move: case RawStylusActions.Up: break; default: return; // do nothing if not Down, Move or Up. } RawStylusInput originalRSI = rawStylusInputReport.RawStylusInput; // See if we have a plugin for the target of this input. StylusPlugInCollection targetPIC = null; StylusPlugInCollection targetRtiPIC = (originalRSI != null) ? originalRSI.Target : null; bool updateEventPoints = false; // Make sure we use UIElement for target if non NULL and hit ContentElement. UIElement newTarget = InputElement.GetContainingUIElement(rawStylusInputReport.StylusDevice.DirectlyOver as DependencyObject) as UIElement; if (newTarget != null) { targetPIC = rawStylusInputReport.PenContext.Contexts.FindPlugInCollection(newTarget); } // Make sure any lock() calls do not reenter on us. using(Dispatcher.DisableProcessing()) { // See if we hit the wrong PlugInCollection on the pen thread and clean things up if we did. if (targetRtiPIC != null && targetRtiPIC != targetPIC && originalRSI != null) { // Fire custom data not confirmed events for both pre and post since bad target... foreach (RawStylusInputCustomData customData in originalRSI.CustomDataList) { customData.Owner.FireCustomData(customData.Data, rawStylusInputReport.Actions, false); } updateEventPoints = originalRSI.StylusPointsModified; // Clear RawStylusInput data. rawStylusInputReport.RawStylusInput = null; } // See if we need to build up an RSI to send to the plugincollection (due to a mistarget). bool sendRawStylusInput = false; if (targetPIC != null && rawStylusInputReport.RawStylusInput == null) { // NOTE: PenContext info will not change (it gets rebuilt instead so keeping ref is fine) // The transformTabletToView matrix and plugincollection rects though can change based // off of layout events which is why we need to lock this. GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(GetTabletToViewTransform(rawStylusInputReport.StylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(targetPIC.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. RawStylusInput rawStylusInput = new RawStylusInput(rawStylusInputReport, transformTabletToView, targetPIC); rawStylusInputReport.RawStylusInput = rawStylusInput; sendRawStylusInput = true; } // Now fire the confirmed enter/leave events as necessary. StylusPlugInCollection currentTarget = rawStylusInputReport.StylusDevice.CurrentVerifiedTarget; if (targetPIC != currentTarget) { if (currentTarget != null) { // Fire leave event. If we never had a plugin for this event then create a temp one. if (originalRSI == null) { GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(GetTabletToViewTransform(rawStylusInputReport.StylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(currentTarget.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. originalRSI = new RawStylusInput(rawStylusInputReport, transformTabletToView, currentTarget); } currentTarget.FireEnterLeave(false, originalRSI, true); } if (targetPIC != null) { // Fire Enter event targetPIC.FireEnterLeave(true, rawStylusInputReport.RawStylusInput, true); } // Update the verified target. rawStylusInputReport.StylusDevice.CurrentVerifiedTarget = targetPIC; } // Now fire RawStylusInput if needed to the right plugincollection. if (sendRawStylusInput) { // We are on the pen thread, just call directly. targetPIC.FireRawStylusInput(rawStylusInputReport.RawStylusInput); updateEventPoints = (updateEventPoints || rawStylusInputReport.RawStylusInput.StylusPointsModified); } // Now fire PrePreviewCustomData events. if (targetPIC != null) { // Send custom data pre event foreach (RawStylusInputCustomData customData in rawStylusInputReport.RawStylusInput.CustomDataList) { customData.Owner.FireCustomData(customData.Data, rawStylusInputReport.Actions, true); } } // VerifyRawTarget might resend to correct plugins or may have hit the wrong plugincollection. The StylusPackets // may be overriden in those plugins so we need to call UpdateEventStylusPoints to update things. if (updateEventPoints) { rawStylusInputReport.StylusDevice.UpdateEventStylusPoints(rawStylusInputReport, true); } } }
private void GenerateGesture(RawStylusInputReport rawStylusInputReport, SystemGesture gesture) { StylusDevice stylusDevice = rawStylusInputReport.StylusDevice; System.Diagnostics.Debug.Assert(stylusDevice != null); RawStylusSystemGestureInputReport inputReport = new RawStylusSystemGestureInputReport( InputMode.Foreground, rawStylusInputReport.Timestamp, rawStylusInputReport.InputSource, rawStylusInputReport.PenContext, rawStylusInputReport.TabletDeviceId, rawStylusInputReport.StylusDeviceId, gesture, 0, // Gesture X location (only used for flicks) 0, // Gesture Y location (only used for flicks) 0); // ButtonState (only used for flicks) inputReport.StylusDevice = stylusDevice; InputReportEventArgs input = new InputReportEventArgs(stylusDevice, inputReport); input.RoutedEvent=InputManager.PreviewInputReportEvent; // Process this directly instead of doing a push. We want this event to get // to the user before the StylusUp and MouseUp event. InputManagerProcessInputEventArgs(input); }
internal void UpdateState(RawStylusInputReport report) { Debug.Assert(report.TabletDeviceId == _tabletDevice.Id); Debug.Assert((report.Actions & RawStylusActions.None) == 0); _eventStylusPoints = new StylusPointCollection( report.StylusPointDescription, report.GetRawPacketData(), GetTabletToElementTransform(null), Matrix.Identity); PresentationSource inputSource = DetermineValidSource(report.InputSource, _eventStylusPoints, report.PenContext.Contexts); // See if we need to remap the stylus data X and Y values to different presentation source. if (inputSource != null && inputSource != report.InputSource) { Point newWindowLocation = PointUtil.ClientToScreen(new Point(0, 0), inputSource); newWindowLocation = _stylusLogic.MeasureUnitsFromDeviceUnits(newWindowLocation); Point oldWindowLocation = _stylusLogic.MeasureUnitsFromDeviceUnits(report.PenContext.Contexts.DestroyedLocation); // Create translate matrix transform to shift coords to map points to new window location. MatrixTransform additionalTransform = new MatrixTransform(new Matrix(1, 0, 0, 1, oldWindowLocation.X - newWindowLocation.X, oldWindowLocation.Y - newWindowLocation.Y)); _eventStylusPoints = _eventStylusPoints.Reformat(report.StylusPointDescription, additionalTransform); } _rawPosition = _eventStylusPoints[_eventStylusPoints.Count - 1]; _inputSource = new SecurityCriticalDataClass<PresentationSource>(inputSource); if (inputSource != null) { // Update our screen position from this move. Point pt = _stylusLogic.DeviceUnitsFromMeasureUnits((Point)_rawPosition); _lastScreenLocation = PointUtil.ClientToScreen(pt, inputSource); } // If we are not blocked from updating the location we want to use for the // promoted mouse location then update it. We set this flag in the post process phase // of Stylus events (after they have fired). if (!_fBlockMouseMoveChanges) { _lastMouseScreenLocation = _lastScreenLocation; } if ((report.Actions & RawStylusActions.Down) != 0 || (report.Actions & RawStylusActions.Move) != 0) { _fInAir = false; // Keep the stylus down location for turning system gestures into mouse event if ((report.Actions & RawStylusActions.Down) != 0) { _needToSendMouseDown = true; // reset the gesture flag. This is used to determine if we will need to fabricate a systemgesture tap on the // corresponding up event. _fGestureWasFired = false; _fDetectedDrag = false; _seenHoldEnterGesture = false; // Make sure our drag and move deltas are up to date. TabletDevice.UpdateSizeDeltas(report.StylusPointDescription, _stylusLogic); } // See if we need to do our own Drag detection (on Stylus Move event) else if (inputSource != null && _fBlockMouseMoveChanges && _seenDoubleTapGesture && !_fGestureWasFired && !_fDetectedDrag) { Size delta = TabletDevice.CancelSize; // We use the first point of the packet data for Drag detection to try and // filter out cases where the stylus skips when going down. Point dragPosition =(Point)_eventStylusPoints[0]; dragPosition = _stylusLogic.DeviceUnitsFromMeasureUnits(dragPosition); dragPosition = PointUtil.ClientToScreen(dragPosition, inputSource); // See if we need to detect a Drag gesture. If so do the calculation. if ((Math.Abs(_lastMouseScreenLocation.X - dragPosition.X) > delta.Width) || (Math.Abs(_lastMouseScreenLocation.Y - dragPosition.Y) > delta.Height)) { _fDetectedDrag = true; } } } UpdateEventStylusPoints(report, false); if ((report.Actions & RawStylusActions.Up) != 0 || (report.Actions & RawStylusActions.InAirMove) != 0) { _fInAir = true; if ((report.Actions & RawStylusActions.Up) != 0) { _sawMouseButton1Down = false; // reset this on Stylus Up. } } }
internal StylusPlugInCollection TargetPlugInCollection(RawStylusInputReport inputReport) { // Caller must make call to this routine inside of lock(__rtiLock)! StylusPlugInCollection pic = null; // We should only be called when not on the application thread! System.Diagnostics.Debug.Assert(!inputReport.StylusDevice.CheckAccess()); // We're on the pen thread so can't touch visual tree. Use capturedPlugIn (if capture on) or cached rects. bool elementHasCapture = false; pic = inputReport.StylusDevice.GetCapturedPlugInCollection(ref elementHasCapture); int pointLength = inputReport.PenContext.StylusPointDescription.GetInputArrayLengthPerPoint(); // Make sure that the captured Plugin Collection is still in the list. CaptureChanges are // deferred so there is a window where the stylus device is not updated yet. This protects us // from using a bogus plugin collecton for an element that is in an invalid state. if (elementHasCapture && !_plugInCollectionList.Contains(pic)) { elementHasCapture = false; // force true hittesting to be done! } if (!elementHasCapture && inputReport.Data != null && inputReport.Data.Length >= pointLength) { int[] data = inputReport.Data; System.Diagnostics.Debug.Assert(data.Length % pointLength == 0); Point ptTablet = new Point(data[data.Length - pointLength], data[data.Length - pointLength + 1]); // Note: the StylusLogic data inside DeviceUnitsFromMeasurUnits is protected by __rtiLock. ptTablet = ptTablet * inputReport.StylusDevice.TabletDevice.TabletToScreen; ptTablet.X = (int)Math.Round(ptTablet.X); // Make sure we snap to whole window pixels. ptTablet.Y = (int)Math.Round(ptTablet.Y); ptTablet = _stylusLogic.MeasureUnitsFromDeviceUnits(ptTablet); // change to measured units now. pic = HittestPlugInCollection(ptTablet); // Use cached rectangles for UIElements. } return pic; }
internal StylusPlugInCollection InvokeStylusPluginCollectionForMouse(RawStylusInputReport inputReport, IInputElement directlyOver, StylusPlugInCollection currentPlugInCollection) { StylusPlugInCollection newPlugInCollection = null; // lock to make sure only one event is processed at a time and no changes to state can // be made until we finish routing this event. lock(__rtiLock) { //Debug.Assert(inputReport.Actions == RawStylusActions.Down || // inputReport.Actions == RawStylusActions.Up || // inputReport.Actions == RawStylusActions.Move); // Find new target plugin collection if (directlyOver != null) { UIElement uiElement = InputElement.GetContainingUIElement(directlyOver as DependencyObject) as UIElement; if (uiElement != null) { newPlugInCollection = FindPlugInCollection(uiElement); } } // Fire Leave event to old pluginCollection if we need to. if (currentPlugInCollection != null && currentPlugInCollection != newPlugInCollection) { // NOTE: input report points for mouse are in avalon measured units and not device! RawStylusInput tempRawStylusInput = new RawStylusInput(inputReport, currentPlugInCollection.ViewToElement, currentPlugInCollection); currentPlugInCollection.FireEnterLeave(false, tempRawStylusInput, true); } if (newPlugInCollection != null) { // NOTE: input report points for mouse are in avalon measured units and not device! RawStylusInput rawStylusInput = new RawStylusInput(inputReport, newPlugInCollection.ViewToElement, newPlugInCollection); inputReport.RawStylusInput = rawStylusInput; if (newPlugInCollection != currentPlugInCollection) { newPlugInCollection.FireEnterLeave(true, rawStylusInput, true); } // We are on the pen thread, just call directly. newPlugInCollection.FireRawStylusInput(rawStylusInput); // Fire custom data events (always confirmed for mouse) foreach (RawStylusInputCustomData customData in rawStylusInput.CustomDataList) { customData.Owner.FireCustomData(customData.Data, inputReport.Actions, true); } } } return newPlugInCollection; }
void ProcessInputReport(RawStylusInputReport inputReport) { // First, assign the StylusDevice (note it may still be null for new StylusDevice) inputReport.StylusDevice = FindStylusDeviceWithLock(inputReport.StylusDeviceId); // Only call plugins if we are not in a drag drop operation and the HWND is enabled! if (!_inDragDrop || !inputReport.PenContext.Contexts.IsWindowDisabled) { // Handle real time input (call StylusPlugIns) InvokeStylusPluginCollection(inputReport); } // ETW event indicating that a stylus input report was queued. EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, EventTrace.Event.StylusEventQueued, inputReport.StylusDeviceId); // Queue up new event. lock(_stylusEventQueueLock) { _queueStylusEvents.Enqueue(inputReport); } // post the args into dispatcher queue Dispatcher.BeginInvoke(DispatcherPriority.Input, _dlgInputManagerProcessInput, null); }
internal void InvokeStylusPluginCollection(RawStylusInputReport inputReport) { // Find PenContexts object for this inputReport. StylusPlugInCollection pic = null; // lock to make sure only one event is processed at a time and no changes to state can // be made until we finish routing this event. lock(__rtiLock) { switch (inputReport.Actions) { case RawStylusActions.Down: case RawStylusActions.Move: case RawStylusActions.Up: // Figure out current target plugincollection. pic = TargetPlugInCollection(inputReport); break; default: return; // Nothing to do unless one of the above events } StylusPlugInCollection currentPic = inputReport.StylusDevice.CurrentNonVerifiedTarget; // Fire Leave event if we need to. if (currentPic != null && currentPic != pic) { // Create new RawStylusInput to send GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(_stylusLogic.GetTabletToViewTransform(inputReport.StylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(currentPic.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. RawStylusInput tempRawStylusInput = new RawStylusInput(inputReport, transformTabletToView, currentPic); currentPic.FireEnterLeave(false, tempRawStylusInput, false); inputReport.StylusDevice.CurrentNonVerifiedTarget = null; } if (pic != null) { // NOTE: PenContext info will not change (it gets rebuilt instead so keeping ref is fine) // The transformTabletToView matrix and plugincollection rects though can change based // off of layout events which is why we need to lock this. GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(_stylusLogic.GetTabletToViewTransform(inputReport.StylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(pic.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. RawStylusInput rawStylusInput = new RawStylusInput(inputReport, transformTabletToView, pic); inputReport.RawStylusInput = rawStylusInput; if (pic != currentPic) { inputReport.StylusDevice.CurrentNonVerifiedTarget = pic; pic.FireEnterLeave(true, rawStylusInput, false); } // We are on the pen thread, just call directly. pic.FireRawStylusInput(rawStylusInput); } } // lock(__rtiLock) }
void PromoteRawToPreview(RawStylusInputReport report, ProcessInputEventArgs e) { RoutedEvent routedEvent = GetPreviewEventFromRawStylusActions(report.Actions); if (routedEvent != null && report.StylusDevice != null && !report.StylusDevice.IgnoreStroke) { StylusEventArgs args; if (routedEvent != Stylus.PreviewStylusSystemGestureEvent) { if (routedEvent == Stylus.PreviewStylusDownEvent) { args = new StylusDownEventArgs(report.StylusDevice, report.Timestamp); } else { args = new StylusEventArgs(report.StylusDevice, report.Timestamp); } } else { RawStylusSystemGestureInputReport reportSg = (RawStylusSystemGestureInputReport)report; args = new StylusSystemGestureEventArgs(report.StylusDevice, report.Timestamp, reportSg.SystemGesture, reportSg.GestureX, reportSg.GestureY, reportSg.ButtonState); } args.InputReport = report; args.RoutedEvent= routedEvent; e.PushInput(args, e.StagingItem); } }