private RawKeyboardInputReport ExtractRawKeyboardInputReport(NotifyInputEventArgs e, RoutedEvent Event) { RawKeyboardInputReport keyboardInput = null; InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; if (input != null) { if (input.Report.Type == InputType.Keyboard && input.RoutedEvent == Event) { keyboardInput = input.Report as RawKeyboardInputReport; } } return(keyboardInput); }
private RawKeyboardActions GetNonRedundantActions(NotifyInputEventArgs e) { RawKeyboardActions actions; // 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 = (RawKeyboardActions)o; } else { actions = new RawKeyboardActions(); } return(actions); }
public bool ProcessInput(InputEventArgs input) { StagingAreaInputItem stagingItem = new StagingAreaInputItem(input); PreProcessInputEventArgs preProcessArgs = new PreProcessInputEventArgs(this, stagingItem); if (PreProcessInput != null) { PreProcessInput(this, preProcessArgs); } NotifyInputEventArgs notifyArgs = new NotifyInputEventArgs(this, stagingItem); if (PreNotifyInput != null) { PreNotifyInput(this, notifyArgs); } #if notyet if (!preProcessArgs.Canceled) { /* XXX route the event */; } #endif if (PostNotifyInput != null) { PostNotifyInput(this, notifyArgs); } if (PostProcessInput != null) { ProcessInputEventArgs processArgs = new ProcessInputEventArgs(this, stagingItem); PostProcessInput(this, processArgs); } return(true); }
private void PreNotifyInput(object sender, NotifyInputEventArgs e) { RawKeyboardInputReport keyboardInput = ExtractRawKeyboardInputReport(e, InputManager.PreviewInputReportEvent); if(keyboardInput != null) { CheckForDisconnectedFocus(); // Activation // // MITIGATION: KEYBOARD_STATE_OUT_OF_[....] // // It is very important that we allow multiple activate events. // This is how we deal with the fact that Win32 sometimes sends // us a WM_SETFOCUS message BEFORE it has updated it's internal // internal keyboard state information. When we get the // WM_SETFOCUS message, we activate the keyboard with the // keyboard state (even though it could be wrong). Then when // we get the first "real" keyboard input event, we activate // the keyboard again, since Win32 will have updated the // keyboard state correctly by then. // if((keyboardInput.Actions & RawKeyboardActions.Activate) == RawKeyboardActions.Activate) { //if active source is null, no need to do special-case handling if(_activeSource == null) { // we are now active. _activeSource = new SecurityCriticalDataClass<PresentationSource>(keyboardInput.InputSource); } else if(_activeSource.Value != keyboardInput.InputSource) { IKeyboardInputProvider toDeactivate = _activeSource.Value.GetInputProvider(typeof(KeyboardDevice)) as IKeyboardInputProvider; // we are now active. _activeSource = new SecurityCriticalDataClass<PresentationSource>(keyboardInput.InputSource); if(toDeactivate != null) { toDeactivate.NotifyDeactivate(); } } } // Generally, we need to check against redundant actions. // We never prevet 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. // If the input is reporting a key down, the action is never // considered redundant. if((keyboardInput.Actions & RawKeyboardActions.KeyDown) == RawKeyboardActions.KeyDown) { RawKeyboardActions actions = GetNonRedundantActions(e); actions |= RawKeyboardActions.KeyDown; e.StagingItem.SetData(_tagNonRedundantActions, actions); // Pass along the key that was pressed, and update our state. Key key = KeyInterop.KeyFromVirtualKey(keyboardInput.VirtualKey); e.StagingItem.SetData(_tagKey, key); e.StagingItem.SetData(_tagScanCode, new ScanCode(keyboardInput.ScanCode, keyboardInput.IsExtendedKey)); // Tell the InputManager that the MostRecentDevice is us. if(_inputManager!=null) _inputManager.Value.MostRecentInputDevice = this; } // if((keyboardInput.Actions & RawKeyboardActions.KeyUp) == RawKeyboardActions.KeyUp) { RawKeyboardActions actions = GetNonRedundantActions(e); actions |= RawKeyboardActions.KeyUp; e.StagingItem.SetData(_tagNonRedundantActions, actions); // Pass along the key that was pressed, and update our state. Key key = KeyInterop.KeyFromVirtualKey(keyboardInput.VirtualKey); e.StagingItem.SetData(_tagKey, key); e.StagingItem.SetData(_tagScanCode, new ScanCode(keyboardInput.ScanCode, keyboardInput.IsExtendedKey)); // Tell the InputManager that the MostRecentDevice is us. if(_inputManager!=null) _inputManager.Value.MostRecentInputDevice = this; } } // On KeyDown, we might need to set the Repeat flag if(e.StagingItem.Input.RoutedEvent == Keyboard.PreviewKeyDownEvent) { CheckForDisconnectedFocus(); KeyEventArgs args = (KeyEventArgs) e.StagingItem.Input; // Is this the same as the previous key? (Look at the real key, e.g. TextManager // might have changed args.Key it to Key.TextInput.) if (_previousKey == args.RealKey) { // Yes, this is a repeat (we got the keydown for it twice, with no KeyUp in between) args.SetRepeat(true); } // Otherwise, keep this key to check against next time. else { _previousKey = args.RealKey; args.SetRepeat(false); } } // On KeyUp, we clear Repeat flag else if(e.StagingItem.Input.RoutedEvent == Keyboard.PreviewKeyUpEvent) { CheckForDisconnectedFocus(); KeyEventArgs args = (KeyEventArgs) e.StagingItem.Input; args.SetRepeat(false); // Clear _previousKey, so that down/up/down/up doesn't look like a repeat _previousKey = Key.None; } }
private RawKeyboardActions GetNonRedundantActions(NotifyInputEventArgs e) { RawKeyboardActions actions; // 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 = (RawKeyboardActions) o; } else { actions = new RawKeyboardActions(); } return actions; }
private RawKeyboardInputReport ExtractRawKeyboardInputReport(NotifyInputEventArgs e, RoutedEvent Event) { RawKeyboardInputReport keyboardInput = null; InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; if(input != null) { if(input.Report.Type == InputType.Keyboard && input.RoutedEvent == Event) { keyboardInput = input.Report as RawKeyboardInputReport; } } return keyboardInput; }
private bool ProcessStagingArea() { bool handled = false; // For performance reasons, try to reuse the input event args. // If we are reentrered, we have to start over with fresh event // args, so we clear the member variables before continuing. // Also, we cannot simply make an single instance of the // PreProcessedInputEventArgs and cast it to NotifyInputEventArgs // or ProcessInputEventArgs because a malicious user could upcast // the object and call inappropriate methods. NotifyInputEventArgs notifyInputEventArgs = (_notifyInputEventArgs != null) ? _notifyInputEventArgs : new NotifyInputEventArgs(); ProcessInputEventArgs processInputEventArgs = (_processInputEventArgs != null) ? _processInputEventArgs : new ProcessInputEventArgs(); PreProcessInputEventArgs preProcessInputEventArgs = (_preProcessInputEventArgs != null) ? _preProcessInputEventArgs : new PreProcessInputEventArgs(); _notifyInputEventArgs = null; _processInputEventArgs = null; _preProcessInputEventArgs = null; // Because we can be reentered, we can't just enumerate over the // staging area - that could throw an exception if the queue // changes underneath us. Instead, just loop until we find a // frame marker or until the staging area is empty. StagingAreaInputItem item = null; while ((item = PopInput()) != null) { // If we found a marker, we have reached the end of a // "section" of the staging area. We just return from // the synchronous processing of the staging area. // If a dispatcher frame has been pushed by someone, this // will not return to the original ProcessInput. Instead // it will unwind to the dispatcher and since we have // already pushed a work item to continue processing the // input, it will simply call back into us to do more // processing. At which point we will continue to drain // the staging area. This could cause strage behavior, // but it is deemed more acceptable than stalling input // processing. // In the future, in ordre to // make sure we all agree on this. We could also // just delay the rest of the staging area until // the dispatcher frame finishes. Unfortunately, // this means one could receive an input event for // something that happened a long time ago. if (item.IsMarker) { break; } // Pre-Process the input. This could modify the staging // area, and it could cancel the processing of this // input event. // // Because we use multi-cast delegates, we always have to // create a new multi-cast delegate when we add or remove // a handler. This means we can just call the current // multi-cast delegate instance, and it is safe to iterate // over, even if we get reentered. if (_preProcessInput != null) { preProcessInputEventArgs.Reset(item, this); // Invoke the handlers in reverse order so that handlers that // users add are invoked before handlers in the system. Delegate[] handlers = _preProcessInput.GetInvocationList(); for (int i = (handlers.Length - 1); i >= 0; i--) { PreProcessInputEventHandler handler = (PreProcessInputEventHandler)handlers[i]; handler(this, preProcessInputEventArgs); } } if (!preProcessInputEventArgs.Canceled) { // Pre-Notify the input. // // Because we use multi-cast delegates, we always have to // create a new multi-cast delegate when we add or remove // a handler. This means we can just call the current // multi-cast delegate instance, and it is safe to iterate // over, even if we get reentered. if (_preNotifyInput != null) { notifyInputEventArgs.Reset(item, this); // Invoke the handlers in reverse order so that handlers that // users add are invoked before handlers in the system. Delegate[] handlers = _preNotifyInput.GetInvocationList(); for (int i = (handlers.Length - 1); i >= 0; i--) { NotifyInputEventHandler handler = (NotifyInputEventHandler)handlers[i]; handler(this, notifyInputEventArgs); } } // Raise the input event being processed. InputEventArgs input = item.Input; // Some input events are explicitly associated with // an element. Those that are not are associated with // the target of the input device for this event. DependencyObject eventSource = input.Source as DependencyObject; if (eventSource == null || !InputElement.IsValid(eventSource as IInputElement)) { if (input.Device != null) { eventSource = input.Device.Target as DependencyObject; } } // During synchronized input processing, event should be discarded if not listening for this input type. if (_isSynchronizedInput && SynchronizedInputHelper.IsMappedEvent(input) && Array.IndexOf(SynchronizedInputEvents, input.RoutedEvent) < 0 && Array.IndexOf(PairedSynchronizedInputEvents, input.RoutedEvent) < 0) { if (!SynchronizedInputHelper.ShouldContinueListening(input)) { // Discard the event _synchronizedInputState = SynchronizedInputStates.Discarded; SynchronizedInputHelper.RaiseAutomationEvents(); CancelSynchronizedInput(); } else { _synchronizedInputAsyncClearOperation = Dispatcher.BeginInvoke((Action) delegate { // Discard the event _synchronizedInputState = SynchronizedInputStates.Discarded; SynchronizedInputHelper.RaiseAutomationEvents(); CancelSynchronizedInput(); }, DispatcherPriority.Background); } } else { if (eventSource != null) { if (InputElement.IsUIElement(eventSource)) { UIElement e = (UIElement)eventSource; e.RaiseEvent(input, true); // Call the "trusted" flavor of RaiseEvent. } else if (InputElement.IsContentElement(eventSource)) { ContentElement ce = (ContentElement)eventSource; ce.RaiseEvent(input, true);// Call the "trusted" flavor of RaiseEvent. } else if (InputElement.IsUIElement3D(eventSource)) { UIElement3D e3D = (UIElement3D)eventSource; e3D.RaiseEvent(input, true); // Call the "trusted" flavor of RaiseEvent } // If synchronized input raise appropriate automation event. if (_isSynchronizedInput && SynchronizedInputHelper.IsListening(_listeningElement, input)) { if (!SynchronizedInputHelper.ShouldContinueListening(input)) { SynchronizedInputHelper.RaiseAutomationEvents(); CancelSynchronizedInput(); } else { _synchronizedInputAsyncClearOperation = Dispatcher.BeginInvoke((Action) delegate { SynchronizedInputHelper.RaiseAutomationEvents(); CancelSynchronizedInput(); }, DispatcherPriority.Background); } } } } // Post-Notify the input. // // Because we use multi-cast delegates, we always have to // create a new multi-cast delegate when we add or remove // a handler. This means we can just call the current // multi-cast delegate instance, and it is safe to iterate // over, even if we get reentered. if (_postNotifyInput != null) { notifyInputEventArgs.Reset(item, this); // Invoke the handlers in reverse order so that handlers that // users add are invoked before handlers in the system. Delegate[] handlers = _postNotifyInput.GetInvocationList(); for (int i = (handlers.Length - 1); i >= 0; i--) { NotifyInputEventHandler handler = (NotifyInputEventHandler)handlers[i]; handler(this, notifyInputEventArgs); } } // Post-Process the input. This could modify the staging // area. // // Because we use multi-cast delegates, we always have to // create a new multi-cast delegate when we add or remove // a handler. This means we can just call the current // multi-cast delegate instance, and it is safe to iterate // over, even if we get reentered. if (_postProcessInput != null) { processInputEventArgs.Reset(item, this); RaiseProcessInputEventHandlers(_postProcessInput, processInputEventArgs); // PreviewInputReport --> InputReport if (item.Input.RoutedEvent == InputManager.PreviewInputReportEvent) { if (!item.Input.Handled) { InputReportEventArgs previewInputReport = (InputReportEventArgs)item.Input; InputReportEventArgs inputReport = new InputReportEventArgs(previewInputReport.Device, previewInputReport.Report); inputReport.RoutedEvent = InputManager.InputReportEvent; PushInput(inputReport, item); } } } if (input.Handled) { handled = true; } } } // Store our input event args so that we can use them again, and // avoid having to allocate more. _notifyInputEventArgs = notifyInputEventArgs; _processInputEventArgs = processInputEventArgs; _preProcessInputEventArgs = preProcessInputEventArgs; // Make sure to throw away the contents of the event args so // we don't keep refs around to things we don't mean to. _notifyInputEventArgs.Reset(null, null); _processInputEventArgs.Reset(null, null); _preProcessInputEventArgs.Reset(null, null); return(handled); }
private void InputManager_PostNotifyInput(object sender, NotifyInputEventArgs e) { if (this.IsPointerButtonEventItem(e.StagingItem)) { this.handlingPointerButtonEvent = false; } }
private void PreProcessInput( object sender, NotifyInputEventArgs e ) { if( e.StagingItem.Input is KeyEventArgs ) { this.UpdateKeyModifierTriggerProperties(); } }
private void PreNotifyInput(object sender, NotifyInputEventArgs e) { RawKeyboardInputReport keyboardInput = ExtractRawKeyboardInputReport(e, InputManager.PreviewInputReportEvent); if (keyboardInput != null) { CheckForDisconnectedFocus(); // Activation // // MITIGATION: KEYBOARD_STATE_OUT_OF_SYNC // // It is very important that we allow multiple activate events. // This is how we deal with the fact that Win32 sometimes sends // us a WM_SETFOCUS message BEFORE it has updated it's internal // internal keyboard state information. When we get the // WM_SETFOCUS message, we activate the keyboard with the // keyboard state (even though it could be wrong). Then when // we get the first "real" keyboard input event, we activate // the keyboard again, since Win32 will have updated the // keyboard state correctly by then. // if ((keyboardInput.Actions & RawKeyboardActions.Activate) == RawKeyboardActions.Activate) { //if active source is null, no need to do special-case handling if (_activeSource == null) { // we are now active. _activeSource = new SecurityCriticalDataClass <PresentationSource>(keyboardInput.InputSource); } else if (_activeSource.Value != keyboardInput.InputSource) { IKeyboardInputProvider toDeactivate = _activeSource.Value.GetInputProvider(typeof(KeyboardDevice)) as IKeyboardInputProvider; // we are now active. _activeSource = new SecurityCriticalDataClass <PresentationSource>(keyboardInput.InputSource); if (toDeactivate != null) { toDeactivate.NotifyDeactivate(); } } } // Generally, we need to check against redundant actions. // We never prevet 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. // If the input is reporting a key down, the action is never // considered redundant. if ((keyboardInput.Actions & RawKeyboardActions.KeyDown) == RawKeyboardActions.KeyDown) { RawKeyboardActions actions = GetNonRedundantActions(e); actions |= RawKeyboardActions.KeyDown; e.StagingItem.SetData(_tagNonRedundantActions, actions); // Pass along the key that was pressed, and update our state. Key key = KeyInterop.KeyFromVirtualKey(keyboardInput.VirtualKey); e.StagingItem.SetData(_tagKey, key); e.StagingItem.SetData(_tagScanCode, new ScanCode(keyboardInput.ScanCode, keyboardInput.IsExtendedKey)); // Tell the InputManager that the MostRecentDevice is us. if (_inputManager != null) { _inputManager.Value.MostRecentInputDevice = this; } } // We are missing detection for redundant ups if ((keyboardInput.Actions & RawKeyboardActions.KeyUp) == RawKeyboardActions.KeyUp) { RawKeyboardActions actions = GetNonRedundantActions(e); actions |= RawKeyboardActions.KeyUp; e.StagingItem.SetData(_tagNonRedundantActions, actions); // Pass along the key that was pressed, and update our state. Key key = KeyInterop.KeyFromVirtualKey(keyboardInput.VirtualKey); e.StagingItem.SetData(_tagKey, key); e.StagingItem.SetData(_tagScanCode, new ScanCode(keyboardInput.ScanCode, keyboardInput.IsExtendedKey)); // Tell the InputManager that the MostRecentDevice is us. if (_inputManager != null) { _inputManager.Value.MostRecentInputDevice = this; } } } // On KeyDown, we might need to set the Repeat flag if (e.StagingItem.Input.RoutedEvent == Keyboard.PreviewKeyDownEvent) { CheckForDisconnectedFocus(); KeyEventArgs args = (KeyEventArgs)e.StagingItem.Input; // Is this the same as the previous key? (Look at the real key, e.g. TextManager // might have changed args.Key it to Key.TextInput.) if (_previousKey == args.RealKey) { // Yes, this is a repeat (we got the keydown for it twice, with no KeyUp in between) args.SetRepeat(true); } // Otherwise, keep this key to check against next time. else { _previousKey = args.RealKey; args.SetRepeat(false); } } // On KeyUp, we clear Repeat flag else if (e.StagingItem.Input.RoutedEvent == Keyboard.PreviewKeyUpEvent) { CheckForDisconnectedFocus(); KeyEventArgs args = (KeyEventArgs)e.StagingItem.Input; args.SetRepeat(false); // Clear _previousKey, so that down/up/down/up doesn't look like a repeat _previousKey = Key.None; } }
private void PlayOrPause4SpaceKey(object sender, NotifyInputEventArgs e) { if (this.OwnedWindows.Count != 0) { return; } if (e.StagingItem.Input.RoutedEvent != Keyboard.KeyDownEvent) return; var args = e.StagingItem.Input as KeyEventArgs; if (args == null || args.Key != Key.Space) { return; } args.Handled = true; PlayOrPauseOrOpen(); }
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<PresentationSource>(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<PresentationSource>(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(true, ptClient, _inputSource.Value, out mouseOver, out rawMouseOver); } else { LocalHitTest(true, 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(true, ptClient, _inputSource.Value); } else { mouseOver = LocalHitTest(true, 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(true, 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; } } } }
void inputManager_PreNotifyInput(object sender, NotifyInputEventArgs e) { if (e.StagingItem.Input.RoutedEvent == PreviewRawInputEvent) { RawMultitouchReport report = e.StagingItem.Input as RawMultitouchReport; if (report != null && !report.Handled) { report.Context.OverElement = GetTargetElement(report.Context.Contact.Position, report.Context.Root, report); Contact contact; if (!report.CleanUp) { if (ContactsManager.ExistingContacts.TryGetValue(report.Context.Contact.Id, out contact)) contact.InputArgs = report; else { contact = new Contact(report); ContactsManager.ExistingContacts[report.Context.Contact.Id] = contact; } } else { contact = ContactsManager.ExistingContacts[report.Context.Contact.Id]; contact.InputArgs = report; report.Handled = true; } UIElementsList mergedList; report.Context.ElementsList = BuildElementsList(report, report.Context.OverElement, report.Context.ElementsList, out mergedList); if(mergedList != null) RaiseEnterLeave(contact, mergedList); if(report.CleanUp) { if (report.Captured != null) contact.Capture(null); ContactsManager.ExistingContacts.Remove(contact.Id); } } } Debug.Assert(e.StagingItem.Input.RoutedEvent != null, "routed event null"); }
public bool ProcessInput (InputEventArgs input) { StagingAreaInputItem stagingItem = new StagingAreaInputItem (input); PreProcessInputEventArgs preProcessArgs = new PreProcessInputEventArgs(this, stagingItem); if (PreProcessInput != null) PreProcessInput (this, preProcessArgs); NotifyInputEventArgs notifyArgs = new NotifyInputEventArgs(this, stagingItem); if (PreNotifyInput != null) PreNotifyInput (this, notifyArgs); #if notyet if (!preProcessArgs.Canceled) /* XXX route the event */; #endif if (PostNotifyInput != null) PostNotifyInput (this, notifyArgs); if (PostProcessInput != null) { ProcessInputEventArgs processArgs = new ProcessInputEventArgs (this, stagingItem); PostProcessInput (this, processArgs); } return true; }
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.Stylus) { RawStylusInputReport rawStylusInputReport = (RawStylusInputReport) inputReportEventArgs.Report; StylusDevice stylusDevice = rawStylusInputReport.StylusDevice; if (stylusDevice != null) { // update stylus device state (unless this is exclusively system gesture or // in-range/out-of-range event - which don't carry much info) switch (rawStylusInputReport.Actions) { case RawStylusActions.SystemGesture: stylusDevice.UpdateStateForSystemGesture( (RawStylusSystemGestureInputReport)rawStylusInputReport); break; case RawStylusActions.OutOfRange: _lastInRangeTime = Environment.TickCount; stylusDevice.UpdateInRange(false, rawStylusInputReport.PenContext); UpdateIsStylusInRange(false); break; case RawStylusActions.InRange: _lastInRangeTime = Environment.TickCount; stylusDevice.UpdateInRange(true, rawStylusInputReport.PenContext); stylusDevice.UpdateState(rawStylusInputReport); UpdateIsStylusInRange(true); _lastRawMouseAction = RawMouseActions.None; // make sure we promote a mouse move on next event. break; default: // InAirMove, Down, Move, Up go through here. stylusDevice.UpdateState(rawStylusInputReport); break; } // Can only update Over state if not in a DragDrop operation!! if (!_inDragDrop && !rawStylusInputReport.PenContext.Contexts.IsWindowDisabled && !stylusDevice.IgnoreStroke) { Point position = stylusDevice.GetRawPosition(null); position = DeviceUnitsFromMeasureUnits(position); // change back to device coords. IInputElement target = stylusDevice.FindTarget(stylusDevice.CriticalActiveSource, position); SelectStylusDevice(stylusDevice, target, true); } else { SelectStylusDevice(stylusDevice, null, false); // don't update over. } // If this is a stylus down and we don't have a valid target then the stylus went down // on the wrong window (a transparent window handling bug in wisptis). In this case // we want to ignore all stylus input until after the next stylus up. if (rawStylusInputReport.Actions == RawStylusActions.Down && stylusDevice.Target == null) { stylusDevice.IgnoreStroke = true; } // Tell the InputManager that the MostRecentDevice is us. _inputManager.Value.MostRecentInputDevice = stylusDevice; // Verify that we sent the real time stylus events to the proper plugincollection. VerifyStylusPlugInCollectionTarget(rawStylusInputReport); } } } // During the PreviewStylusDown event, we update the tap count, if there are // multiple "quick" taps in approximately the "same" location (as defined // by the hosting environment, aka the registry). if(e.StagingItem.Input.RoutedEvent == Stylus.PreviewStylusDownEvent) { StylusEventArgs stylusDownEventArgs = e.StagingItem.Input as StylusDownEventArgs; StylusDevice stylusDevice = stylusDownEventArgs.StylusDevice; if (stylusDevice != null) { Point ptClient = stylusDevice.GetRawPosition(null); // determine barrel state... bool bBarrelPressed = false; int barrelPos = stylusDevice.TabletDevice.StylusPointDescription.GetButtonBitPosition(StylusPointProperties.BarrelButton); if (barrelPos != -1 && stylusDevice.StylusButtons[barrelPos].StylusButtonState == StylusButtonState.Down) { bBarrelPressed = true; } Point pPixelPoint = DeviceUnitsFromMeasureUnits(ptClient); Point pLastPixelPoint = DeviceUnitsFromMeasureUnits(stylusDevice.LastTapPoint); // How long since the last click? (deals with tickcount wrapping too) // Here's some info on how this works... // int.MaxValue - int.MinValue = -1 (subtracting any negative # from MaxValue keeps this negative) // int.MinValue - int.MaxValue = 1 (subtracting any positive # from MinValue keeps this positive) // So as the values get farther apart from MaxInt and MinInt the difference grows which is what we want. // We use Abs to ensure if we get older time coming through here (not expected) we'll do better // at filtering it out if delta is greater than the double tap time. We should always see // MinInt - MaxInt which will produce a positive number when wrapping happens. int timeSpan = Math.Abs(unchecked(stylusDownEventArgs.Timestamp - stylusDevice.LastTapTime)); // Is the delta coordinates of this tap close enough to the last tap? Size doubleTapSize = stylusDevice.TabletDevice.DoubleTapSize; bool isSameSpot = (Math.Abs(pPixelPoint.X - pLastPixelPoint.X) < doubleTapSize.Width) && (Math.Abs(pPixelPoint.Y - pLastPixelPoint.Y) < doubleTapSize.Height); // Now check everything to see if this is a multi-click. if (timeSpan < _doubleTapDeltaTime && isSameSpot && (bBarrelPressed == stylusDevice.LastTapBarrelDown) ) { // Yes, increment the count stylusDevice.TapCount++; } else { // No, not a multi-click, reset everything. stylusDevice.TapCount = 1; stylusDevice.LastTapPoint = new Point(ptClient.X, ptClient.Y); stylusDevice.LastTapTime = stylusDownEventArgs.Timestamp; stylusDevice.LastTapBarrelDown = bBarrelPressed; } // Make sure to update the mouse location on stylus down. ProcessMouseMove(stylusDevice, stylusDownEventArgs.Timestamp, false); } } }