Jelajahi Sumber

Implemented WM_TOUCH support

Nikita Tsukanov 6 tahun lalu
induk
melakukan
d39bc04dc9

+ 2 - 1
samples/ControlCatalog.NetCore/Program.cs

@@ -46,7 +46,8 @@ namespace ControlCatalog.NetCore
         public static AppBuilder BuildAvaloniaApp()
             => AppBuilder.Configure<App>()
                 .UsePlatformDetect()
-                .With(new X11PlatformOptions{EnableMultiTouch = true})
+                .With(new X11PlatformOptions {EnableMultiTouch = true})
+                .With(new Win32PlatformOptions {EnableMultitouch = true})
                 .UseSkia()
                 .UseReactiveUI();
 

+ 72 - 0
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@@ -574,6 +574,7 @@ namespace Avalonia.Win32.Interop
             WM_AFXLAST = 0x037F,
             WM_PENWINFIRST = 0x0380,
             WM_PENWINLAST = 0x038F,
+            WM_TOUCH = 0x0240,
             WM_APP = 0x8000,
             WM_USER = 0x0400,
 
@@ -836,10 +837,16 @@ namespace Avalonia.Win32.Interop
 
         [DllImport("user32.dll")]
         public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
+
+        [DllImport("user32")]
+        public static extern IntPtr GetMessageExtraInfo();
         
         [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "RegisterClassExW")]
         public static extern ushort RegisterClassEx(ref WNDCLASSEX lpwcx);
 
+        [DllImport("user32.dll")]
+        public static extern void RegisterTouchWindow(IntPtr hWnd, int flags);
+        
         [DllImport("user32.dll")]
         public static extern bool ReleaseCapture();
 
@@ -1035,6 +1042,17 @@ namespace Avalonia.Win32.Interop
         [return: MarshalAs(UnmanagedType.Bool)]
         public static extern bool GetMonitorInfo([In] IntPtr hMonitor, ref MONITORINFO lpmi);
 
+        [DllImport("user32")]
+        public static extern bool GetTouchInputInfo(
+            IntPtr hTouchInput,
+            uint        cInputs,
+            [Out]TOUCHINPUT[] pInputs,
+            int         cbSize
+        );
+        
+        [DllImport("user32")]
+        public static extern bool CloseTouchInputHandle(IntPtr hTouchInput);
+        
         [return: MarshalAs(UnmanagedType.Bool)]
         [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "PostMessageW")]
         public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
@@ -1309,6 +1327,60 @@ namespace Avalonia.Win32.Interop
             public IntPtr hIconSm;
         }
 
+        [StructLayout(LayoutKind.Sequential)]
+        public struct TOUCHINPUT
+        {
+            public int X;
+            public int Y;
+            public IntPtr Source;
+            public uint Id;
+            public TouchInputFlags Flags;
+            public int Mask;
+            public uint Time;
+            public IntPtr ExtraInfo;
+            public int CxContact;
+            public int CyContact;
+        }
+
+        [Flags]
+        public enum TouchInputFlags
+        {
+            /// <summary>
+            /// Movement has occurred. Cannot be combined with TOUCHEVENTF_DOWN.
+            /// </summary>
+            TOUCHEVENTF_MOVE = 0x0001,
+
+            /// <summary>
+            /// The corresponding touch point was established through a new contact. Cannot be combined with TOUCHEVENTF_MOVE or TOUCHEVENTF_UP.
+            /// </summary>
+            TOUCHEVENTF_DOWN = 0x0002,
+
+            /// <summary>
+            /// A touch point was removed.
+            /// </summary>
+            TOUCHEVENTF_UP = 0x0004,
+
+            /// <summary>
+            /// A touch point is in range. This flag is used to enable touch hover support on compatible hardware. Applications that do not want support for hover can ignore this flag.
+            /// </summary>
+            TOUCHEVENTF_INRANGE = 0x0008,
+
+            /// <summary>
+            /// Indicates that this TOUCHINPUT structure corresponds to a primary contact point. See the following text for more information on primary touch points.
+            /// </summary>
+            TOUCHEVENTF_PRIMARY = 0x0010,
+
+            /// <summary>
+            /// When received using GetTouchInputInfo, this input was not coalesced.
+            /// </summary>
+            TOUCHEVENTF_NOCOALESCE = 0x0020,
+
+            /// <summary>
+            /// The touch event came from the user's palm.
+            /// </summary>
+            TOUCHEVENTF_PALM = 0x0080
+        }
+
         [Flags]
         public enum OpenFileNameFlags
         {

+ 5 - 2
src/Windows/Avalonia.Win32/Win32Platform.cs

@@ -40,6 +40,7 @@ namespace Avalonia
     {
         public bool UseDeferredRendering { get; set; } = true;
         public bool AllowEglInitialization { get; set; }
+        public bool? EnableMultitouch { get; set; }
     }
 }
 
@@ -59,7 +60,8 @@ namespace Avalonia.Win32
             CreateMessageWindow();
         }
 
