/// <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(false, input, promote); InputManager.CurrentInputManager._stagingArea.Push(item); return(item); }
/// <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(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(); } }
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(); } }
// Only we can make these. internal NotifyInputEventArgs(StagingAreaInputItem input) { StagingItem = input; }
// Only we can make these. Note that we cache and reuse instances. internal PreProcessInputEventArgs(StagingAreaInputItem input) : base(input) { _canceled = false; }
// Only we can make these. Note that we cache and reuse instances. internal PreProcessInputEventArgs(StagingAreaInputItem input) : base(input) { _canceled = false; }
/// <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> /// <returns> /// The specified staging area input item. /// </returns> public StagingAreaInputItem PushInput(StagingAreaInputItem input) { InputManager.CurrentInputManager._stagingArea.Push(input); return(input); }
// Only we can make these. internal ProcessInputEventArgs(StagingAreaInputItem input) : base(input) { }
/// <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> /// <returns> /// The specified staging area input item. /// </returns> public StagingAreaInputItem PushInput(StagingAreaInputItem input) { InputManager.CurrentInputManager._currentStagingStack.Push(input); return input; }
// Only we can make these. internal ProcessInputEventArgs(StagingAreaInputItem input) : base(input) { }
// Only we can make these. internal NotifyInputEventArgs(StagingAreaInputItem input) { StagingItem = input; }
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); }