Browse Source

Implement custom cursors on X11.

grokys 5 years ago
parent
commit
193cdb3324
3 changed files with 65 additions and 3 deletions
  1. 57 2
      src/Avalonia.X11/X11CursorFactory.cs
  2. 1 1
      src/Avalonia.X11/X11Structs.cs
  3. 7 0
      src/Avalonia.X11/XLib.cs

+ 57 - 2
src/Avalonia.X11/X11CursorFactory.cs

@@ -1,8 +1,13 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.InteropServices;
+using Avalonia.Controls.Platform.Surfaces;
 using Avalonia.Input;
 using Avalonia.Platform;
+using Avalonia.Utilities;
+
+#nullable enable
 
 namespace Avalonia.X11
 {
@@ -67,9 +72,9 @@ namespace Avalonia.X11
             return new PlatformHandle(handle, "XCURSOR");
         }
 
-        public IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
+        public unsafe IPlatformHandle CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
         {
-            throw new NotImplementedException();
+            return new XImageCursor(_display, cursor, hotSpot);
         }
 
         private static IntPtr GetNullCursor(IntPtr display)
@@ -79,5 +84,55 @@ namespace Avalonia.X11
             IntPtr pixmap = XLib.XCreateBitmapFromData(display, window, NullCursorData, 1, 1);
             return XLib.XCreatePixmapCursor(display, pixmap, pixmap, ref color, ref color, 0, 0);
         }
+
+        private unsafe class XImageCursor : IFramebufferPlatformSurface, IPlatformHandle, IDisposable
+        {
+            private readonly PixelSize _pixelSize;
+            private readonly IUnmanagedBlob _blob;
+
+            public XImageCursor(IntPtr display, IBitmapImpl bitmap, PixelPoint hotSpot)
+            {
+                var size = Marshal.SizeOf<XcursorImage>() +
+                    (bitmap.PixelSize.Width * bitmap.PixelSize.Height * 4);
+
+                _pixelSize = bitmap.PixelSize;
+                _blob = AvaloniaLocator.Current.GetService<IRuntimePlatform>().AllocBlob(size);
+                
+                var image = (XcursorImage*)_blob.Address;
+                image->version = 1;
+                image->size = Marshal.SizeOf<XcursorImage>();
+                image->width = bitmap.PixelSize.Width;
+                image->height = bitmap.PixelSize.Height;
+                image->xhot = hotSpot.X;
+                image->yhot = hotSpot.Y;
+                image->pixels = (IntPtr)(image + 1);
+               
+                using (var renderTarget = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().CreateRenderTarget(new[] { this }))
+                using (var ctx = renderTarget.CreateDrawingContext(null))
+                {
+                    var r = new Rect(_pixelSize.ToSize(1)); 
+                    ctx.DrawBitmap(RefCountable.CreateUnownedNotClonable(bitmap), 1, r, r);
+                }
+
+                Handle = XLib.XcursorImageLoadCursor(display, _blob.Address);
+            }
+
+            public IntPtr Handle { get; }
+            public string HandleDescriptor => "XCURSOR";
+
+            public void Dispose()
+            {
+                XLib.XcursorImageDestroy(Handle);
+                _blob.Dispose();
+            }
+
+            public ILockedFramebuffer Lock()
+            {
+                return new LockedFramebuffer(
+                    _blob.Address + Marshal.SizeOf<XcursorImage>(),
+                    _pixelSize, _pixelSize.Width * 4,
+                    new Vector(96, 96), PixelFormat.Bgra8888, null);
+            }
+        }
     }
 }

+ 1 - 1
src/Avalonia.X11/X11Structs.cs

@@ -1684,7 +1684,7 @@ namespace Avalonia.X11 {
 	[StructLayout (LayoutKind.Sequential)]
 	internal struct XcursorImage
 	{
-		private int version;
+		public int version;
 		public int size;       /* nominal size for matching */
 		public int width;      /* actual width */
 		public int height;     /* actual height */

+ 7 - 0
src/Avalonia.X11/XLib.cs

@@ -19,6 +19,7 @@ namespace Avalonia.X11
         const string libX11Randr = "libXrandr.so.2";
         const string libX11Ext = "libXext.so.6";
         const string libXInput = "libXi.so.6";
+        const string libXCursor = "libXcursor.so.1";
 
         [DllImport(libX11)]
         public static extern IntPtr XOpenDisplay(IntPtr display);
@@ -512,6 +513,12 @@ namespace Avalonia.X11
         [DllImport(libXInput)]
         public static extern void XIFreeDeviceInfo(XIDeviceInfo* info);
 
+        [DllImport(libXCursor)]
+        public static extern IntPtr XcursorImageLoadCursor(IntPtr display, IntPtr image);
+
+        [DllImport(libXCursor)]
+        public static extern IntPtr XcursorImageDestroy(IntPtr image);
+
         public static void XISetMask(ref int mask, XiEventType ev)
         {
             mask |= (1 << (int)ev);