public void Mutated_Event_Should_Be_Raised_With_Nested_ProcessInput() { bool calledWithMutated = false; PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; InputManager.Current.ProcessInput(mutated); } else if (ev.StagingItem.Input.RoutedEvent == InputElement.MutatedEvent) { calledWithMutated = true; } }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsTrue(element.RawEventRaised); Assert.IsTrue(element.MutatedEventRaised); Assert.IsTrue(calledWithMutated); }
public void PreNotifyInput_Should_Not_Be_Called_If_Cancelled() { bool notified = false; PreProcessInputEventHandler preProcess = (sender, ev) => { ev.Cancel(); }; NotifyInputEventHandler preNotify = (sender, ev) => { notified = true; }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.PreNotifyInput += preNotify; InputManager.Current.ProcessInput(e); InputManager.Current.PreNotifyInput -= preNotify; InputManager.Current.PreProcessInput -= preProcess; Assert.IsFalse(notified); }
public void ProcessInput_Should_Return_True_If_One_Event_Handled() { PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; StagingAreaInputItem stagingItem = CreateStagingItem(mutated); ev.PushInput(stagingItem); } }; InputElement element = new InputElement(); element.SetMutatedEventHandled = true; TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; bool result = InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; // This goes against the documentation which says should return true if all events handled. Assert.IsTrue(element.RawEventRaised); Assert.IsTrue(element.MutatedEventRaised); Assert.IsTrue(result); }
public void ProcessInput_Should_Return_False_If_No_Events_Handled() { PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; StagingAreaInputItem stagingItem = CreateStagingItem(mutated); ev.PushInput(stagingItem); } }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; bool result = InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsFalse(result); }
public void PreProcess_Event_Should_Be_Called_With_Mutated_Event() { bool calledWithMutated = false; PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; StagingAreaInputItem stagingItem = CreateStagingItem(mutated); ev.PushInput(stagingItem); ev.Cancel(); } else if (ev.StagingItem.Input.RoutedEvent == InputElement.MutatedEvent) { calledWithMutated = true; } }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsTrue(calledWithMutated); }
public void Mutated_Event_Should_Still_Be_Raised_When_Raw_Event_Cancelled() { PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; StagingAreaInputItem stagingItem = CreateStagingItem(mutated); ev.PushInput(stagingItem); ev.Cancel(); } }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsFalse(element.RawEventRaised); Assert.IsTrue(element.MutatedEventRaised); }
/// <summary> /// To prevent contaminating the system with test side effects /// (basically, to prevent our MockKeyboardDevice from listening and reacting to real /// keyboard input), we need to undo the damage done by the /// base KeyboardDevice's constructor where it subscribes to some events. /// By unsubscribing from these events we make sure our KeyboardDevice /// won't respond to real input and will be used solely to provide the /// desired state of the Shift, Alt and Ctrl modifier keys when asked for. /// </summary> private MockKeyboardDevice() : base(InputManager.Current) { // There's only one global input manager in the system InputManager inputManager = InputManager.Current; // First, unsubscribe our 3 handlers from the InputManager. // 1) // Undo the effect of the following line in the KeyboardDevice ctor: // this._inputManager.Value.PreProcessInput += new PreProcessInputEventHandler(this.PreProcessInput); PreProcessInputEventHandler preProcessInputHandler = GetDelegateForMethod <PreProcessInputEventHandler>(this, "PreProcessInput"); inputManager.PreProcessInput -= preProcessInputHandler; // 2) // Undo the effect of the following line in the KeyboardDevice ctor: // this._inputManager.Value.PreNotifyInput += new NotifyInputEventHandler(this.PreNotifyInput); NotifyInputEventHandler preNotifyInputHandler = GetDelegateForMethod <NotifyInputEventHandler>(this, "PreNotifyInput"); inputManager.PreNotifyInput -= preNotifyInputHandler; // 3) // Undo the effect of the following line in the KeyboardDevice ctor: // this._inputManager.Value.PostProcessInput += new ProcessInputEventHandler(this.PostProcessInput); ProcessInputEventHandler postProcessInputHandler = GetDelegateForMethod <ProcessInputEventHandler>(this, "PostProcessInput"); inputManager.PostProcessInput -= postProcessInputHandler; // Now undo the damage done by creating a new TextServicesManager // this._TsfManager = new SecurityCriticalDataClass<TextServicesManager>(new TextServicesManager(inputManager)); var textServicesManager = this.GetFieldValue("_TsfManager").GetFieldValue("_value"); // this._inputManager.PreProcessInput += new PreProcessInputEventHandler(this.PreProcessInput); preProcessInputHandler = GetDelegateForMethod <PreProcessInputEventHandler>(textServicesManager, "PreProcessInput"); inputManager.PreProcessInput -= preProcessInputHandler; // this._inputManager.PostProcessInput += new ProcessInputEventHandler(this.PostProcessInput); postProcessInputHandler = GetDelegateForMethod <ProcessInputEventHandler>(textServicesManager, "PostProcessInput"); inputManager.PostProcessInput -= postProcessInputHandler; // And finally, revert the changes done by creating a new TextCompositionManager // this._textcompositionManager = new SecurityCriticalData<TextCompositionManager>(new TextCompositionManager(inputManager)); var textCompositionManager = this.GetFieldValue("_textcompositionManager").GetFieldValue("_value"); // this._inputManager.PreProcessInput += new PreProcessInputEventHandler(this.PreProcessInput); preProcessInputHandler = GetDelegateForMethod <PreProcessInputEventHandler>(textCompositionManager, "PreProcessInput"); inputManager.PreProcessInput -= preProcessInputHandler; // his._inputManager.PostProcessInput += new ProcessInputEventHandler(this.PostProcessInput); postProcessInputHandler = GetDelegateForMethod <ProcessInputEventHandler>(textCompositionManager, "PostProcessInput"); inputManager.PostProcessInput -= postProcessInputHandler; }
public void Event_Should_Not_Be_Routed_If_Cancelled() { PreProcessInputEventHandler preProcess = (sender, ev) => { ev.Cancel(); }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsFalse(element.RawEventRaised); }
public void PreProcessInput_Should_Be_Called() { bool called = false; PreProcessInputEventHandler preProcess = (sender, ev) => { called = true; }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsTrue(called); }
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); }