File: System\Windows\Input\Stylus\Common\MultiTouchSystemGestureLogic.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//
//
 
using System;
using System.Diagnostics;
 
namespace System.Windows.Input
{
    /// <summary>
    ///     Handles detection of TwoFingerTap and Rollover static gestures.
    /// </summary>
    internal class MultiTouchSystemGestureLogic
    {
        internal MultiTouchSystemGestureLogic()
        {
            _currentState = State.Idle;
            Reset();
        }
 
        /// <summary>
        ///     Analyzes stylus input and determines if a SystemGesture should be generated.
        /// </summary>
        /// <param name="stylusInputReport">The latest stylus input report.</param>
        /// <returns>The SystemGesture to generate, null otherwise.</returns>
        internal SystemGesture? GenerateStaticGesture(RawStylusInputReport stylusInputReport)
        {
            switch (stylusInputReport.Actions)
            {
                case RawStylusActions.Down:
                    OnTouchDown(stylusInputReport);
                    return null;
 
                case RawStylusActions.Up:
                    return OnTouchUp(stylusInputReport);
 
 
                case RawStylusActions.SystemGesture:
                    OnSystemGesture((RawStylusSystemGestureInputReport)stylusInputReport);
                    return null;
 
                    /*
                case RawStylusActions.Move:
                case RawStylusActions.Activate:
                case RawStylusActions.Deactivate:
                case RawStylusActions.OutOfRange:
                    return null;
                     */
 
                default:
                    return null;
            }
        }
 
        private void OnTouchDown(RawStylusInputReport stylusInputReport)
        {
            switch (_currentState)
            {
                case State.Idle:
                    // The first finger came down
                    Reset(); // Clear old settings
                    _firstStylusDeviceId = stylusInputReport.StylusDeviceId;
                    _currentState = State.OneFingerDown;
                    _firstDownTime = Environment.TickCount;
                    break;
 
                case State.OneFingerDown:
                    // The second finger came down
                    Debug.Assert(_firstStylusDeviceId != null && _firstStylusDeviceId != stylusInputReport.StylusDeviceId);
                    Debug.Assert(_secondStylusDeviceId == null);
                    _secondStylusDeviceId = stylusInputReport.StylusDeviceId;
                    _currentState = State.TwoFingersDown;
                    break;
 
                    // Ignoring fingers beyond two
            }
        }
 
        private SystemGesture? OnTouchUp(RawStylusInputReport stylusInputReport)
        {
            switch (_currentState)
            {
                case State.TwoFingersDown:
                    if (IsTrackedStylusId(stylusInputReport.StylusDeviceId))
                    {
                        // One of the two fingers released
                        _firstUpTime = Environment.TickCount;
                        _currentState = State.OneFingerInStaticGesture;
                    }
                    break;
 
                case State.OneFingerDown:
                    if (IsTrackedStylusId(stylusInputReport.StylusDeviceId))
                    {
                        // The single finger that was down was released
                        _currentState = State.Idle;
                    }
                    break;
 
                case State.OneFingerInStaticGesture:
                    // The last of two fingers released
                    _currentState = State.Idle;
 
                    // Check the times and see if a TwoFingerTap or Rollover should be generated.
                    // TwoFingerTap is more constrained than Rollover, so check it first.
                    if (IsTwoFingerTap())
                    {
                        return SystemGesture.TwoFingerTap;
                    }
#if ROLLOVER_IMPLEMENTED
                    else if (IsRollover())
                    {
                        return SystemGesture.Rollover;
                    }
#endif
                    break;
 
                case State.TwoFingersInWisptisGesture:
                    if (IsTrackedStylusId(stylusInputReport.StylusDeviceId))
                    {
                        // One of the two fingers released.
                        _currentState = State.OneFingerInWisptisGesture;
                    }
                    break;
 
                case State.OneFingerInWisptisGesture:
                    if (IsTrackedStylusId(stylusInputReport.StylusDeviceId))
                    {
                        // The last finger released
                        _currentState = State.Idle;
                    }
                    break;
}
 
            return null;
        }
 
        private void OnSystemGesture(RawStylusSystemGestureInputReport stylusInputReport)
        {
            switch (_currentState)
            {
                case State.TwoFingersDown:
                    switch (stylusInputReport.SystemGesture)
                    {
                        case SystemGesture.Drag:
                        case SystemGesture.RightDrag:
                        case SystemGesture.Flick:
                            // One of the two fingers made a Wisptis detected gesture
                            // that prevents TwoFingerTap or Rollover.
                            _currentState = State.TwoFingersInWisptisGesture;
                            break;
                    }
                    break;
 
                case State.OneFingerInStaticGesture:
                case State.OneFingerDown:
                    switch (stylusInputReport.SystemGesture)
                    {
                        case SystemGesture.Drag:
                        case SystemGesture.RightDrag:
                        case SystemGesture.Flick:
                            // A finger made a Wisptis detected gesture
                            // that prevents TwoFingerTap or Rollover.
                            _currentState = State.OneFingerInWisptisGesture;
                            break;
                    }
                    break;
            }
        }
 
        private void Reset()
        {
            _firstStylusDeviceId = null;
            _secondStylusDeviceId = null;
            _firstDownTime = 0;
            _firstUpTime = 0;
        }
 
        private bool IsTrackedStylusId(int id)
        {
            return (id == _firstStylusDeviceId) || (id == _secondStylusDeviceId);
        }
 
        private bool IsTwoFingerTap()
        {
            int now = Environment.TickCount;
            int sinceFirstDown = now - _firstDownTime;
            int sinceFirstUp = now - _firstUpTime;
            return (sinceFirstUp < TwoFingerTapTime) && (sinceFirstDown < RolloverTime);
        }
 
#if ROLLOVER_IMPLEMENTED
        private bool IsRollover()
        {
            int sinceFirstDown = Environment.TickCount - _firstDownTime;
            return sinceFirstDown < RolloverTime;
        }
#endif
 
        private State _currentState;
        private int? _firstStylusDeviceId;
        private int? _secondStylusDeviceId;
        private int _firstDownTime;
        private int _firstUpTime;
 
        // These numbers come from some usability tests and have yet
        // to become system parameters. If Windows exposes these as parameters,
        // then query the system for these values.
        private const int TwoFingerTapTime = 150;
        private const int RolloverTime = 1158;
 
        private enum State
        {
            Idle,
            OneFingerDown,
            TwoFingersDown,
            OneFingerInStaticGesture,
            TwoFingersInWisptisGesture,
            OneFingerInWisptisGesture,
        }
    }
}