-        public static bool UseDeferredRendering { get; set; }
+        public static bool UseDeferredRendering => Options.UseDeferredRendering;
+        public static Win32PlatformOptions Options { get; private set; }
 
         public Size DoubleClickSize => new Size(
             UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CXDOUBLECLK),
@@ -74,6 +76,7 @@ namespace Avalonia.Win32
 
         public static void Initialize(Win32PlatformOptions options)
         {
+            Options = options;
             AvaloniaLocator.CurrentMutable
                 .Bind<IClipboard>().ToSingleton<ClipboardImpl>()
                 .Bind<IStandardCursorFactory>().ToConstant(CursorFactory.Instance)
@@ -88,7 +91,7 @@ namespace Avalonia.Win32
                 .Bind<IPlatformIconLoader>().ToConstant(s_instance);
             if (options.AllowEglInitialization)
                 Win32GlManager.Initialize();
-            UseDeferredRendering = options.UseDeferredRendering;
+            
             _uiThread = Thread.CurrentThread;
 
             if (OleContext.Current != null)

+ 45 - 1
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -30,6 +30,8 @@ namespace Avalonia.Win32
         private UnmanagedMethods.WndProc _wndProcDelegate;
         private string _className;
         private IntPtr _hwnd;
+        private bool _multitouch;
+        private TouchDevice _touchDevice = new TouchDevice();
         private IInputRoot _owner;
         private bool _trackingMouse;
         private bool _decorated = true;
@@ -414,6 +416,15 @@ namespace Avalonia.Win32
                 IntPtr.Zero);
         }
 
+        bool ShouldIgnoreTouchEmulatedMessage()
+        {
+            if (!_multitouch)
+                return false;
+            var marker = 0xFF515700L;
+            var info = GetMessageExtraInfo().ToInt64();
+            return (info & marker) == marker;
+        }
+        
         [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]
         protected virtual IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
         {
@@ -519,6 +530,8 @@ namespace Avalonia.Win32
                 case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN:
                 case UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN:
                 case UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN:
+                    if(ShouldIgnoreTouchEmulatedMessage())
+                        break;
                     e = new RawMouseEventArgs(
                         WindowsMouseDevice.Instance,
                         timestamp,
@@ -534,6 +547,8 @@ namespace Avalonia.Win32
                 case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP:
                 case UnmanagedMethods.WindowsMessage.WM_RBUTTONUP:
                 case UnmanagedMethods.WindowsMessage.WM_MBUTTONUP:
+                    if(ShouldIgnoreTouchEmulatedMessage())
+                        break;
                     e = new RawMouseEventArgs(
                         WindowsMouseDevice.Instance,
                         timestamp,
@@ -547,6 +562,8 @@ namespace Avalonia.Win32
                     break;
 
                 case UnmanagedMethods.WindowsMessage.WM_MOUSEMOVE:
+                    if(ShouldIgnoreTouchEmulatedMessage())
+                        break;
                     if (!_trackingMouse)
                     {
                         var tm = new UnmanagedMethods.TRACKMOUSEEVENT
@@ -611,7 +628,30 @@ namespace Avalonia.Win32
                                 : RawMouseEventType.MiddleButtonDown,
                         new Point(0, 0), GetMouseModifiers(wParam));
                     break;
-
+                case WindowsMessage.WM_TOUCH:
+                    var touchInputs = new TOUCHINPUT[wParam.ToInt32()];
+                    if (GetTouchInputInfo(lParam, (uint)wParam.ToInt32(), touchInputs, Marshal.SizeOf<TOUCHINPUT>()))
+                    {
+                        foreach (var touchInput in touchInputs)
+                        {
+                            var pt = new POINT {X = touchInput.X / 100, Y = touchInput.Y / 100};
+                            UnmanagedMethods.ScreenToClient(_hwnd, ref pt);
+                            Input?.Invoke(new RawTouchEventArgs(_touchDevice, touchInput.Time,
+                                _owner,
+                                touchInput.Flags.HasFlag(TouchInputFlags.TOUCHEVENTF_UP) ?
+                                    RawMouseEventType.TouchEnd :
+                                    touchInput.Flags.HasFlag(TouchInputFlags.TOUCHEVENTF_DOWN) ?
+                                        RawMouseEventType.TouchBegin :
+                                        RawMouseEventType.TouchUpdate,
+                                new Point(pt.X, pt.Y),
+                                WindowsKeyboardDevice.Instance.Modifiers,
+                                touchInput.Id));
+                        }
+                        CloseTouchInputHandle(lParam);
+                        return IntPtr.Zero;
+                    }
+                    
+                    break;
                 case WindowsMessage.WM_NCPAINT:
                     if (!_decorated)
                     {
@@ -754,6 +794,10 @@ namespace Avalonia.Win32
 
             Handle = new PlatformHandle(_hwnd, PlatformConstants.WindowHandleType);
 
+            _multitouch = Win32Platform.Options.EnableMultitouch ?? false;
+            if (_multitouch)
+                RegisterTouchWindow(_hwnd, 0);
+            
             if (UnmanagedMethods.ShCoreAvailable)
             {
                 uint dpix, dpiy;