File: System\Windows\Input\Stylus\Common\StylusTraceLogger.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.Diagnostics.Tracing;
using System.Runtime.CompilerServices;
using MS.Internal.Telemetry.PresentationCore;
 
namespace System.Windows.Input.Tracing
{
    /// <summary>
    /// Trace logger for the Stylus/Touch stack
    /// </summary>
    internal static class StylusTraceLogger
    {
        #region Enumerations
 
        /// <summary>
        /// Flags to determine used features in the touch stack.
        /// </summary>
        [Flags]
        internal enum FeatureFlags
        {
            /// <summary>
            /// Default value of no features used
            /// </summary>
            None = 0x00000000,
 
            /// <summary>
            /// Determines if a class derived from TouchDevice is used by the developer
            /// </summary>
            CustomTouchDeviceUsed = 0x00000001,
 
            /// <summary>
            /// Determines if any stylus plugin has been used.
            /// </summary>
            StylusPluginsUsed = 0x00000002,
 
            /// <summary>
            /// Determines if a pen flick is processed and used as a scroll up command
            /// </summary>
            FlickScrollingUsed = 0x00000004,
 
            /// <summary>
            /// Determines if the WM_POINTER stack is enabled
            /// </summary>
            PointerStackEnabled = 0x10000000,
 
            /// <summary>
            /// Determines if the WISP based stack is enabled
            /// </summary>
            WispStackEnabled = 0x20000000,
        }
 
        #endregion
 
        #region Data Collection Classes
 
        /// <summary>
        /// A collection of relevant stylus usage statistics and flags
        /// </summary>
        [EventData]
        internal class StylusStatistics
        {
            public FeatureFlags FeaturesUsed { get; set; } = FeatureFlags.None;
        }
 
        /// <summary>
        /// Tracks known re-entrancy hits in the stylus stack.
        /// </summary>
        [EventData]
        internal class ReentrancyEvent
        {
            public string FunctionName { get; set; } = string.Empty;
        }
 
        /// <summary>
        /// Class to log Size instances from tablet
        /// </summary>
        [EventData]
        internal class StylusSize
        {
            public StylusSize(Size size)
            {
                Width = size.Width;
                Height = size.Height;
            }
 
            public double Width { get; set; } = double.NaN;
            public double Height { get; set; } = double.NaN;
        }
 
        /// <summary>
        /// The information for a particular tablet to log on connect/disconnect
        /// </summary>
        [EventData]
        internal class StylusDeviceInfo
        {
            public StylusDeviceInfo(int id, string name, string pnpId, TabletHardwareCapabilities capabilities,
                Size tabletSize, Size screenSize, TabletDeviceType deviceType, int maxContacts)
            {
                Id = id;
                Name = name;
                PlugAndPlayId = pnpId;
                Capabilities = capabilities.ToString("F");
                TabletSize = new StylusTraceLogger.StylusSize(tabletSize);
                ScreenSize = new StylusTraceLogger.StylusSize(screenSize);
                DeviceType = deviceType.ToString("F");
                MaxContacts = maxContacts;
            }
 
            public int Id { get; set; }
            public string Name { get; set; }
            public string PlugAndPlayId { get; set; }
            public string Capabilities { get; set; }
            public StylusSize TabletSize { get; set; }
            public StylusSize ScreenSize { get; set; }
            public string DeviceType { get; set; }
            public int MaxContacts { get; set; }
        }
 
        /// <summary>
        /// The WISP device id of the disconnected device for matching against the
        /// connect.
        /// </summary>
        [EventData]
        internal class StylusDisconnectInfo
        {
            public int Id { get; set; } = -1;
        }
 
        /// <summary>
        /// For reporting errors in the WISP stack
        /// </summary>
        [EventData]
        internal class StylusErrorEventData
        {
            public string Error { get; set; } = null;
        }
 
        #endregion
 
        #region Constants
 
        /// <summary>
        /// Event name for logging datagrid usage details
        /// </summary>
        private const string StartupEventTag = "StylusStartup";
 
        /// <summary>
        /// Event name for logging datagrid usage details
        /// </summary>
        private const string ShutdownEventTag = "StylusShutdown";
 
        /// <summary>
        /// Event name for logging datagrid usage details
        /// </summary>
        private const string StatisticsTag = "StylusStatistics";
 
