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 void PreProcessInput(object sender, PreProcessInputEventArgs e) { InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; if (input != null) { if (input.Report.Type == InputType.Command) { RawAppCommandInputReport rawAppCommandInputReport = input.Report as RawAppCommandInputReport; if (rawAppCommandInputReport != null) { // Claim the input for the Command. input.Device = this; // Set the proper source input.Source = GetSourceFromDevice(rawAppCommandInputReport.Device); } } } }
/// <summary> /// Reports input to the input manager. /// </summary> /// <returns> /// Whether or not any event generated as a consequence of this /// event was handled. /// </returns> /// <remarks> /// Do we really need this? Make the "providers" call InputManager.ProcessInput themselves. /// we currently need to map back to providers for other reasons. /// </remarks> public bool ReportInput(InputReport inputReport) { if (IsDisposed) { throw new ObjectDisposedException(SR.Get(SRID.InputProviderSiteDisposed)); } bool handled = false; InputReportEventArgs input = new InputReportEventArgs(null, inputReport); input.RoutedEvent = InputManager.PreviewInputReportEvent; if (_inputManager != null) { handled = _inputManager.Value.ProcessInput(input); } return(handled); }
private void PostProcessInput(object sender, ProcessInputEventArgs e) { if (e.StagingItem.Input.RoutedEvent == InputManager.InputReportEvent) { if (!e.StagingItem.Input.Handled) { InputReportEventArgs inputReportEventArgs = e.StagingItem.Input as InputReportEventArgs; if (inputReportEventArgs != null) { RawAppCommandInputReport rawAppCommandInputReport = inputReportEventArgs.Report as RawAppCommandInputReport; if (rawAppCommandInputReport != null) { IInputElement commandTarget = e.StagingItem.Input.OriginalSource as IInputElement; if (commandTarget != null) { RoutedCommand command = GetRoutedCommand(rawAppCommandInputReport.AppCommand); if (command != null) { // Send the app command to the tree to be handled by UIElements and ContentElements // that will forward the event to CommandManager. CommandDeviceEventArgs args = new CommandDeviceEventArgs(this, rawAppCommandInputReport.Timestamp, command); args.RoutedEvent = CommandDeviceEvent; args.Source = commandTarget; e.PushInput(args, e.StagingItem); } } } } } } else if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyUpEvent || e.StagingItem.Input.RoutedEvent == Mouse.MouseUpEvent || e.StagingItem.Input.RoutedEvent == Keyboard.GotKeyboardFocusEvent || e.StagingItem.Input.RoutedEvent == Keyboard.LostKeyboardFocusEvent) { CommandManager.InvalidateRequerySuggested(); } }
private void PostProcessInput(object sender, ProcessInputEventArgs e) { // KeyUp if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyUpEvent) { KeyEventArgs keyArgs = (KeyEventArgs)e.StagingItem.Input; if (!keyArgs.Handled) { if (keyArgs.RealKey == Key.LeftAlt || keyArgs.RealKey == Key.RightAlt) { // Make sure both Alt keys are up. ModifierKeys modifiers = keyArgs.KeyboardDevice.Modifiers; if ((modifiers & ModifierKeys.Alt) == 0) { if (_altNumpadEntryMode) { _altNumpadEntryMode = false; // Generate the Unicode equivalent if we // actually entered a number via the numpad. if (_altNumpadEntry != 0) { _altNumpadcomposition.ClearTexts(); if (_altNumpadConversionMode == AltNumpadConversionMode.OEMCodePage) { _altNumpadcomposition.SetText(GetCurrentOEMCPEncoding(_altNumpadEntry)); } else if ((_altNumpadConversionMode == AltNumpadConversionMode.DefaultCodePage) || (_altNumpadConversionMode == AltNumpadConversionMode.HexDefaultCodePage)) { _altNumpadcomposition.SetText(CharacterEncoding(InputLanguageManager.Current.CurrentInputLanguage.TextInfo.ANSICodePage, _altNumpadEntry)); } else if (_altNumpadConversionMode == AltNumpadConversionMode.HexUnicode) { Char[] chars = new Char[1]; chars[0] = (Char)_altNumpadEntry; _altNumpadcomposition.SetText(new string(chars)); } } } } } } else { // Someone handled Alt key up event, we cancel Alt-Numpad handling. _altNumpadEntryMode = false; _altNumpadEntry = 0; _altNumpadConversionMode = AltNumpadConversionMode.OEMCodePage; } } // PreviewTextInputBegin --> TextInputStart else if (e.StagingItem.Input.RoutedEvent == TextCompositionManager.PreviewTextInputStartEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs)e.StagingItem.Input; if (!textArgs.Handled) { TextCompositionEventArgs text = new TextCompositionEventArgs(textArgs.Device, textArgs.TextComposition); text.RoutedEvent = TextCompositionManager.TextInputStartEvent; text.Source = textArgs.TextComposition.Source; e.PushInput(text, e.StagingItem); } } // PreviewTextInputUpdate --> TextInputUpdate else if (e.StagingItem.Input.RoutedEvent == TextCompositionManager.PreviewTextInputUpdateEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs)e.StagingItem.Input; if (!textArgs.Handled) { TextCompositionEventArgs text = new TextCompositionEventArgs(textArgs.Device, textArgs.TextComposition); text.RoutedEvent = TextCompositionManager.TextInputUpdateEvent; text.Source = textArgs.TextComposition.Source; e.PushInput(text, e.StagingItem); } } // PreviewTextInput --> TextInput else if (e.StagingItem.Input.RoutedEvent == TextCompositionManager.PreviewTextInputEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs)e.StagingItem.Input; if (!textArgs.Handled) { TextCompositionEventArgs text = new TextCompositionEventArgs(textArgs.Device, textArgs.TextComposition); text.RoutedEvent = TextCompositionManager.TextInputEvent; text.Source = textArgs.TextComposition.Source; e.PushInput(text, e.StagingItem); } } // TextCompositioniBegin --> TextInput if this is AutomaticComplete. else if (e.StagingItem.Input.RoutedEvent == TextCompositionManager.TextInputStartEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs)e.StagingItem.Input; if (!textArgs.Handled) { if (textArgs.TextComposition.AutoComplete == TextCompositionAutoComplete.On) { textArgs.Handled = UnsafeCompleteComposition(textArgs.TextComposition); } } } // TextCompositionUpdate --> TextInput if this is AutomaticComplete. else if (e.StagingItem.Input.RoutedEvent == TextCompositionManager.TextInputUpdateEvent) { TextCompositionEventArgs textArgs = (TextCompositionEventArgs)e.StagingItem.Input; if (!textArgs.Handled) { if ((textArgs.TextComposition == _deadCharTextComposition) && (_deadCharTextComposition.Composed)) { //Clear the _deadCharTextComposition to handle re-entrant cases. DeadCharTextComposition comp = _deadCharTextComposition; _deadCharTextComposition = null; textArgs.Handled = UnsafeCompleteComposition(comp); } } } // Raw to StartComposition. InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; if (input != null) { if (input.Report.Type == InputType.Text && input.RoutedEvent == InputManager.InputReportEvent) { RawTextInputReport textInput; textInput = (RawTextInputReport)input.Report; // // string inputText = new string(textInput.CharacterCode, 1); bool fDoneAltNumpadComposition = false; if (_altNumpadcomposition != null) { // Generate TextInput event from WM_CHAR handler. if (inputText.Equals(_altNumpadcomposition.Text)) { fDoneAltNumpadComposition = true; } else { // The generated text from InputReport does not matched with _altNumpadcomposition. // Cancel this composition and process the char from InputReport. _altNumpadcomposition.ClearTexts(); } _altNumpadcomposition.Complete(); ClearAltnumpadComposition(); } if (!fDoneAltNumpadComposition) { if (textInput.IsDeadCharacter) { _deadCharTextComposition = new DeadCharTextComposition(_inputManager, (IInputElement)null, inputText, TextCompositionAutoComplete.Off, InputManager.Current.PrimaryKeyboardDevice); if (textInput.IsSystemCharacter) { _deadCharTextComposition.MakeSystem(); } else if (textInput.IsControlCharacter) { _deadCharTextComposition.MakeControl(); } input.Handled = UnsafeStartComposition(_deadCharTextComposition); } else { if (_deadCharTextComposition != null) { input.Handled = CompleteDeadCharComposition(inputText, textInput.IsSystemCharacter, textInput.IsControlCharacter); } else { TextComposition composition = new TextComposition(_inputManager, (IInputElement)e.StagingItem.Input.Source, inputText, TextCompositionAutoComplete.On, InputManager.Current.PrimaryKeyboardDevice); if (textInput.IsSystemCharacter) { composition.MakeSystem(); } else if (textInput.IsControlCharacter) { composition.MakeControl(); } input.Handled = UnsafeStartComposition(composition); } } } } } }
internal void PlayBackCachedDownInputReport(int timestamp) { if (_needToSendMouseDown) { // if we have marked this as handled we need to play the down otherwise we can ignore the down // as it will be process anyway and either way we need to clean up the cached down PresentationSource mouseInputSource = GetMousePresentationSource(); if (mouseInputSource != null) { Point pt = PointUtil.ScreenToClient(_lastMouseScreenLocation, mouseInputSource); _needToSendMouseDown = false; // We've sent down, don't send again. // Update the state we report to the mouse (GetButtonState). _promotedMouseState = MouseButtonState.Pressed; RawMouseActions actions = _fLeftButtonDownTrigger?RawMouseActions.Button1Press:RawMouseActions.Button2Press; // StylusLogic manages the mouse state reported to the MouseDevice to deal with multiple stylusdevice input. if (_stylusLogic.UpdateMouseButtonState(actions)) { // See if we need to set the Mouse Activate flag. InputManager inputManager = (InputManager)Dispatcher.InputManager; if (inputManager != null) { if (inputManager.PrimaryMouseDevice.CriticalActiveSource != mouseInputSource) { actions |= RawMouseActions.Activate; } } // Update the last event we've sent through. _stylusLogic.SetLastRawMouseActions(actions); RawMouseInputReport mouseInputReport = new RawMouseInputReport( InputMode.Foreground, timestamp, mouseInputSource, actions, (int)pt.X, (int)pt.Y, 0, IntPtr.Zero); InputReportEventArgs inputReportArgs = new InputReportEventArgs(this, mouseInputReport); inputReportArgs.RoutedEvent=InputManager.PreviewInputReportEvent; _stylusLogic.InputManagerProcessInputEventArgs(inputReportArgs); } } _needToSendMouseDown = false; // so we don't try and resend it later. } }
private void PromoteMainToMouse(StagingAreaInputItem stagingItem) { if(!stagingItem.Input.Handled) { StylusEventArgs stylusArgs = stagingItem.Input as StylusEventArgs; if (stylusArgs != null) { StylusDevice stylusDevice = stylusArgs.StylusDevice; // We only want to promote to mouse when we actually have real stylus input. if (stylusDevice != null) { Debug.Assert(ShouldPromoteToMouse(stylusDevice) && stylusDevice.TouchDevice.PromotingToOther); if (IgnoreGestureToMousePromotion(stylusArgs as StylusSystemGestureEventArgs, stylusDevice.TouchDevice)) { return; } RawMouseActions actions = stylusDevice.GetMouseActionsFromStylusEventAndPlaybackCachedDown(stagingItem.Input.RoutedEvent, stylusArgs); if (actions != RawMouseActions.None) { PresentationSource mouseInputSource = stylusDevice.GetMousePresentationSource(); if (mouseInputSource != null) { Point pt = PointUtil.ScreenToClient(stylusDevice.LastMouseScreenPoint, mouseInputSource); // // use the dispatcher as a way of coalescing mouse *move* messages // BUT don't flood the dispatcher with delegates if we're already // waiting for a callback // if ((actions & RawMouseActions.AbsoluteMove) != 0) { if (actions == _lastRawMouseAction && _waitingForDelegate) { return; // We don't need to process this one. } else { //set the waiting bit so we won't enter here again //until we get the callback _waitingForDelegate = true; Dispatcher.BeginInvoke(DispatcherPriority.Input, (DispatcherOperationCallback)delegate(object unused) { //reset our flags here in the callback. _waitingForDelegate = false; return null; }, null); } } // See if we need to set the Mouse Activate flag. if (_inputManager.Value.PrimaryMouseDevice.CriticalActiveSource != mouseInputSource) { actions |= RawMouseActions.Activate; } _lastRawMouseAction = actions; RawMouseInputReport mouseInputReport = new RawMouseInputReport( InputMode.Foreground, stylusArgs.Timestamp, mouseInputSource, actions, (int)pt.X, (int)pt.Y, 0, IntPtr.Zero); InputReportEventArgs inputReportArgs = new InputReportEventArgs(stylusDevice, mouseInputReport); inputReportArgs.RoutedEvent=InputManager.PreviewInputReportEvent; _inputManager.Value.ProcessInput(inputReportArgs); } } } } } }
private void PreProcessInput(object sender, PreProcessInputEventArgs e) { if (_inputEnabled) { if(e.StagingItem.Input.RoutedEvent == InputManager.PreviewInputReportEvent) { InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; if(input != null && !input.Handled) { // See if we are in a DragDrop operation. If so set our internal flag // which stops us from promoting Stylus or Mouse events! if (_inDragDrop != _inputManager.Value.InDragDrop) { _inDragDrop = _inputManager.Value.InDragDrop; // If we are going out of DragDrop then we need to re [....] the mouse state // if we have a stylus device in range (otherwise we [....] on the next // stylus coming in range). if (!_inDragDrop && _stylusDeviceInRange) { UpdateMouseState(); } } if (input.Report.Type == InputType.Mouse) { // If we see a non stylus mouse event (not triggered from stylus event) if ((input.Device as StylusDevice) == null) { // And we only do work if we are enabled for stylus input (ie - have tablet devices) if (TabletDevices.Count != 0) { RawMouseInputReport mouseInputReport = (RawMouseInputReport) input.Report; RawMouseActions actions = mouseInputReport.Actions; int mouseExtraInfo = NativeMethods.IntPtrToInt32(mouseInputReport.ExtraInformation); bool fromWisptis = (mouseExtraInfo & 0xFFFFFF00) == 0xFF515700; // MI_WP_SIGNATURE // Grab the stylus info if from wisptis if (fromWisptis) { _lastMouseMoveFromStylus = true; // Grab the current stylus Id out of the extra info. _lastStylusDeviceId = (mouseExtraInfo & 0x000000FF); } // If mouse is getting deactivated and StylusOver is non null then force stylusover to null. if ((actions & RawMouseActions.Deactivate) == RawMouseActions.Deactivate) { _seenRealMouseActivate = false; if (CurrentStylusDevice != null) { PenContexts penContexts = GetPenContextsFromHwnd(mouseInputReport.InputSource); // If we are inRange still then defer the Deactivate call till we are OutOfRange. if (_stylusDeviceInRange && !_inDragDrop && (penContexts == null || !penContexts.IsWindowDisabled)) { _mouseDeactivateInputReport = mouseInputReport; e.Cancel(); input.Handled = true; return; } #if !MULTICAPTURE else if (CurrentStylusDevice.DirectlyOver != null) #else else #endif { MouseDevice mouseDevice = _inputManager.Value.PrimaryMouseDevice; if (mouseDevice.CriticalActiveSource == mouseInputReport.InputSource) { #if !MULTICAPTURE // Update over to be null when deactivating. CurrentStylusDevice.ChangeStylusOver(null); #else lock (__stylusDeviceLock) { foreach (var pair in __stylusDeviceMap) { var currentDevice = pair.Value; if (currentDevice.DirectlyOver != null) { // Update over to be null when deactivating. currentDevice.ChangeStylusOver(null); } } } #endif } } } } // See if we got some mouse input we need to check for consistency (not tagged from wisptis) else if ((actions & RawMouseActions.CancelCapture) != 0) { // We need to resend this back through as coming from a stylusdevice if in range if (CurrentStylusDevice != null && CurrentStylusDevice.InRange) { RawMouseInputReport cancelCaptureInputReport = new RawMouseInputReport(mouseInputReport.Mode, mouseInputReport.Timestamp, mouseInputReport.InputSource, mouseInputReport.Actions, 0, // Rest of the parameters are not used... 0, 0, IntPtr.Zero); InputReportEventArgs args = new InputReportEventArgs(CurrentStylusDevice, cancelCaptureInputReport); args.RoutedEvent = InputManager.PreviewInputReportEvent; e.Cancel(); _inputManager.Value.ProcessInput(args); } } // Handle the Mouse activation else if ((actions & RawMouseActions.Activate) != 0) { // If mouse is getting Activated and we ate a Deactivate then clear the cached Deactivate. _mouseDeactivateInputReport = null; // We process Activate events and make sure to clear any other actions if we are resending // this from a StylusDevice. This is so we don't get a move generated before we see the // StylusDevice InRange event and the following StylusMove which will generate a MouseMove. StylusDevice activateStylusDevice = null; _seenRealMouseActivate = true; // See if we need to process this event from us. if (CurrentStylusDevice != null && CurrentStylusDevice.InRange) activateStylusDevice = CurrentStylusDevice; else if (fromWisptis || ShouldConsiderStylusInRange(mouseInputReport)) activateStylusDevice = FindStylusDevice(_lastStylusDeviceId); // We need to resend this as coming from a stylusdevice if in range possibly. if (activateStylusDevice != null) { // Check to se if we have already Activated the mouse from a stylus event. // If not then we need to let this one go through marked from us if we are in range! if (mouseInputReport.InputSource != _inputManager.Value.PrimaryMouseDevice.CriticalActiveSource) { Point pt; pt = activateStylusDevice.LastMouseScreenPoint; // Use last promoted mouse location. pt = PointUtil.ScreenToClient(pt, mouseInputReport.InputSource); RawMouseInputReport activateInputReport = new RawMouseInputReport(mouseInputReport.Mode, mouseInputReport.Timestamp, mouseInputReport.InputSource, RawMouseActions.Activate, // Only let activate happen. (int)pt.X, (int)pt.Y, mouseInputReport.Wheel, mouseInputReport.ExtraInformation); InputReportEventArgs args = new InputReportEventArgs(activateStylusDevice, activateInputReport); args.RoutedEvent = InputManager.PreviewInputReportEvent; _inputManager.Value.ProcessInput(args); } // If stylus is active then eat this since we'll send the activate. We just cancel // to ensure the mouse event from HwndMouseInputProvider returns that it was not handled. // The mouse device code will not do anything with the event during PreProcessInput and // it will not see a PreNotifyInput event for this. e.Cancel(); } } // Handle moves and button presses that might be from wisptis or in conflict with our current state else if ((actions & (RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor | RawMouseActions.Button1Press | RawMouseActions.Button1Release | RawMouseActions.Button2Press | RawMouseActions.Button2Release)) != 0) { // If we see a mouse left down and stylus is inRange and we haven't sent a mouse down // then send it through. if ((actions & RawMouseActions.Button1Press) != 0 && CurrentStylusDevice != null && !CurrentStylusDevice.InAir) { // We can only Activate the window without flashing the tray icon for it when // we are processing an Input message. So we defer it till we see the mouse down. HwndSource hwndSource = mouseInputReport.InputSource as HwndSource; IntPtr hwnd = hwndSource != null ? hwndSource.CriticalHandle : IntPtr.Zero; // If we see a stylusdown and we are not the foreground window // and there's no capture then make sure we get activated. // We only do this for top most windows. if (hwnd != IntPtr.Zero && _inputManager.Value.PrimaryMouseDevice.Captured != null && UnsafeNativeMethods.GetParent(new HandleRef(this, hwnd)) == IntPtr.Zero && hwnd != UnsafeNativeMethods.GetForegroundWindow()) { // Check to see if this window has the WS_EX_NOACTIVATE style set, if so don't do the activation work. int style = UnsafeNativeMethods.GetWindowLong(new HandleRef(this,hwnd), NativeMethods.GWL_EXSTYLE); if ((style & NativeMethods.WS_EX_NOACTIVATE) == 0) { UnsafeNativeMethods.SetForegroundWindow(new HandleRef(this,hwndSource.Handle)); } } // There are times we need to make sure we promote the left mouse down before we see a system gesture. // This is when the press and hold gesture is disabled and thus we can guarentee that sending the // left mouse down is the correct thing to do. This is critical for some controls such as repeat // buttons since in order get them in the pressed state (and start them repeating) we have to send the // left mouse down. Note if you go down with the stylus and don't move it past the drag tolerance no // system gesture will be generated and the normal code to promote the mouse down will not happen otherwise. // // This code will kick in on Vista with the new support to disable the press and hold gesture per element // (via WM_TABLE_QUERYSYSTEMGESTURESTATUS message) and also on XP and Vista if the press and hold gesture is // disabled in the tablet control panel. if (!CurrentStylusDevice.SentMouseDown && fromWisptis && ShouldPromoteToMouse(CurrentStylusDevice)) { // left button down...lets replay the down at this time... // Note: We may wait till later if stylus is not down yet! // We will do it only when we are not manipulating and we will // delay it if we know that manipulations are possible. StylusTouchDevice touchDevice = CurrentStylusDevice.TouchDevice; if (touchDevice.PromotingToManipulation) { touchDevice.StoredStagingAreaItems.Add(e.StagingItem); } else if (touchDevice.PromotingToOther) { CurrentStylusDevice.PlayBackCachedDownInputReport(mouseInputReport.Timestamp); } } } // We want to eat mouse messages with the wisptis injected signature except // if the MouseDevice is getting activated or deactivated by it (filtered out // above). We also want to eat any spurious mouse events recieved between the // stylus down and the stylus system gesture getting fired. if (fromWisptis) { // eat mouse messages generated by stylus; // these will be handled off the stylus event stream and promoted to a mouse input event bool handled = true; // If the mouse is captured we need to validate that the mouse location // is actually inside the client area (we will only see those wisptis // events and can thus eat this one). Point ptMouse = new Point(mouseInputReport.X, mouseInputReport.Y); bool stylusIsDown = (CurrentStylusDevice != null) ? !CurrentStylusDevice.InAir : false; if (!stylusIsDown && Mouse.Captured != null && !InWindowClientRect(ptMouse, mouseInputReport.InputSource)) { handled = false; } // If the input has been marked as Handled, we want it to be cancelled at PreProcess stage. if (handled) { // We can't mark left and right mouse buttons as handled since it will stop the // DefWindowProc from being processed but we Cancel it which stops mouse from processing // it. Move's though we need to eat. if ((actions & (RawMouseActions.Button1Press | RawMouseActions.Button2Press)) == 0) { input.Handled = true; } e.Cancel(); // If the stylus is in the up state when we see a mouse down then just note that we've // seen the mouse left down and wanted to send it but the stylus down // has not been seen yet so we can't. When we see the stylus down later we'll promote // the left mouse down after processing the stylus down. if ((actions & RawMouseActions.Button1Press) != 0 && CurrentStylusDevice != null && CurrentStylusDevice.InAir) { CurrentStylusDevice.SetSawMouseButton1Down(true); } // Only try to process stylus events on wisptis generated mouse events and // make sure we don't re-enter ourselves. if (!_processingQueuedEvent) { // Make sure we process any pending Stylus Input before this mouse event. InputManagerProcessInput(null); } } } else { bool cancelMouseEvent = false; bool markHandled = true; // If Stylus is in range then it will be driving the mouse. Ignore any mouse input. if (_stylusDeviceInRange) { cancelMouseEvent = true; // We can't mark left and right mouse buttons as handled since it will stop the // DefWindowProc from being processed but we Cancel it which stops mouse from processing // it. Move's though we need to eat. if ((actions & (RawMouseActions.Button1Press | RawMouseActions.Button2Press)) == 0) { markHandled = false; } } // If we see only a mouse move related action while the stylus is in range then // eat it or try to defer it if not currently in range to see if we come into range. else if ((actions & ~(RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor)) == 0) { if (DeferMouseMove(mouseInputReport)) { cancelMouseEvent = true; } else { // If we now think we're going in range then eat this mouse event if (_lastMouseMoveFromStylus && ShouldConsiderStylusInRange(mouseInputReport)) { SendDeferredMouseEvent(false); // Make sure we clear any deferred mouse events now. cancelMouseEvent = true; } // We're now allowing this mouse event (and deferred one) to be processed... else { // It's a Synchronize that we are letting through so set stylus was not last move anymore. _lastMouseMoveFromStylus = false; // See if we are dealing with a second mouse event, // if so force the original one it to be processed first. if (!_triedDeferringMouseMove) SendDeferredMouseEvent(true); // CurrentStylusDevice is not in range and we're seeeing mouse messages // that are not from wisptis, time to set IsStylusOver to null if (CurrentStylusDevice != null) { // No current stylus device anymore either. SelectStylusDevice(null, null, true); } } } } // If we see a down and have a cached move then let them both go through else { // We see a mouse button 1 or 2 down/up. If we have a cache then dump it and mark that we've // seen mouse input. _lastMouseMoveFromStylus = false; SendDeferredMouseEvent(true); // CurrentStylusDevice is not in range and we're seeeing mouse messages // that are not from wisptis, time to set IsStylusOver to null if (CurrentStylusDevice != null) { // No current stylus device anymore either. SelectStylusDevice(null, null, true); } } // See if we wanted to eat this mouse event... if (cancelMouseEvent) { e.Cancel(); // abort this input if (markHandled) { input.Handled = true; // We also don't want MouseDevice processing this. } } } } // Some other real mouse only generated event came through... else { // Make sure it's only the ones we know should come through. Debug.Assert((actions & ~(RawMouseActions.Button3Press | RawMouseActions.Button3Release | RawMouseActions.Button4Press | RawMouseActions.Button4Release | RawMouseActions.Button5Press | RawMouseActions.Button5Release | RawMouseActions.VerticalWheelRotate | RawMouseActions.HorizontalWheelRotate)) == 0); // If we are not in range then make sure we update our state. // Otherwise we just let this event go through to the MouseDevice. if (!_stylusDeviceInRange) { // We are letting this move through so set stylus was not last move anymore. _lastMouseMoveFromStylus = false; // Dump cache! SendDeferredMouseEvent(true); // CurrentStylusDevice is not in range and we're seeeing mouse messages // that are not from wisptis, time to set IsStylusOver to null if (CurrentStylusDevice != null) { // We now don't have a current stylus device. SelectStylusDevice(null, null, true); } } else { // Make sure to dump the cached mouse event if we are in // range to make sure this mouse event is at the right spot! SendDeferredMouseEvent(true); } } } else { _lastMouseMoveFromStylus = false; } } else { // This event is marked as coming from a StylusDevice so make sure we update flag that we saw mouse event from stylus. _lastMouseMoveFromStylus = true; RawMouseInputReport rawMouseInputReport = (RawMouseInputReport) input.Report; StylusDevice stylusDevice = (StylusDevice)input.Device; if (!stylusDevice.InRange && rawMouseInputReport._isSynchronize) { // eat this one because it is from an activate. e.Cancel(); input.Handled = true; } } } else if (input.Report.Type == InputType.Stylus) { RawStylusInputReport stylusInputReport = (RawStylusInputReport) input.Report; StylusDevice stylusDevice = stylusInputReport.StylusDevice; // RTI sets this if it finds StylusDevice based on Id. bool cancelInput = true; // Only process if we see we have valid input data. if (stylusInputReport.InputSource != null && stylusInputReport.PenContext != null) { if (stylusDevice == null) { // look up stylus device, select it in the Stylus, and claim input for it stylusDevice = FindStylusDevice(stylusInputReport.StylusDeviceId); // Try refreshing tablets if we failed to find this stylus device. if (stylusDevice == null) { stylusDevice = TabletDevices.UpdateStylusDevices( stylusInputReport.TabletDeviceId, stylusInputReport.StylusDeviceId); } stylusInputReport.StylusDevice = stylusDevice; // update stylusdevice. } _triedDeferringMouseMove = false; // reset anytime we see stylus input. // See if this is the special InRange input report that we use to track queued inrange // events so that we can better filter out bogus mouse input. if (stylusInputReport.Actions == RawStylusActions.InRange && stylusInputReport.Data == null) { stylusInputReport.PenContext.DecrementQueuedInRangeCount(); e.Cancel(); input.Handled = true; _lastInRangeTime = Environment.TickCount; return; } // See if this is the special DoubleTap Gesture input report. We use this // event to know when we won't get the tap or drag gesture while the stylus // is down. This allows us to detect and generate the Drag gesture on our own. if (stylusInputReport.Actions == RawStylusActions.SystemGesture && stylusDevice != null) { RawStylusSystemGestureInputReport systemGestureReport = (RawStylusSystemGestureInputReport)stylusInputReport; if (systemGestureReport.SystemGesture == RawStylusSystemGestureInputReport.InternalSystemGestureDoubleTap) { stylusDevice.SeenDoubleTapGesture = true; e.Cancel(); input.Handled = true; return; } } if (stylusDevice != null && IsValidStylusAction(stylusInputReport)) { cancelInput = false; // We can process this event - don't cancel! // See if a static gesture can be generated TabletDevice tabletDevice = stylusDevice.TabletDevice; if (tabletDevice != null) { SystemGesture? systemGesture = tabletDevice.GenerateStaticGesture(stylusInputReport); if (systemGesture != null) { GenerateGesture(stylusInputReport, systemGesture.Value); } } // See if we need to generate a tap gesture. if (stylusInputReport.Actions == RawStylusActions.Up) { if (!stylusDevice.GestureWasFired) { GenerateGesture(stylusInputReport, stylusDevice.LastTapBarrelDown ? SystemGesture.RightTap : SystemGesture.Tap); } if (!_inDragDrop && !stylusInputReport.PenContext.Contexts.IsWindowDisabled) { // We need to process a MouseMove before promoting a MouseUp (in PromoteMainToMouse) // since the stylus updates the button states for mouse to up then. // Note: The Stylus Up is at the same location as the last stylus move so this is OK to do here. ProcessMouseMove(stylusDevice, stylusInputReport.Timestamp, false); } } input.Device = stylusDevice; } } if (cancelInput) { e.Cancel(); // Don't process this bogus event any further. } } } } } }
public bool ReportInput(InputReport inputReport) { if(IsDisposed) { throw new ObjectDisposedException(SR.Get(SRID.InputProviderSiteDisposed)); } bool handled = false; InputReportEventArgs input = new InputReportEventArgs(null, inputReport); input.RoutedEvent=InputManager.PreviewInputReportEvent; if(_inputManager != null) { handled = _inputManager.Value.ProcessInput(input); } return handled; }
private void ProcessMouseMove(StylusDevice stylusDevice, int timestamp, bool isSynchronize) { System.Diagnostics.Debug.Assert(stylusDevice != null); if (!ShouldPromoteToMouse(stylusDevice) || !stylusDevice.TouchDevice.PromotingToOther) { return; } PresentationSource mouseInputSource = stylusDevice.GetMousePresentationSource(); if (mouseInputSource != null) { RawMouseActions actions = RawMouseActions.AbsoluteMove; // Don't set Activate flag if a synchronize is requested! if (!isSynchronize) { if (_inputManager.Value.PrimaryMouseDevice.CriticalActiveSource != mouseInputSource) { actions |= RawMouseActions.Activate; } } Point pt = stylusDevice.LastMouseScreenPoint; // Use last promoted mouse location. pt = PointUtil.ScreenToClient(pt, mouseInputSource); RawMouseInputReport mouseInputReport = new RawMouseInputReport(InputMode.Foreground, timestamp, mouseInputSource, actions, (int)pt.X, (int)pt.Y, 0, IntPtr.Zero); if (isSynchronize) { mouseInputReport._isSynchronize = true; } _lastRawMouseAction = actions; InputReportEventArgs inputReportArgs = new InputReportEventArgs(stylusDevice, mouseInputReport); inputReportArgs.RoutedEvent = InputManager.PreviewInputReportEvent; // Process this directly instead of doing a push. We want this event to get // to the user before the StylusUp and MouseUp event. InputManagerProcessInputEventArgs(inputReportArgs); } }
private void SendDeferredMouseEvent(bool sendInput) { if (sendInput) { _triedDeferringMouseMove = true; // Only reset to not try again if we don't find we are in range. // Only send if we have valid PresentationSource and CompositionTarget. if (_deferredMouseMove != null && _deferredMouseMove.InputSource != null && _deferredMouseMove.InputSource.CompositionTarget != null && !_deferredMouseMove.InputSource.CompositionTarget.IsDisposed) { // Process mouse move now since nothing else from stylus came through... InputReportEventArgs mouseArgs = new InputReportEventArgs(_inputManager.Value.PrimaryMouseDevice, _deferredMouseMove); mouseArgs.RoutedEvent = InputManager.PreviewInputReportEvent; _deferredMouseMove = null; // Clear this out before sending. // This will cause _lastMoveFromStylus to be set to false. _inputManager.Value.ProcessInput(mouseArgs); } } // We no longer need the ref on the cached input report. _deferredMouseMove = null; }
private void GenerateGesture(RawStylusInputReport rawStylusInputReport, SystemGesture gesture) { StylusDevice stylusDevice = rawStylusInputReport.StylusDevice; System.Diagnostics.Debug.Assert(stylusDevice != null); RawStylusSystemGestureInputReport inputReport = new RawStylusSystemGestureInputReport( InputMode.Foreground, rawStylusInputReport.Timestamp, rawStylusInputReport.InputSource, rawStylusInputReport.PenContext, rawStylusInputReport.TabletDeviceId, rawStylusInputReport.StylusDeviceId, gesture, 0, // Gesture X location (only used for flicks) 0, // Gesture Y location (only used for flicks) 0); // ButtonState (only used for flicks) inputReport.StylusDevice = stylusDevice; InputReportEventArgs input = new InputReportEventArgs(stylusDevice, inputReport); input.RoutedEvent=InputManager.PreviewInputReportEvent; // Process this directly instead of doing a push. We want this event to get // to the user before the StylusUp and MouseUp event. InputManagerProcessInputEventArgs(input); }
internal object InputManagerProcessInput(object oInput) { RawStylusInputReport rawStylusInputReport = null; // Now grab the queued up Stylus input reports and process them. lock(_stylusEventQueueLock) { if (_queueStylusEvents.Count > 0) { rawStylusInputReport = _queueStylusEvents.Dequeue(); } } if (rawStylusInputReport != null) { // Check to see if the Vista wisptis has set the IP_RECT_MAPPING_CHANGED bit in the packet status. // If so we need to trigger our mappings to be udpated just in case we processed this before wisptis. switch (rawStylusInputReport.Actions) { case RawStylusActions.InAirMove: case RawStylusActions.Down: //case RawStylusActions.Move: (note this can cause problems with TabletDevice.DoubleTapSize and CancelSize if handled so we don't do work here) case RawStylusActions.Up: int packetStatusIndex = rawStylusInputReport.PenContext.StylusPointDescription.GetPropertyIndex(StylusPointProperties.PacketStatus.Id); if (packetStatusIndex != -1 && rawStylusInputReport.StylusDevice != null) { int packetStatus = rawStylusInputReport.Data[packetStatusIndex]; // grab first packet to check. if ((packetStatus & 0x10) != 0) // Is IP_RECT_MAPPING_CHANGED (0x00000010) set? { using(Dispatcher.DisableProcessing()) // make sure lock() doesn't cause re-entrancy. { // Update display mappings for this TabletDevice. rawStylusInputReport.StylusDevice.TabletDevice.UpdateScreenMeasurements(); } } } break; // we don't need to process any other types of actions. } // build InputReportEventArgs InputReportEventArgs input = new InputReportEventArgs(null, rawStylusInputReport); input.RoutedEvent=InputManager.PreviewInputReportEvent; // Set flag to prevent reentrancy due to wisptis mouse event getting triggered // while processing this stylus event. _processingQueuedEvent = true; try { InputManagerProcessInputEventArgs(input); } finally { _processingQueuedEvent = false; } } return null; }
private void GenerateInRange(RawStylusInputReport rawStylusInputReport) { StylusDevice stylusDevice = rawStylusInputReport.StylusDevice; RawStylusInputReport inputReport = new RawStylusInputReport(rawStylusInputReport.Mode, rawStylusInputReport.Timestamp, rawStylusInputReport.InputSource, rawStylusInputReport.PenContext, RawStylusActions.InRange, stylusDevice.TabletDevice.Id, stylusDevice.Id, rawStylusInputReport.Data); InputReportEventArgs input = new InputReportEventArgs(stylusDevice, inputReport); input.RoutedEvent=InputManager.PreviewInputReportEvent; _inputManager.Value.ProcessInput(input); }
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. // 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 PreProcessInput(object sender, PreProcessInputEventArgs 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; // Normally we only process mouse input that is from our // active visual manager. 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) { // Console.WriteLine("RawMouseActions.Activate"); // If other actions are being reported besides the // activate, separate them into different events. if ((rawMouseInputReport.Actions & ~RawMouseActions.Activate) != 0) { // Cancel this event. We'll push a new event for the activate. e.Cancel(); // Push a new RawMouseInputReport for the non-activate actions. RawMouseInputReport reportActions = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, rawMouseInputReport.Actions & ~RawMouseActions.Activate, rawMouseInputReport.X, rawMouseInputReport.Y, rawMouseInputReport.Wheel, rawMouseInputReport.ExtraInformation); InputReportEventArgs actionsArgs = new InputReportEventArgs(inputReportEventArgs.Device, reportActions); actionsArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(actionsArgs, null); // Create a new RawMouseInputReport for the activate. RawMouseInputReport reportActivate = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, RawMouseActions.Activate, rawMouseInputReport.X, rawMouseInputReport.Y, rawMouseInputReport.Wheel, rawMouseInputReport.ExtraInformation); // Push a new RawMouseInputReport for the activate. InputReportEventArgs activateArgs = new InputReportEventArgs(inputReportEventArgs.Device, reportActivate); activateArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(activateArgs, null); } } // Only process mouse input that is from our active PresentationSource. else if ((_inputSource != null) && (rawMouseInputReport.InputSource == _inputSource.Value)) { // We need to remember the StylusDevice that generated this input. Use the _tagStylusDevice // to store this in before we take over the inputReport Device and loose it. Any // input reports we re-push need to preserve this too. This is used to set the StylusDevice // property on MouseEventArgs. InputDevice inputDevice = e.StagingItem.GetData(_tagStylusDevice) as StylusDevice; if (inputDevice == null) { inputDevice = inputReportEventArgs.Device as StylusDevice; if (inputDevice != null) { e.StagingItem.SetData(_tagStylusDevice, inputDevice); } } // Claim the input for the mouse. inputReportEventArgs.Device = this; // If the input is reporting mouse deactivation, we need // to ensure that the element receives a final leave. // Note that activation could have been moved to another // visual manager in our app, which means that the leave // was already sent. So only do this if the deactivate // event is from the visual manager that we think is active. if ((rawMouseInputReport.Actions & RawMouseActions.Deactivate) == RawMouseActions.Deactivate) { if (_mouseOver != null) { // Push back this event, and cancel the current processing. e.PushInput(e.StagingItem); e.Cancel(); _isPhysicallyOver = false; ChangeMouseOver(null, e.StagingItem.Input.Timestamp); } } // If the input is reporting mouse movement, we need to check // if we need to update our sense of "mouse over". // if ((rawMouseInputReport.Actions & RawMouseActions.AbsoluteMove) == RawMouseActions.AbsoluteMove) { // If other actions are being reported besides the // move, separate them into different events. if ((rawMouseInputReport.Actions & ~(RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor)) != 0) { // Cancel this event. We'll push a new event for the move. e.Cancel(); // Push a new RawMouseInputReport for the non-move actions. RawMouseInputReport reportActions = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, rawMouseInputReport.Actions & ~(RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor), 0, 0, rawMouseInputReport.Wheel, rawMouseInputReport.ExtraInformation); InputReportEventArgs actionsArgs = new InputReportEventArgs(inputDevice, reportActions); actionsArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(actionsArgs, null); // Push a new RawMouseInputReport for the AbsoluteMove. RawMouseInputReport reportMove = new RawMouseInputReport(rawMouseInputReport.Mode, rawMouseInputReport.Timestamp, rawMouseInputReport.InputSource, rawMouseInputReport.Actions & (RawMouseActions.AbsoluteMove | RawMouseActions.QueryCursor), rawMouseInputReport.X, rawMouseInputReport.Y, 0, IntPtr.Zero); InputReportEventArgs moveArgs = new InputReportEventArgs(inputDevice, reportMove); moveArgs.RoutedEvent=InputManager.PreviewInputReportEvent; e.PushInput(moveArgs, null); } else { // Convert the point from client coordinates into "root" coordinates. // We do this in the pre-process stage because it is possible that // this conversion will fail, in which case we want to cancel the // mouse move event. bool success = true; Point ptClient = new Point(rawMouseInputReport.X, rawMouseInputReport.Y); Point ptRoot = PointUtil.TryClientToRoot(ptClient, rawMouseInputReport.InputSource, false, out success); if(success) { e.StagingItem.SetData(_tagRootPoint, ptRoot); } else { e.Cancel(); } } } } } } else { // All mouse event processing should only happen if we still have an active input source. if (_inputSource != null) { if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseDownEvent) { MouseButtonEventArgs mouseButtonEventArgs = e.StagingItem.Input as MouseButtonEventArgs; if (_mouseCapture != null && !_isPhysicallyOver) { // The mouse is not physically over the capture point (or // subtree), so raise the PreviewMouseDownOutsideCapturedElement // event first. MouseButtonEventArgs clickThrough = new MouseButtonEventArgs(this, mouseButtonEventArgs.Timestamp, mouseButtonEventArgs.ChangedButton, GetStylusDevice(e.StagingItem)); clickThrough.RoutedEvent=Mouse.PreviewMouseDownOutsideCapturedElementEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(clickThrough); } } else if (e.StagingItem.Input.RoutedEvent == Mouse.PreviewMouseUpEvent) { MouseButtonEventArgs mouseButtonEventArgs = e.StagingItem.Input as MouseButtonEventArgs; if (_mouseCapture != null && !_isPhysicallyOver) { // The mouse is not physically over the capture point (or // subtree), so raise the PreviewMouseUpOutsideCapturedElement // event first. MouseButtonEventArgs clickThrough = new MouseButtonEventArgs(this, mouseButtonEventArgs.Timestamp, mouseButtonEventArgs.ChangedButton, GetStylusDevice(e.StagingItem)); clickThrough.RoutedEvent=Mouse.PreviewMouseUpOutsideCapturedElementEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(clickThrough); } } } } }
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); }
public void Synchronize() { // System.Console.WriteLine("Synchronize"); // VerifyAccess(); // Simulate a mouse move PresentationSource activeSource = CriticalActiveSource; if (activeSource != null && activeSource.CompositionTarget != null && !activeSource.CompositionTarget.IsDisposed) { int timeStamp = Environment.TickCount; Point ptClient = GetClientPosition(); RawMouseInputReport report = new RawMouseInputReport(InputMode.Foreground, timeStamp, activeSource, RawMouseActions.AbsoluteMove, (int) ptClient.X, (int) ptClient.Y, 0, IntPtr.Zero); report._isSynchronize = true; InputReportEventArgs inputReportEventArgs; if (_stylusDevice != null) { // if we have a current stylusdevice .. use it inputReportEventArgs = new InputReportEventArgs(_stylusDevice, report); } else { inputReportEventArgs = new InputReportEventArgs(this, report); } inputReportEventArgs.RoutedEvent=InputManager.PreviewInputReportEvent; //ProcessInput has a linkdemand _inputManager.Value.ProcessInput(inputReportEventArgs); } }
public void Synchronize() { // Simulate a stylus move (if we are current stylus, inrange, visuals still valid to update // and has moved). if (InRange && _inputSource != null && _inputSource.Value != null && _inputSource.Value.CompositionTarget != null && !_inputSource.Value.CompositionTarget.IsDisposed) { Point ptDevice = PointUtil.ScreenToClient(_lastScreenLocation, _inputSource.Value); // GlobalHitTest always returns an IInputElement, so we are sure to have one. IInputElement stylusOver = GlobalHitTest(_inputSource.Value, ptDevice); bool fOffsetChanged = false; if (_stylusOver == stylusOver) { Point ptOffset = GetPosition(stylusOver); fOffsetChanged = MS.Internal.DoubleUtil.AreClose(ptOffset.X, _rawElementRelativePosition.X) == false || MS.Internal.DoubleUtil.AreClose(ptOffset.Y, _rawElementRelativePosition.Y) == false; } if(fOffsetChanged || _stylusOver != stylusOver) { int timeStamp = Environment.TickCount; PenContext penContext = _stylusLogic.GetStylusPenContextForHwnd(_inputSource.Value,TabletDevice.Id); if (_eventStylusPoints != null && _eventStylusPoints.Count > 0 && StylusPointDescription.AreCompatible(penContext.StylusPointDescription, _eventStylusPoints.Description)) { StylusPoint stylusPoint = _eventStylusPoints[_eventStylusPoints.Count - 1]; int[] data = stylusPoint.GetPacketData(); // get back to the correct coordinate system Matrix m = TabletDevice.TabletToScreen; m.Invert(); Point ptTablet = ptDevice * m; data[0] = (int)ptTablet.X; data[1] = (int)ptTablet.Y; RawStylusInputReport report = new RawStylusInputReport(InputMode.Foreground, timeStamp, _inputSource.Value, penContext, InAir?RawStylusActions.InAirMove:RawStylusActions.Move, TabletDevice.Id, Id, data); report.Synchronized = true; InputReportEventArgs inputReportEventArgs = new InputReportEventArgs(this, report); inputReportEventArgs.RoutedEvent=InputManager.PreviewInputReportEvent; _stylusLogic.InputManagerProcessInputEventArgs(inputReportEventArgs); } } } }
private void PostProcessInput(object sender, ProcessInputEventArgs e) { //only [....] with mouse capture if we're enabled, or else there are no tablet devices //hence no input. We have to work around this because getting the //Tablet.TabletDevices will load Penimc.dll. if (_inputEnabled) { // Watch the LostMouseCapture and GotMouseCapture events to keep stylus capture in [....]. if(e.StagingItem.Input.RoutedEvent == Mouse.LostMouseCaptureEvent || e.StagingItem.Input.RoutedEvent == Mouse.GotMouseCaptureEvent) { #if MULTICAPTURE var mouseStylusDevice = Mouse.PrimaryDevice.StylusDevice; #endif // Make sure mouse and stylus capture is the same. foreach (TabletDevice tabletDevice in TabletDevices) { foreach (StylusDevice stylusDevice in tabletDevice.StylusDevices) { #if MULTICAPTURE if (stylusDevice == mouseStylusDevice) { #endif // We use the Mouse device state for each call just in case we // get reentered in the middle of changing so when we continue // we'll use the current mouse capture state (which should NOP). stylusDevice.Capture(Mouse.Captured, Mouse.CapturedMode); #if MULTICAPTURE } #endif } } } } if(e.StagingItem.Input.RoutedEvent == InputManager.InputReportEvent && !_inDragDrop) { InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs; if(!input.Handled && input.Report.Type == InputType.Stylus) { RawStylusInputReport report = (RawStylusInputReport) input.Report; // Only promote if the window is enabled! if (!report.PenContext.Contexts.IsWindowDisabled) { PromoteRawToPreview(report, e); // Need to reset this flag at the end of StylusUp processing. if (report.Actions == RawStylusActions.Up) { report.StylusDevice.IgnoreStroke = false; } } } } // If we are processing an OutOfRange event then see if we need to update the over state. // We need to update it if mouse is already outside the window (MouseDevice.DirectlyOver // is null) since if it has already seen the WM_MOUSELEAVE we'll never update out over // state properly. If the WM_MOUSELEAVE comes in after we see the OutOfRange then the // code at the end of PreProcessInput will deal that case properly. if(e.StagingItem.Input.RoutedEvent == Stylus.StylusOutOfRangeEvent) { RawMouseInputReport mouseDeactivateInputReport = _mouseDeactivateInputReport; _mouseDeactivateInputReport = null; StylusEventArgs eventArgsOutOfRange = (StylusEventArgs)e.StagingItem.Input; // If we have deferred mouse moves then make sure we process last one now. if (_lastRawMouseAction == RawMouseActions.AbsoluteMove && _waitingForDelegate) { ProcessMouseMove(eventArgsOutOfRange.StylusDevice, eventArgsOutOfRange.Timestamp, false); } // See if we need to set the Mouse Activate flag. PresentationSource mouseSource = _inputManager.Value.PrimaryMouseDevice.CriticalActiveSource; // See if we need to change the stylus over state state and send a mouse deactivate. // We send the cached Deactivate through if we saw mouse deactivate before out of range event // *or* for a quick move with the stylus over a window we may not even see any win32 mouse events // so in that case we also need to deactivate the mouse since we were the ones that activated it. if (mouseDeactivateInputReport != null || (!_seenRealMouseActivate && mouseSource != null)) { // First update the StylusDevice DirectlyOver to null if the mouse device saw a Deactivate (means // the mouse left the window) or if it never saw a real activate (stylus mouse promotion // caused it to be active). eventArgsOutOfRange.StylusDevice.ChangeStylusOver(null); // Now send the mouse deactivate RawMouseInputReport newMouseInputReport = mouseDeactivateInputReport != null ? new RawMouseInputReport( mouseDeactivateInputReport.Mode, eventArgsOutOfRange.Timestamp, // updated time mouseDeactivateInputReport.InputSource, mouseDeactivateInputReport.Actions, mouseDeactivateInputReport.X, mouseDeactivateInputReport.Y, mouseDeactivateInputReport.Wheel, mouseDeactivateInputReport.ExtraInformation) : new RawMouseInputReport( InputMode.Foreground, eventArgsOutOfRange.Timestamp, // updated time mouseSource, RawMouseActions.Deactivate, 0, 0, 0, IntPtr.Zero); InputReportEventArgs actionsArgs = new InputReportEventArgs(eventArgsOutOfRange.StylusDevice, newMouseInputReport); actionsArgs.RoutedEvent=InputManager.PreviewInputReportEvent; _inputManager.Value.ProcessInput(actionsArgs); } } // Deal with sending mouse events to the plugins. // NOTE: We want to do this after the mousedevice has sent it's click through // events (PreviewMouseDownOutsideCapturedElementEvent/PreviewMouseUpOutsideCapturedElementEvent) // and PreviewMouse events so that we can route more accurately to where the Mouse events will // actually get routed. CallPlugInsForMouse(e); PromotePreviewToMain(e); UpdateButtonStates(e); PromoteMainToOther(e); // See if we need to generate a drag gesture. if(e.StagingItem.Input.RoutedEvent == Stylus.StylusMoveEvent) { StylusEventArgs stylusMove = (StylusEventArgs)e.StagingItem.Input; if (stylusMove.StylusDevice.SeenDoubleTapGesture && !stylusMove.StylusDevice.GestureWasFired && stylusMove.StylusDevice.DetectedDrag) { GenerateGesture(stylusMove.InputReport, SystemGesture.Drag); } } // Process the flick scroll up/down system gesture now. if (e.StagingItem.Input.RoutedEvent == Stylus.StylusSystemGestureEvent) { StylusSystemGestureEventArgs stylusSystemGesture = (StylusSystemGestureEventArgs)e.StagingItem.Input; if (stylusSystemGesture.SystemGesture == SystemGesture.Flick) { HandleFlick(stylusSystemGesture.ButtonState, stylusSystemGesture.StylusDevice.DirectlyOver); } } }