diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs
index 582608045f..c92f97e01d 100644
--- a/src/Avalonia.X11/X11Platform.cs
+++ b/src/Avalonia.X11/X11Platform.cs
@@ -370,6 +370,8 @@ namespace Avalonia
///
public bool? UseRetainedFramebuffer { get; set; }
+ public bool? UseXShmFramebuffer { get; set; }
+
public X11PlatformOptions()
{
try
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 6c1b381a40..cda422ae9a 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -26,6 +26,7 @@ using Avalonia.Input.Platform;
using System.Runtime.InteropServices;
using Avalonia.Dialogs;
using Avalonia.Platform.Storage.FileIO;
+using Avalonia.X11.XShmExtensions;
// ReSharper disable IdentifierTypo
// ReSharper disable StringLiteralTypo
@@ -195,6 +196,13 @@ namespace Avalonia.X11
new X11FramebufferSurface(_x11.DeferredDisplay, _renderHandle,
depth, _platform.Options.UseRetainedFramebuffer ?? false)
};
+
+ if (_platform.Options.UseXShmFramebuffer is true)
+ {
+ var x11ShmFramebufferSurface = new X11ShmFramebufferSurface(this, _x11.DeferredDisplay, _handle, _renderHandle);
+ _x11ShmFramebufferSurface = x11ShmFramebufferSurface;
+ surfaces.Insert(0, x11ShmFramebufferSurface);
+ }
if (egl != null)
surfaces.Insert(0,
@@ -611,6 +619,12 @@ namespace Avalonia.X11
return;
HandleKeyEvent(ref ev);
}
+ else if (ev.type == XEventName.XShmCompletionEvent)
+ {
+ // Because the XShmCompletionEvent only occurs when the XShmPutImage is called, and the XShmPutImage only be called when use X11ShmFramebufferSurface, so we can safely assume that the _x11ShmFramebufferSurface is not null.
+ Debug.Assert(_x11ShmFramebufferSurface!=null, "If application can receive the XShmCompletionEvent, that the _x11ShmFramebufferSurface must not be null.");
+ _x11ShmFramebufferSurface?.OnXShmCompletionEvent(ref ev);
+ }
}
private Thickness? GetFrameExtents()
@@ -798,6 +812,7 @@ namespace Avalonia.X11
new PixelSize(MaxWindowDimension, MaxWindowDimension));
private double _scaling = 1;
+ private X11ShmFramebufferSurface? _x11ShmFramebufferSurface;
private void ScheduleInput(RawInputEventArgs args, ref XEvent xev)
{
diff --git a/src/Avalonia.X11/XShmExtensions/X11ShmImage.cs b/src/Avalonia.X11/XShmExtensions/X11ShmImage.cs
index f55adb955f..7cfc5c0866 100644
--- a/src/Avalonia.X11/XShmExtensions/X11ShmImage.cs
+++ b/src/Avalonia.X11/XShmExtensions/X11ShmImage.cs
@@ -9,16 +9,51 @@ using Avalonia.Platform;
namespace Avalonia.X11.XShmExtensions;
+class X11ShmFramebufferContext
+{
+ public X11ShmFramebufferContext(X11Window x11Window, IntPtr display, IntPtr windowHandle, IntPtr renderHandle)
+ {
+ X11Window = x11Window;
+ Display = display;
+ WindowHandle = windowHandle;
+ RenderHandle = renderHandle;
+ }
+
+ public X11Window X11Window { get; }
+
+ public IntPtr Display { get; }
+
+ public IntPtr WindowHandle { get; }
+
+ public IntPtr RenderHandle { get; }
+}
+
internal class X11ShmFramebufferSurface : IFramebufferPlatformSurface
{
+ public X11ShmFramebufferSurface(X11Window x11Window, IntPtr display, IntPtr windowHandle, IntPtr renderHandle)
+ {
+ _context = new X11ShmFramebufferContext(x11Window, display, windowHandle, renderHandle);
+ }
+
+ private readonly X11ShmFramebufferContext _context;
+
public IFramebufferRenderTarget CreateFramebufferRenderTarget()
{
- return new X11ShmImageSwapchain();
+ return new X11ShmImageSwapchain(_context);
+ }
+
+ public void OnXShmCompletionEvent(ref XEvent ev)
+ {
+
}
}
internal class X11ShmImage
{
+ ///
+ /// Returns false if we haven't got a completion event since the last Present, can call ProcessPendingEvents here
+ ///
+ public bool IsReady { get; }
}
internal class X11ShmImageManager
@@ -26,20 +61,47 @@ internal class X11ShmImageManager
}
-class DeferredDisplayEvents
-{
+//class DeferredDisplayEvents
+//{
-}
+//}
internal class X11ShmImageSwapchain : IFramebufferRenderTarget
{
+ public X11ShmImageSwapchain(X11ShmFramebufferContext context)
+ {
+ _context = context;
+ }
+
+ private readonly X11ShmFramebufferContext _context;
+
+ private Queue _availableQueue = new();
+ private Queue _presentationQueue = new();
+ private PixelSize? _lastSize;
+
public void Dispose()
{
}
+ private void DrainPresentationQueue()
+ {
+ while (_presentationQueue.Count > 0 && _presentationQueue.Peek().IsReady)
+ {
+ _availableQueue.Enqueue(_presentationQueue.Dequeue());
+ }
+ }
+
public ILockedFramebuffer Lock()
{
+ /*
+ 1) gets the current window geometry, if it doesn't match the lastSize disposes images and clears queues
+ 2) calls DrainPresentationQueue();
+ 3) if there is an image in _availableQueue - use this image
+ 4) else if there are no images in _availableQueue and presentationQueue.Count < swapchainSize - create new image
+ 5) else synchronously wait for the first image from _presentationQueue and use it
+ 6) return a framebuffer associated with chosen image
+ */
throw new NotImplementedException();
}
}