/// <summary> /// Pushes an input event onto the top of the staging area. /// </summary> /// <param name="input"> /// The input event to place on the staging area. This may not /// be null, and may not already exist in the staging area. /// </param> /// <param name="promote"> /// An existing staging area item to promote the state from. /// </param> /// <returns> /// The staging area input item that wraps the specified input. /// </returns> public StagingAreaInputItem PushInput(InputEventArgs input, StagingAreaInputItem promote) // Note: this should be a bool, and always use the InputItem available on these args. { StagingAreaInputItem item = new StagingAreaInputItem(input, promote); InputManager.CurrentInputManager._currentStagingStack.Push(item); return item; }
internal StagingAreaInputItem(InputEventArgs input, StagingAreaInputItem promote) { Input = input; if (promote != null && promote._table != null) { // REFACTOR -- need a hashtable! _table = (Hashtable)promote._table.Clone(); } }
internal StagingAreaInputItem(bool isMarker, InputEventArgs input, StagingAreaInputItem promote) { IsMarker = isMarker; Input = input; if (promote != null && promote._keys != null) { // REFACTOR -- need a hashtable! _keys = (ArrayList)promote._keys.Clone(); _values = (ArrayList)promote._values.Clone(); } }
/// <summary> /// Synchronously processes the specified input. /// </summary> /// <remarks> /// The specified input is processed by all of the filters and /// monitors, and is finally dispatched to the appropriate /// element as an input event. /// </remarks> /// <returns> /// Whether or not any event generated as a consequence of this /// event was handled. /// </returns> public bool ProcessInput(InputEventArgs input) { VerifyAccess(); if (input == null) { throw new ArgumentNullException("input"); } // Push a marker indicating the portion of the staging area // that needs to be processed. Stack stk = new Stack(); // Push the input to be processed onto the staging area. stk.Push(new StagingAreaInputItem(input, null)); _stagingArea.Enqueue(stk); // Post a work item to continue processing the staging area // in case someone pushes a dispatcher frame in the middle // of input processing. DispatcherFrame frame = Dispatcher.CurrentFrame; if (frame != null) { if (!_frameStagingArea.Contains(frame)) { _frameStagingArea.Add(frame); Dispatcher.BeginInvoke(_continueProcessingStagingAreaCallback, frame); } } return true; }
/// <summary> /// Synchronously processes the specified input. /// </summary> /// <remarks> /// The specified input is processed by all of the filters and /// monitors, and is finally dispatched to the appropriate /// element as an input event. /// </remarks> /// <returns> /// Whether or not any event generated as a consequence of this /// event was handled. /// </returns> public bool ProcessInput(InputEventArgs input) { VerifyAccess(); if (input == null) { throw new ArgumentNullException("input"); } // Push a marker indicating the portion of the staging area // that needs to be processed. _stagingArea.Push(new StagingAreaInputItem(true, null, null)); // Push the input to be processed onto the staging area. _stagingArea.Push(new StagingAreaInputItem(false, input, null)); // Post a work item to continue processing the staging area // in case someone pushes a dispatcher frame in the middle // of input processing. if (!_continueProcessingStagingArea) { Dispatcher.BeginInvoke(_continueProcessingStagingAreaCallback, true); _continueProcessingStagingArea = true; } // Now drain the staging area up to the marker we pushed. bool handled = (bool)ProcessStagingArea(false); return handled; }
private object ProcessStagingArea(object postContinue) { bool handled = false; // NOTE -- avalon caches the XXXEventArgs. In our system, the costs are different, // so it is probably cheaper for us to just create them, since everything gets created // on the heap anyways, and IL is expensive. we should reconsider this if // its a performance impact. // 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. _continueProcessingStagingArea = true; try { while (_stagingArea.Count > 0) { _currentStagingStack = _stagingArea.Dequeue() as Stack; do { StagingAreaInputItem item = (StagingAreaInputItem)_currentStagingStack.Pop(); // Pre-Process the input. This could modify the staging // area, and it could cancel the processing of this // input event. // bool fCanceled = false; int devType = (int)item.Input._inputDevice.DeviceType; if (InputDeviceEvents[devType]._preProcessInput != null) { PreProcessInputEventArgs preProcessInputEventArgs; InputDeviceEvents[devType]._preProcessInput(this, preProcessInputEventArgs = new PreProcessInputEventArgs(item)); fCanceled = preProcessInputEventArgs._canceled; } if (!fCanceled) { // Pre-Notify the input. // if (InputDeviceEvents[devType]._preNotifyInput != null) { InputDeviceEvents[devType]._preNotifyInput(this, new NotifyInputEventArgs(item)); } // Raise the input event being processed. InputEventArgs input = item.Input; // Some input events are explicitly associated with // an element. Those that are not instead are associated with // the target of the input device for this event. UIElement eventSource = input._source as UIElement; if (eventSource == null && input._inputDevice != null) { eventSource = input._inputDevice.Target; } if (eventSource != null) { eventSource.RaiseEvent(input); } // Post-Notify the input. // if (InputDeviceEvents[devType]._postNotifyInput != null) { InputDeviceEvents[devType]._postNotifyInput(this, new NotifyInputEventArgs(item)); } // Post-Process the input. This could modify the staging // area. if (InputDeviceEvents[devType]._postProcessInput != null) { InputDeviceEvents[devType]._postProcessInput(this, new ProcessInputEventArgs(item)); } // 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; _currentStagingStack.Push(new StagingAreaInputItem(inputReport, item)); } } if (input.Handled) { handled = true; } } }while(_currentStagingStack.Count > 0); } } finally { _continueProcessingStagingArea = false; // It is possible that we can be re-entered by a nested // dispatcher frame. Continue processing the staging // area if we need to. if (_stagingArea.Count > 0) { _continueProcessingStagingArea = true; // Before we actually start to drain the staging area, we need // to post a work item to process more input. This enables us // to process more input if we enter a nested pump. Dispatcher.BeginInvoke(_continueProcessingStagingAreaCallback, true); } } return(handled); }
private object ProcessStagingArea(object postContinue) { bool handled = false; // NOTE -- avalon caches the XXXEventArgs. In our system, the costs are different, // so it is probably cheaper for us to just create them, since everything gets created // on the heap anyways, and IL is expensive. we should reconsider this if // its a performance impact. // 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. while (_stagingArea.Count > 0) { StagingAreaInputItem item = (StagingAreaInputItem)_stagingArea.Pop(); // 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. // 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 you 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. // bool fCanceled = false; int devType = (int)item.Input._inputDevice.DeviceType; if (InputDeviceEvents[devType]._preProcessInput != null) { PreProcessInputEventArgs preProcessInputEventArgs; InputDeviceEvents[devType]._preProcessInput(this, preProcessInputEventArgs = new PreProcessInputEventArgs(item)); fCanceled = preProcessInputEventArgs._canceled; } if (!fCanceled) { // Pre-Notify the input. // if (InputDeviceEvents[devType]._preNotifyInput != null) { InputDeviceEvents[devType]._preNotifyInput(this, new NotifyInputEventArgs(item)); } // Raise the input event being processed. InputEventArgs input = item.Input; // Some input events are explicitly associated with // an element. Those that are not instead are associated with // the target of the input device for this event. UIElement eventSource = input._source as UIElement; if (eventSource == null && input._inputDevice != null) { eventSource = input._inputDevice.Target; } if (eventSource != null) { eventSource.RaiseEvent(input); } // Post-Notify the input. // if (InputDeviceEvents[devType]._postNotifyInput != null) { InputDeviceEvents[devType]._postNotifyInput(this, new NotifyInputEventArgs(item)); } // Post-Process the input. This could modify the staging // area. if (InputDeviceEvents[devType]._postProcessInput != null) { InputDeviceEvents[devType]._postProcessInput(this, new ProcessInputEventArgs(item)); // 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; _stagingArea.Push(new StagingAreaInputItem(false, inputReport, item)); } } } if (input.Handled) { handled = true; } } } if ((bool)postContinue == true) { _continueProcessingStagingArea = false; // It is possible that we can be re-entered by a nested // dispatcher frame. Continue processing the staging // area if we need to. if (_stagingArea.Count > 0) { // Before we actually start to drain the staging area, we need // to post a work item to process more input. This enables us // to process more input if we enter a nested pump. Dispatcher.BeginInvoke(_continueProcessingStagingAreaCallback, true); _continueProcessingStagingArea = true; // Now fall through to synchronously drain the staging area. } } return(handled); }