Browse Source

Merge pull request #8268 from AvaloniaUI/feature/x11-xsync-counter

[X11] Added support for the basic version of _NET_WM_SYNC_REQUEST protocol
Dan Walmsley 3 years ago
parent
commit
e4e0ba1652
4 changed files with 62 additions and 10 deletions
  1. 1 0
      src/Avalonia.X11/X11Atoms.cs
  2. 10 0
      src/Avalonia.X11/X11Info.cs
  3. 34 10
      src/Avalonia.X11/X11Window.cs
  4. 17 0
      src/Avalonia.X11/XLib.cs

+ 1 - 0
src/Avalonia.X11/X11Atoms.cs

@@ -155,6 +155,7 @@ namespace Avalonia.X11
         public readonly IntPtr _NET_FRAME_EXTENTS;
         public readonly IntPtr _NET_WM_PING;
         public readonly IntPtr _NET_WM_SYNC_REQUEST;
+        public readonly IntPtr _NET_WM_SYNC_REQUEST_COUNTER;
         public readonly IntPtr _NET_SYSTEM_TRAY_S;
         public readonly IntPtr _NET_SYSTEM_TRAY_ORIENTATION;
         public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE;

+ 10 - 0
src/Avalonia.X11/X11Info.cs

@@ -33,6 +33,7 @@ namespace Avalonia.X11
         public IntPtr LastActivityTimestamp { get; set; }
         public XVisualInfo? TransparentVisualInfo { get; set; }
         public bool HasXim { get; set; }
+        public bool HasXSync { get; set; }
         public IntPtr DefaultFontSet { get; set; }
         
         public unsafe X11Info(IntPtr display, IntPtr deferredDisplay, bool useXim)
@@ -101,6 +102,15 @@ namespace Avalonia.X11
             {
                 //Ignore, XI is not supported
             }
+
+            try
+            {
+                HasXSync = XSyncInitialize(display, out _, out _) != Status.Success;
+            }
+            catch
+            {
+                //Ignore, XSync is not supported
+            }
         }
     }
 }

+ 34 - 10
src/Avalonia.X11/X11Window.cs

@@ -45,6 +45,8 @@ namespace Avalonia.X11
         private IntPtr _handle;
         private IntPtr _xic;
         private IntPtr _renderHandle;
+        private IntPtr _xSyncCounter;
+        private XSyncValue _xSyncValue;
         private bool _mapped;
         private bool _wasMappedAtLeastOnce = false;
         private double? _scalingOverride;
@@ -190,6 +192,16 @@ namespace Avalonia.X11
                 NativeMenuExporter = DBusMenuExporter.TryCreateTopLevelNativeMenu(_handle);
             NativeControlHost = new X11NativeControlHost(_platform, this);
             InitializeIme();
+            
+            XChangeProperty(_x11.Display, _handle, _x11.Atoms.WM_PROTOCOLS, _x11.Atoms.XA_ATOM, 32,
+                PropertyMode.Replace, new[] { _x11.Atoms.WM_DELETE_WINDOW, _x11.Atoms._NET_WM_SYNC_REQUEST }, 2);
+
+            if (_x11.HasXSync)
+            {
+                _xSyncCounter = XSyncCreateCounter(_x11.Display, _xSyncValue);
+                XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_SYNC_REQUEST_COUNTER,
+                    _x11.Atoms.XA_CARDINAL, 32, PropertyMode.Replace, ref _xSyncCounter, 1);
+            }
         }
 
         class SurfaceInfo  : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
@@ -383,15 +395,7 @@ namespace Avalonia.X11
                      (ev.type == XEventName.VisibilityNotify &&
                       ev.VisibilityEvent.state < 2))
             {
-                if (!_triggeredExpose)
-                {
-                    _triggeredExpose = true;
-                    Dispatcher.UIThread.Post(() =>
-                    {
-                        _triggeredExpose = false;
-                        DoPaint();
-                    }, DispatcherPriority.Render);
-                }
+                EnqueuePaint();
             }
             else if (ev.type == XEventName.FocusIn)
             {
@@ -503,6 +507,7 @@ namespace Avalonia.X11
                 if (_useRenderWindow)
                     XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width,
                         ev.ConfigureEvent.height);
+                EnqueuePaint();
             }
             else if (ev.type == XEventName.DestroyNotify 
                      && ev.DestroyWindowEvent.window == _handle)
@@ -518,7 +523,11 @@ namespace Avalonia.X11
                         if (Closing?.Invoke() != true)
                             Dispose();
                     }
-
+                    else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST)
+                    {
+                        _xSyncValue.Lo = new UIntPtr(ev.ClientMessageEvent.ptr3.ToPointer()).ToUInt32();
+                        _xSyncValue.Hi = ev.ClientMessageEvent.ptr4.ToInt32();
+                    }
                 }
             }
             else if (ev.type == XEventName.KeyPress || ev.type == XEventName.KeyRelease)
@@ -730,9 +739,24 @@ namespace Avalonia.X11
             ScheduleInput(mev, ref ev);
         }
 
+        void EnqueuePaint()
+        {
+            if (!_triggeredExpose)
+            {
+                _triggeredExpose = true;
+                Dispatcher.UIThread.Post(() =>
+                {
+                    _triggeredExpose = false;
+                    DoPaint();
+                }, DispatcherPriority.Render);
+            }
+        }
+        
         void DoPaint()
         {
             Paint?.Invoke(new Rect());
+            if (_xSyncCounter != IntPtr.Zero)
+                XSyncSetCounter(_x11.Display, _xSyncCounter, _xSyncValue);
         }
         
         public void Invalidate(Rect rect)

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

@@ -542,6 +542,18 @@ namespace Avalonia.X11
         public static extern int XRRQueryExtension (IntPtr dpy,
             out int event_base_return,
             out int error_base_return);
+        
+        [DllImport(libX11Ext)]
+        public static extern Status XSyncInitialize(IntPtr dpy, out int event_base_return, out int error_base_return);
+
+        [DllImport(libX11Ext)]
+        public static extern IntPtr XSyncCreateCounter(IntPtr dpy, XSyncValue initialValue);
+        
+        [DllImport(libX11Ext)]
+        public static extern int XSyncDestroyCounter(IntPtr dpy, IntPtr counter);
+        
+        [DllImport(libX11Ext)]
+        public static extern int XSyncSetCounter(IntPtr dpy, IntPtr counter, XSyncValue value);
 
         [DllImport(libX11Randr)]
         public static extern int XRRQueryVersion(IntPtr dpy,
@@ -627,6 +639,11 @@ namespace Avalonia.X11
             public int bw;
             public int d;
         }
+        
+        public struct XSyncValue {
+            public int Hi;
+            public uint Lo;
+        }
 
         public static bool XGetGeometry(IntPtr display, IntPtr window, out XGeometry geo)
         {