Przeglądaj źródła

[GTK3] Use XPutImage on Linux when possible

Nikita Tsukanov 8 lat temu
rodzic
commit
e3b3e05a0c

+ 32 - 0
src/Gtk/Avalonia.Gtk3/FramebufferManager.cs

@@ -5,6 +5,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Avalonia.Controls.Platform.Surfaces;
 using Avalonia.Platform;
+using Avalonia.Threading;
 
 namespace Avalonia.Gtk3
 {
@@ -27,7 +28,38 @@ namespace Avalonia.Gtk3
             var s = _window.ClientSize;
             var width = (int) s.Width;
             var height = (int) s.Height;
+            
+            if (!Dispatcher.UIThread.CheckAccess() && Gtk3Platform.DisplayClassName.ToLower().Contains("x11"))
+            {
+                var x11 = LockX11Framebuffer(width, height);
+                if (x11 != null)
+                    return x11;
+            }
+            
+
             return new ImageSurfaceFramebuffer(_window, width, height, _window.LastKnownScaleFactor);
         }
+
+        private static int X11ErrorHandler(IntPtr d, IntPtr e)
+        {
+            return 0;
+        }
+
+        private static X11.XErrorHandler X11ErrorHandlerDelegate = X11ErrorHandler;
+        
+        private static IntPtr X11Display;
+        private ILockedFramebuffer LockX11Framebuffer(int width, int height)
+        {
+            if (!_window.GdkWindowHandle.HasValue)
+                return null;
+            if (X11Display == IntPtr.Zero)
+            {
+                X11Display = X11.XOpenDisplay(IntPtr.Zero);
+                if (X11Display == IntPtr.Zero)
+                    return null;
+                X11.XSetErrorHandler(X11ErrorHandlerDelegate);
+            }
+            return new X11Framebuffer(X11Display, _window.GdkWindowHandle.Value, width, height, _window.LastKnownScaleFactor);
+        }
     }
 }

+ 5 - 0
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.InteropServices;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
@@ -22,11 +23,15 @@ namespace Avalonia.Gtk3
         internal static readonly MouseDevice Mouse = new MouseDevice();
         internal static readonly KeyboardDevice Keyboard = new KeyboardDevice();
         internal static IntPtr App { get; set; }
+        internal static string DisplayClassName;
         public static bool UseDeferredRendering = true;
         public static void Initialize()
         {
             Resolver.Resolve();
             Native.GtkInit(0, IntPtr.Zero);
+            var disp = Native.GdkGetDefaultDisplay();
+            DisplayClassName = Utf8Buffer.StringFromPtr(Native.GTypeName(Marshal.ReadIntPtr(Marshal.ReadIntPtr(disp))));
+            
             using (var utf = new Utf8Buffer("avalonia.app." + Guid.NewGuid()))
                 App = Native.GtkApplicationNew(utf, 0);
             //Mark current thread as UI thread

+ 4 - 0
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@@ -318,6 +318,9 @@ namespace Avalonia.Gtk3.Interop
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
             public delegate void g_object_ref(GObject instance);
             
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
+            public delegate IntPtr g_type_name(IntPtr instance);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
             public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags);
             
@@ -410,6 +413,7 @@ namespace Avalonia.Gtk3.Interop
         public static D.gtk_dialog_add_button GtkDialogAddButton;
         public static D.g_object_unref GObjectUnref;
         public static D.g_object_ref GObjectRef;
+        public static D.g_type_name GTypeName;
         public static D.g_signal_connect_object GSignalConnectObject;
         public static D.g_signal_handler_disconnect GSignalHandlerDisconnect;
         public static D.g_timeout_add GTimeoutAdd;

+ 2 - 1
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@@ -31,7 +31,7 @@ namespace Avalonia.Gtk3
         private object _lock = new object();
         private IDeferredRenderOperation _nextRenderOperation;
         private readonly AutoResetEvent _canSetNextOperation = new AutoResetEvent(true);
