SFML.Net
SFML.Net copied to clipboard
Clipboard causing app to freeze for 1-2 seconds
I've had numerous performance issues when getting SFML's Clipboard every frame (especially on Windows (10 FPS), on Linux it wasn't as bad (70 FPS), which may be hardware related) and I mostly worked around that by caching the Clipboard on my side upon window focus. Which lead me to the following issue.
A very bad hiccup occurs when getting SFML.Window.Clipboard.Contents while the contents are not text (image pixels for example). On my Linux machine, the app freezes for about 1 or 2 seconds.
From what I saw in the binding implementation - SFML assumes the clipboard contents are always text and does the parsing all the time. Perhaps there may be an OS-dependent way to check whether the clipboard contents are text before parsing the data and skip doing so if non-textual data is present.
Minimal example:
- Make sure to copy image pixels from some image editor software beforehand
- Screenshot may also work
using SFML.Graphics;
using SFML.System;
using SFML.Window;
namespace MinimalExampleClipboardIssue;
public class Program
{
public static void Main()
{
var window = new RenderWindow(new(1280, 720), "Clipboard Hiccup Issue");
var rect = new RectangleShape(new Vector2f(200, 200));
window.GainedFocus += (_, _) => _ = Clipboard.Contents;
while (window.IsOpen)
{
rect.Position = window.MapPixelToCoords(Mouse.GetPosition(window));
window.DispatchEvents();
window.Clear();
window.Draw(rect);
window.Display();
}
}
}
On X Server clipboard getting is a request sent to another process to send over the data (that's why for some programs copying then closing it will remove the data and prevent you from copy pasting).
SFML waits for 1 second: https://github.com/SFML/SFML/blob/master/src/SFML/Window/Unix/ClipboardImpl.cpp#L145
I looked and SDL has similar wait (also 1 second) but also has some code to not wait again if called again (I didn't look into logic of it, I don't want to read SDL code too much if I contribute to SFML).
More info (I don't know if up to date but I assume so, X is pretty stable/legacy): https://www.uninformativ.de/blog/postings/2017-04-02/0/POSTING-en.html
In case anyone else comes across this (which I doubt), here is my working solution for the time being:
public string Clipboard { get; set; }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int XInitThreadsDelegate();
bool shouldGetClipboard;
SomeInitFunction()
{
window.GainedFocus += (_, _) => shouldGetClipboard = true;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && NativeLibrary.TryLoad("libX11.so.6", out var libX11))
{
var funcPtr = NativeLibrary.GetExport(libX11, "XInitThreads");
var func = Marshal.GetDelegateForFunctionPointer<XInitThreadsDelegate>(funcPtr);
var result = func.Invoke(); // 1 should be ok, 0 fail
}
var thread = new Thread(() =>
{
while (true)
{
if (shouldGetClipboard)
{
shouldGetClipboard = false;
Clipboard = SFML.Window.Clipboard.Contents;
}
Thread.Sleep(100);
}
});
thread.Start();
}