internal HwndStylusInputProvider(HwndSource source) { InputManager inputManager = InputManager.Current; StylusLogic stylusLogic = inputManager.StylusLogic; IntPtr sourceHandle; (new UIPermission(PermissionState.Unrestricted)).Assert(); try //Blessed Assert this is for RegisterInputManager and RegisterHwndforinput { // Register ourselves as an input provider with the input manager. _site = new SecurityCriticalDataClass <InputProviderSite>(inputManager.RegisterInputProvider(this)); sourceHandle = source.Handle; } finally { UIPermission.RevertAssert(); } stylusLogic.RegisterHwndForInput(inputManager, source); _source = new SecurityCriticalDataClass <HwndSource>(source); _stylusLogic = new SecurityCriticalDataClass <StylusLogic>(stylusLogic); // Enables multi-touch input UnsafeNativeMethods.SetProp(new HandleRef(this, sourceHandle), "MicrosoftTabletPenServiceProperty", new HandleRef(null, new IntPtr(MultiTouchEnabledFlag))); }
internal PointerStylusDevice(PointerTabletDevice tabletDevice, UnsafeNativeMethods.POINTER_DEVICE_CURSOR_INFO cursorInfo) { _cursorInfo = cursorInfo; _tabletDevice = tabletDevice; _pointerLogic = StylusLogic.GetCurrentStylusLogicAs <PointerLogic>(); // Touch devices have a special set of handling code if (tabletDevice.Type == TabletDeviceType.Touch) { TouchDevice = new PointerTouchDevice(this); } _interactionEngine = new PointerInteractionEngine(this); _interactionEngine.InteractionDetected += HandleInteraction; List <StylusButton> buttons = new List <StylusButton>(); // Create a button collection for this StylusDevice based off the button properties stored in the tablet // This needs to be done as each button instance has a StylusDevice owner that it uses to access the raw // data in the StylusDevice. foreach (var prop in _tabletDevice.DeviceInfo.StylusPointProperties) { if (prop.IsButton) { StylusButton button = new StylusButton(StylusPointPropertyIds.GetStringRepresentation(prop.Id), prop.Id); button.SetOwner(this); buttons.Add(button); } } _stylusButtons = new StylusButtonCollection(buttons); }
internal override void UpdateState(UIElement element) { bool unhookPenContexts = true; // Disable processing of the queue during blocking operations to prevent unrelated reentrancy // which a call to Lock() can cause. using (element.Dispatcher.DisableProcessing()) { // See if we should be enabled if (element.IsVisible && element.IsEnabled && element.IsHitTestVisible) { PresentationSource presentationSource = PresentationSource.CriticalFromVisual(element as Visual); if (presentationSource != null) { unhookPenContexts = false; // Are we currently hooked up? If not then hook up. if (_penContexts == null) { InputManager inputManager = (InputManager)element.Dispatcher.InputManager; PenContexts penContexts = StylusLogic.GetCurrentStylusLogicAs <WispLogic>().GetPenContextsFromHwnd(presentationSource); // _penContexts must be non null or don't do anything. if (penContexts != null) { _penContexts = penContexts; lock (penContexts.SyncRoot) { penContexts.AddStylusPlugInCollection(Wrapper); foreach (StylusPlugIn spi in Wrapper) { spi.InvalidateIsActiveForInput(); // Uses _penContexts being set to determine active state. } // NTRAID:WINDOWSOS#1677277-2006/06/05-WAYNEZEN, // Normally the Rect will be updated when we receive the LayoutUpdate. // However there could be a race condition which the LayoutUpdate gets received // before the properties like IsVisible being set. // So we should always force to call OnLayoutUpdated whenever the input is active. Wrapper.OnLayoutUpdated(this.Wrapper, EventArgs.Empty); } } } } } if (unhookPenContexts) { Unhook(); } } }
/// <summary> /// Creates a new input provider for a particular source that handles WM_POINTER messages /// </summary> /// <param name="source">The source to handle messages for</param> internal HwndPointerInputProvider(HwndSource source) { _site = new SecurityCriticalDataClass <InputProviderSite>(InputManager.Current.RegisterInputProvider(this)); _source = new SecurityCriticalDataClass <HwndSource>(source); _pointerLogic = new SecurityCriticalDataClass <PointerLogic>(StylusLogic.GetCurrentStylusLogicAs <PointerLogic>()); // Register the stylus plugin manager _pointerLogic.Value.PlugInManagers[_source.Value] = new PointerStylusPlugInManager(_source.Value); // Store if this window is enabled or disabled int style = MS.Win32.UnsafeNativeMethods.GetWindowLong(new HandleRef(this, source.CriticalHandle), MS.Win32.NativeMethods.GWL_STYLE); IsWindowEnabled = (style & MS.Win32.NativeMethods.WS_DISABLED) == 0; }
internal override void UpdateState(UIElement element) { bool unhook = true; // See if we should be enabled if (element.IsVisible && element.IsEnabled && element.IsHitTestVisible) { PresentationSource presentationSource = PresentationSource.CriticalFromVisual(element as Visual); if (presentationSource != null) { unhook = false; // Are we currently hooked up? If not then hook up. if (_manager == null) { _manager = StylusLogic.GetCurrentStylusLogicAs <PointerLogic>().PlugInManagers[presentationSource]; // _manager must be non null or don't do anything. if (_manager != null) { _manager.AddStylusPlugInCollection(Wrapper); foreach (StylusPlugIn spi in Wrapper) { spi.InvalidateIsActiveForInput(); } // // Normally the Rect will be updated when we receive the LayoutUpdate. // However there could be a race condition which the LayoutUpdate gets received // before the properties like IsVisible being set. // So we should always force to call OnLayoutUpdated whenever the input is active. Wrapper.OnLayoutUpdated(this.Wrapper, EventArgs.Empty); } } } } if (unhook) { Unhook(); } }
///////////////////////////////////////////////////////////////////// internal HwndStylusInputProvider(HwndSource source) { InputManager inputManager = InputManager.Current; _stylusLogic = new SecurityCriticalDataClass <WispLogic>(StylusLogic.GetCurrentStylusLogicAs <WispLogic>()); IntPtr sourceHandle; // Register ourselves as an input provider with the input manager. _site = new SecurityCriticalDataClass <InputProviderSite>(inputManager.RegisterInputProvider(this)); sourceHandle = source.Handle; _stylusLogic.Value.RegisterHwndForInput(inputManager, source); _source = new SecurityCriticalDataClass <HwndSource>(source); // Enables multi-touch input UnsafeNativeMethods.SetProp(new HandleRef(this, sourceHandle), "MicrosoftTabletPenServiceProperty", new HandleRef(null, new IntPtr(MultiTouchEnabledFlag))); }
internal void SynchronizeReverseInheritPropertyFlags(DependencyObject oldParent, bool isCoreParent) { if (IsKeyboardFocusWithin) { Keyboard.PrimaryDevice.ReevaluateFocusAsync(this, oldParent, isCoreParent); } // Reevelauate the stylus properties first to guarentee that our property change // notifications fire before mouse properties. if (IsStylusOver) { StylusLogic.CurrentStylusLogicReevaluateStylusOver(this, oldParent, isCoreParent); } if (IsStylusCaptureWithin) { StylusLogic.CurrentStylusLogicReevaluateCapture(this, oldParent, isCoreParent); } if (IsMouseOver) { Mouse.PrimaryDevice.ReevaluateMouseOver(this, oldParent, isCoreParent); } if (IsMouseCaptureWithin) { Mouse.PrimaryDevice.ReevaluateCapture(this, oldParent, isCoreParent); } if (AreAnyTouchesOver) { TouchDevice.ReevaluateDirectlyOver(this, oldParent, isCoreParent); } if (AreAnyTouchesCapturedWithin) { TouchDevice.ReevaluateCapturedWithin(this, oldParent, isCoreParent); } }
internal WispTabletDeviceCollection() { WispLogic stylusLogic = StylusLogic.GetCurrentStylusLogicAs <WispLogic>(); bool enabled = stylusLogic.Enabled; if (!enabled) { enabled = ShouldEnableTablets(); } // If enabled or we are a tabletpc (vista sets dynamically if digitizers present) then enable the pen! if (enabled) { UpdateTablets(); // Create the tablet device collection! // Enable stylus input on all hwnds if we have not yet done so. if (!stylusLogic.Enabled) { stylusLogic.EnableCore(); } } }
internal IntPtr FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero; // It is possible to be re-entered during disposal. Just return. if (null == _source || null == _source.Value) { return(result); } switch (msg) { case WindowMessage.WM_ENABLE: _stylusLogic.Value.OnWindowEnableChanged(hwnd, (int)NativeMethods.IntPtrToInt32(wParam) == 0); break; case WindowMessage.WM_TABLET_QUERYSYSTEMGESTURESTATUS: handled = true; NativeMethods.POINT pt1 = new NativeMethods.POINT( NativeMethods.SignedLOWORD(lParam), NativeMethods.SignedHIWORD(lParam)); SafeNativeMethods.ScreenToClient(new HandleRef(this, hwnd), pt1); Point ptClient1 = new Point(pt1.x, pt1.y); IInputElement inputElement = StylusDevice.LocalHitTest(_source.Value, ptClient1); if (inputElement != null) { // walk up the parent chain DependencyObject elementCur = (DependencyObject)inputElement; bool isPressAndHoldEnabled = Stylus.GetIsPressAndHoldEnabled(elementCur); bool isFlicksEnabled = Stylus.GetIsFlicksEnabled(elementCur); bool isTapFeedbackEnabled = Stylus.GetIsTapFeedbackEnabled(elementCur); bool isTouchFeedbackEnabled = Stylus.GetIsTouchFeedbackEnabled(elementCur); uint flags = 0; if (!isPressAndHoldEnabled) { flags |= TABLET_PRESSANDHOLD_DISABLED; } if (!isTapFeedbackEnabled) { flags |= TABLET_TAPFEEDBACK_DISABLED; } if (isTouchFeedbackEnabled) { flags |= TABLET_TOUCHUI_FORCEON; } else { flags |= TABLET_TOUCHUI_FORCEOFF; } if (!isFlicksEnabled) { flags |= TABLET_FLICKS_DISABLED; } result = new IntPtr(flags); } break; case WindowMessage.WM_TABLET_FLICK: handled = true; int flickData = NativeMethods.IntPtrToInt32(wParam); // We always handle any scroll actions if we are enabled. We do this when we see the SystemGesture Flick come through. // Note: Scrolling happens on window flicked on even if it is not the active window. if (_stylusLogic != null && _stylusLogic.Value.Enabled && (StylusLogic.GetFlickAction(flickData) == 1)) { result = new IntPtr(0x0001); // tell UIHub the flick has already been handled. } break; } if (handled && EventTrace.IsEnabled(EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info)) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientInputMessage, EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, (_source.Value.CompositionTarget != null ? _source.Value.CompositionTarget.Dispatcher.GetHashCode() : 0), hwnd.ToInt64(), msg, (int)wParam, (int)lParam); } return(result); }
internal WispStylusTouchDevice(StylusDeviceBase stylusDevice) : base(stylusDevice) { _stylusLogic = StylusLogic.GetCurrentStylusLogicAs <WispLogic>(); PromotingToOther = true; }
/// <summary> /// Used in order to allow StylusPlugins to affect mouse inputs as well as touch. /// </summary> internal static void InvokePlugInsForMouse(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?.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.UnsecureCurrent.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; } // // Take the presentation source which is associated to the directly over element. source = PresentationSource.CriticalFromVisual(directlyOverVisual); } if ((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, () => { return(MousePointDescription); }, 0, 0, data); PointerStylusPlugInManager manager = StylusLogic.GetCurrentStylusLogicAs <PointerLogic>()?.GetManagerForSource(source); // Avoid re-entrancy due to lock() being used. using (Dispatcher.CurrentDispatcher.DisableProcessing()) { // Call plugins (does enter/leave and FireCustomData as well) _activeMousePlugInCollection = manager?.InvokeStylusPluginCollectionForMouse(inputReport, directlyOver, _activeMousePlugInCollection); } } } }
/// <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); } } }