-
+        internal IntPtr? GdkWindowHandle;
         public WindowBaseImpl(GtkWindow gtkWidget)
         {
             
@@ -55,6 +55,7 @@ namespace Avalonia.Gtk3
             ConnectEvent("leave-notify-event", OnLeaveNotifyEvent);
             Connect<Native.D.signal_generic>("destroy", OnDestroy);
             Native.GtkWidgetRealize(gtkWidget);
+            GdkWindowHandle = this.Handle.Handle;
             _lastSize = ClientSize;
             if (Gtk3Platform.UseDeferredRendering)
             {

+ 54 - 0
src/Gtk/Avalonia.Gtk3/X11.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Avalonia.Gtk3
+{
+    class X11
+    {
+        [DllImport("libX11.so.6")]
+        public static extern IntPtr XOpenDisplay(IntPtr name);
+        
+        [DllImport("libX11.so.6")]
+        public static extern IntPtr XFreeGC(IntPtr display, IntPtr gc);
+        
+        [DllImport("libX11.so.6")]
+        public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valuemask, IntPtr values);
+        
+        [DllImport("libX11.so.6")]
+        public static extern int XInitImage(ref XImage image);
+        
+        [DllImport("libX11.so.6")]
+        public static extern int XDestroyImage(ref XImage image);
+        
+        [DllImport("libX11.so.6")]
+        public static extern IntPtr XSetErrorHandler(XErrorHandler handler);
+
+        public delegate int XErrorHandler(IntPtr display, IntPtr error);
+
+        [DllImport("libX11.so.6")]
+        public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image,
+            int srcx, int srcy, int destx, int desty, uint width, uint height);
+
+        
+        public unsafe struct XImage
+        {
+            public int width, height; /* size of image */
+            public int xoffset; /* number of pixels offset in X direction */
+            public int format; /* XYBitmap, XYPixmap, ZPixmap */
+            public IntPtr data; /* pointer to image data */
+            public int byte_order; /* data byte order, LSBFirst, MSBFirst */
+            public int bitmap_unit; /* quant. of scanline 8, 16, 32 */
+            public int bitmap_bit_order; /* LSBFirst, MSBFirst */
+            public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */
+            public int depth; /* depth of image */
+            public int bytes_per_line; /* accelerator to next scanline */
+            public int bits_per_pixel; /* bits per pixel (ZPixmap) */
+            public ulong red_mask; /* bits in z arrangement */
+            public ulong green_mask;
+            public ulong blue_mask;
+            private fixed byte funcs[128];
+        }
+        
+        
+    }
+}

+ 55 - 0
src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Runtime.InteropServices;
+using Avalonia.Platform;
+
+namespace Avalonia.Gtk3
+{
+    class X11Framebuffer : ILockedFramebuffer
+    {
+        private readonly IntPtr _display;
+        private readonly IntPtr _xid;
+        private IUnmanagedBlob _blob;
+
+        public X11Framebuffer(IntPtr display, IntPtr xid, int width, int height, int factor)
+        {
+            _display = display;
+            _xid = xid;
+            Width = width*factor;
+            Height = height*factor;
+            RowBytes = Width * 4;
+            Dpi = new Vector(96, 96) * factor;
+            Format = PixelFormat.Bgra8888;
+            _blob = AvaloniaLocator.Current.GetService<IRuntimePlatform>().AllocBlob(RowBytes * Height);
+            Address = _blob.Address;
+        }
+        
+        public void Dispose()
+        {
+            var image = new X11.XImage();
+            int bitsPerPixel = 32;
+            image.width = Width;
+            image.height = Height;
+            image.format = 2; //ZPixmap;
+            image.data = Address;
+            image.byte_order = 0;// LSBFirst;
+            image.bitmap_unit = bitsPerPixel;
+            image.bitmap_bit_order = 0;// LSBFirst;
+            image.bitmap_pad = bitsPerPixel;
+            image.depth = 24;
+            image.bytes_per_line = RowBytes - Width * 4;
+            image.bits_per_pixel = bitsPerPixel;
+            X11.XInitImage(ref image);
+            var gc = X11.XCreateGC(_display, _xid, 0, IntPtr.Zero);
+            X11.XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint) Width, (uint) Height);
+            X11.XFreeGC(_display, gc);
+            _blob.Dispose();
+        }
+
+        public IntPtr Address { get; }
+        public int Width { get; }
+        public int Height { get; }
+        public int RowBytes { get; }
+        public Vector Dpi { get; }
+        public PixelFormat Format { get; }
+    }
+}