/// <summary> /// Retrieves the StylusDevice associated with the cursor id. /// </summary> /// <param name="cursorId">The id of the StylusDevice to retrieve</param> /// <returns>The StylusDevice associated with the id</returns> internal PointerStylusDevice GetStylusByCursorId(uint cursorId) { PointerStylusDevice stylus = null; _stylusDeviceMap.TryGetValue(cursorId, out stylus); return(stylus); }
/// <summary> /// Retrieve the StylusDevice associated with the cursor id /// </summary> /// <param name="cursorId">The cursor id</param> /// <returns>The StylusDevice associated with the device id</returns> internal PointerStylusDevice GetStylusDeviceByCursorId(uint cursorId) { PointerStylusDevice stylus = null; foreach (var tablet in _tabletDeviceMap.Values) { if ((stylus = tablet.GetStylusByCursorId(cursorId)) != null) { break; } } return(stylus); }
internal PointerFlickEngine(PointerStylusDevice stylusDevice) { _stylusDevice = stylusDevice; _timePeriod = 8; _timePeriodAlpha = .001; _collectingData = false; _analyzingData = false; _previousFlickDataValid = false; _allowPressFlicks = true; Reset(); SetTolerance(.5); }
/// <summary> /// Initializes the interaction engine /// </summary> /// <param name="stylusDevice"></param> /// <param name="configuration"></param> internal PointerInteractionEngine(PointerStylusDevice stylusDevice, List <UnsafeNativeMethods.INTERACTION_CONTEXT_CONFIGURATION> configuration = null) { _stylusDevice = stylusDevice; // Only create a flick engine for Pen devices if (_stylusDevice.TabletDevice.Type == TabletDeviceType.Stylus) { // Currently disabled pending decision about flick support in Windows 10 RS3 //_flickEngine = new PointerFlickEngine(_stylusDevice); } // Create our interaction context for gesture recognition IntPtr interactionContext = IntPtr.Zero; UnsafeNativeMethods.CreateInteractionContext(out interactionContext); _interactionContext = new SecurityCriticalDataForSet <IntPtr>(interactionContext); if (configuration == null) { configuration = DefaultConfiguration; } if (_interactionContext.Value != IntPtr.Zero) { // We do not want to filter specific pointers UnsafeNativeMethods.SetPropertyInteractionContext(_interactionContext.Value, UnsafeNativeMethods.INTERACTION_CONTEXT_PROPERTY.INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, Convert.ToUInt32(false)); // Use screen measurements here as this makes certain math easier for us UnsafeNativeMethods.SetPropertyInteractionContext(_interactionContext.Value, UnsafeNativeMethods.INTERACTION_CONTEXT_PROPERTY.INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS, (UInt32)UnsafeNativeMethods.InteractionMeasurementUnits.Screen); // Configure the context UnsafeNativeMethods.SetInteractionConfigurationInteractionContext(_interactionContext.Value, (uint)configuration.Count, configuration.ToArray()); // Store the delegate so it can be accessed over time _callbackDelegate = Callback; // Register for interaction notifications UnsafeNativeMethods.RegisterOutputCallbackInteractionContext(_interactionContext.Value, _callbackDelegate); } }
private void BuildStylusDevices() { UInt32 cursorCount = 0; List <PointerStylusDevice> pointerStylusDevices = new List <PointerStylusDevice>(); if (UnsafeNativeMethods.GetPointerDeviceCursors(_deviceInfo.Device, ref cursorCount, null)) { UnsafeNativeMethods.POINTER_DEVICE_CURSOR_INFO[] cursors = new UnsafeNativeMethods.POINTER_DEVICE_CURSOR_INFO[cursorCount]; if (UnsafeNativeMethods.GetPointerDeviceCursors(_deviceInfo.Device, ref cursorCount, cursors)) { foreach (var cursor in cursors) { PointerStylusDevice stylus = new PointerStylusDevice(this, cursor); _stylusDeviceMap.Add(stylus.CursorId, stylus); pointerStylusDevices.Add(stylus); } } } _stylusDevices = new StylusDeviceCollection(pointerStylusDevices.ToArray()); }
/// <summary> /// Target a plugin collection that either has capture or passes a hit test. /// raw input. /// </summary> /// <param name="inputReport">The raw input to process</param> /// <returns>The appropriate collection target</returns> internal StylusPlugInCollection TargetPlugInCollection(RawStylusInputReport inputReport) { StylusPlugInCollection pic = null; PointerStylusDevice stylusDevice = inputReport.StylusDevice.As <PointerStylusDevice>(); bool elementHasCapture = false; pic = stylusDevice.GetCapturedPlugInCollection(ref elementHasCapture); int pointLength = inputReport.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 *= inputReport.InputSource.CompositionTarget.TransformFromDevice; // change to measured units now. pic = HittestPlugInCollection(ptTablet); // Use cached rectangles for UIElements. } return(pic); }
internal PointerTouchDevice(PointerStylusDevice stylusDevice) : base(stylusDevice) { _stylusDevice = stylusDevice; }
/// <summary> /// Calls the appropriate plugin collection for the raw input. /// </summary> /// <remarks> /// This would normally be called on the RTI thread. As we do not have one right now, we just call this in the /// Hwnd input provider in order to keep similar semantics to how the WISP stack accomplishes this. When the RTI is /// re-implemented, we need to ensure this gets called from there exclusively. /// </remarks> internal void InvokeStylusPluginCollection(RawStylusInputReport inputReport) { // Find PenContexts object for this inputReport. StylusPlugInCollection pic = null; 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 } PointerStylusDevice stylusDevice = inputReport.StylusDevice.As <PointerStylusDevice>(); StylusPlugInCollection currentPic = stylusDevice.CurrentVerifiedTarget; // 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(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.CurrentStylusLogic.Statistics.FeaturesUsed |= Tracing.StylusTraceLogger.FeatureFlags.StylusPluginsUsed; stylusDevice.CurrentVerifiedTarget = null; } if (pic != null) { // NOTE: Comment not applicable without RTI. 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(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.CurrentVerifiedTarget = pic; pic.FireEnterLeave(true, rawStylusInput, false); } // Send the input pic.FireRawStylusInput(rawStylusInput); // Indicate we've used a stylus plugin StylusLogic.CurrentStylusLogic.Statistics.FeaturesUsed |= Tracing.StylusTraceLogger.FeatureFlags.StylusPluginsUsed; } }
/// <summary> /// Verifies that the call to the stylus plugin on the RTI thread was valid against the current visual tree. /// If this verification fails, we will rebuild and resend the raw input and custom data in order to inform /// the target collections of the error and fix it. /// </summary> /// <remarks> /// This is a remnant of verifying hits to the collections based on the UI thread calling this /// after the real time input thread has previously called a collection. This is due to the RTI thread /// not being 100% synchronous with the visual tree in terms of hitting the collections. This is sort of /// redundant right now, but needs to be here when we re-implement the RTI. /// </remarks> internal 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. } PointerLogic pointerLogic = StylusLogic.GetCurrentStylusLogicAs <PointerLogic>(); 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 = FindPlugInCollection(newTarget); } // Make sure any lock() calls do not reenter on us. using (Dispatcher.CurrentDispatcher.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: (This applies when RTI is back in) 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(rawStylusInputReport.StylusDevice.As <PointerStylusDevice>().GetTabletToElementTransform(null)); // 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; } PointerStylusDevice stylusDevice = rawStylusInputReport.StylusDevice.As <PointerStylusDevice>(); // Now fire the confirmed enter/leave events as necessary. StylusPlugInCollection currentTarget = 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(stylusDevice.GetTabletToElementTransform(null)); // 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); // Indicate we've used a stylus plugin pointerLogic.Statistics.FeaturesUsed |= Tracing.StylusTraceLogger.FeatureFlags.StylusPluginsUsed; } // Update the verified target. 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); // Indicate we've used a stylus plugin pointerLogic.Statistics.FeaturesUsed |= Tracing.StylusTraceLogger.FeatureFlags.StylusPluginsUsed; } // 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.As <PointerStylusDevice>().UpdateEventStylusPoints(rawStylusInputReport, true); } } }