        /// <summary>
        /// Event name for a stylus error
        /// </summary>
        private const string ErrorTag = "StylusError";
 
        /// <summary>
        /// Event name for a stylus connection
        /// </summary>
        private const string DeviceConnectTag = "StylusConnect";
 
        /// <summary>
        /// Event name for a stylus disconnection
        /// </summary>
        private const string DeviceDisconnectTag = "StylusDisconnect";
 
        /// <summary>
        /// Event name for a stylus disconnection
        /// </summary>
        private const string ReentrancyTag = "StylusReentrancy";
 
        /// <summary>
        /// Event name for the retry limits on re-entrancy into the touch stack being reached
        /// </summary>
        private const string ReentrancyRetryLimitTag = "StylusReentrancyRetryLimitReached";
 
        #endregion
 
        #region Data Collection Functions
 
        /// <summary>
        /// Logs Stylus/Touch stack startup
        /// </summary>
        internal static void LogStartup()
        {
            Log(StartupEventTag);
        }
 
        /// <summary>
        /// Log various statistics about the stack
        /// </summary>
        /// <param name="stylusData">The statistics to log</param>
        internal static void LogStatistics(StylusStatistics stylusData)
        {
            Requires<ArgumentNullException>(stylusData != null);
 
            Log(StatisticsTag, stylusData);
        }
 
        /// <summary>
        /// Log that the retry limit for touch stack re-entrancy has been reached.
        /// </summary>
        internal static void LogReentrancyRetryLimitReached()
        {
            Log(ReentrancyRetryLimitTag);
        }
 
        /// <summary>
        /// Logs an error in the stack
        /// </summary>
        /// <param name="error"></param>
        internal static void LogError(string error)
        {
            Requires<ArgumentNullException>(error != null);
 
            Log(ErrorTag, new StylusErrorEventData() { Error = error });
        }
 
        /// <summary>
        /// Logs device information on connect
        /// </summary>
        /// <param name="deviceInfo"></param>
        internal static void LogDeviceConnect(StylusDeviceInfo deviceInfo)
        {
            Requires<ArgumentNullException>(deviceInfo != null);
 
            Log(DeviceConnectTag, deviceInfo);
        }
 
        /// <summary>
        /// Logs device id on disconnect
        /// </summary>
        /// <param name="deviceId"></param>
        internal static void LogDeviceDisconnect(int deviceId)
        {
            Log(DeviceDisconnectTag, new StylusDisconnectInfo() { Id = deviceId });
        }
 
        /// <summary>
        /// Logs detected re-entrancy in the stack
        /// </summary>
        /// <param name="message"></param>
        /// <param name="functionName"></param>
        internal static void LogReentrancy([CallerMemberName] string functionName = "")
        {
            Log(ReentrancyTag, new ReentrancyEvent() { FunctionName = functionName });
        }
 
        /// <summary>
        /// Logs Stylus/Touch stack shutdown
        /// </summary>
        internal static void LogShutdown()
        {
            Log(ShutdownEventTag);
        }
 
        #endregion
 
        #region Utility
 
        /// <summary>
        /// Throws exception when condition is not met.  We can't use the contracts version of this
        /// since ccrewrite does not work on C++\CLI or netmodules and PresentationCore is a hybrid 
        /// assembly.
        /// </summary>
        /// <typeparam name="T">The type of exception to throw</typeparam>
        /// <param name="condition">The condition to check</param>
        private static void Requires<T>(bool condition)
            where T : Exception, new()
        {
            if (!condition) throw new T();
        }
 
        /// <summary>
        /// Logs a tag with no associated data
        /// </summary>
        /// <param name="tag">The event tag to log</param>
        private static void Log(string tag)
        {
            EventSource logger = TraceLoggingProvider.GetProvider();
            logger?.Write(tag, TelemetryEventSource.MeasuresOptions());
        }
 
        /// <summary>
        /// Logs a tag with associated event data
        /// </summary>
        /// <typeparam name="T">The type of the event data</typeparam>
        /// <param name="tag">The event tag to log</param>
        /// <param name="data">The event data to log (default null)</param>
        private static void Log<T>(string tag, T data = null)
            where T : class
        {
            EventSource logger = TraceLoggingProvider.GetProvider();
            logger?.Write(tag, TelemetryEventSource.MeasuresOptions(), data);
        }
 
        #endregion
    }
}