-
Notifications
You must be signed in to change notification settings - Fork 452
Make SDL3Clipboard support images #6241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
There's also the caveat that I only implemented a way to copy PNGs. Wouldn't it also need to be able to copy JPGs depending on some setting? I know osu! lets you choose the image format for screenshot savings. |
|
What you have looks pretty standard for the new age of unmanaged->managed callbacks. That being said, this PR now competes with #6223 and we should probably make sure that this implementation is going to easily lend itself to other mime types in the future. |
| unsafe | ||
| { | ||
| nuint size = 0; | ||
| byte* data = (byte*)SDL3.SDL_GetClipboardData(Encoding.UTF8.GetBytes("image/png"), &size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe SDL3-CS handles this automagically somehow, but I think you're missing a null terminator here (Pretty sure the marshaller only does this for string-typed pinvoke-parameters). The byte[] returned by Encoding.GetBytes does not include a null byte at the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would "image/png\0"u8 work better then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that should work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!
Pass unmanaged struct to callbacks and free it on cleanup
less finnicky than creating a managed string every time
Apparently I can't use C#'s collection expression syntax here :(
|
I'm testing this on windows (by commenting out the Does copying images to/from framework into external programs work on Linux? |
|
It does work on Linux (at least on my machine on X11), as long as there is a clipboard manager present. If there is no clipboard manager, the selection will disappear once the application closes, which is expected behavior. I don't know why it doesn't work on Windows though. :/ |
|
Works perfectly fine on windows if the mime type is changed to I wonder if it works on macOS (which currently uses |
At least on Wayland, the mime type also has to match. If I e.g. copy a PNG from my local storage, the current code works fine. If I use a Paint-like program to scribble around, Ctrl-A and Ctrl-C, then it's also saved as an The obvious solution would be to just have a list of image mimetypes that are tried one after another. FWIW, SDL itself also does this for the built-in |
|
I'll try to implement a multi-mime-types solution. |
|
We should probably ask ImageSharp about its supported decoder formats, see SixLabors/ImageSharp#1353 (but with This is what I get on windows: |
Susko3
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather rewrite this PR then to try and fix it.
| nuint size = 0; | ||
| byte* data = (byte*)SDL3.SDL_GetClipboardData("image/png\0"u8, &size); | ||
| if (data == null) | ||
| return null; | ||
|
|
||
| buffer = new byte[size]; | ||
| for (nuint i = 0; i < size; i++) | ||
| buffer[i] = data[i]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should use Marshal.Copy.
| private unsafe struct ClipboardImageData | ||
| { | ||
| public byte* RawBuffer; | ||
| public nuint Length; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can probably be done much cleaner with a GCHandleType.Pinned ObjectHandle<byte[]>.
Similar to:
osu-framework/osu.Framework/Audio/Sample/SampleBassFactory.cs
Lines 84 to 85 in 32e7571
| using (var handle = new ObjectHandle<byte[]>(data, GCHandleType.Pinned)) | |
| SampleId = Bass.SampleLoad(handle.Address, 0, dataLength, PlaybackConcurrency.Value, flags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing around a byte[] means the copying will happen in the callback and not before.
But I might end up using an ObjectHandle anyways to support multiple mime types. Only this time it'll need to be the Image itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is trickier than I expected.
In the SampleBassFactory code, the handle is created with using, so it gets freed immediately after SampleLoad gets called. This is fine because SampleLoad makes a copy of the data immediately - from its documentation:
There is no need to pin the memory buffer for this method, since after loading a sample from memory, the memory can safely be discarded, as a copy is made.
But I can't just free the ObjectHandle immediately after SDL_SetClipboardData gets called. Instead I have to free it inside of the cleanup callback, which will be called with the old userdata before each new call to SDL_SetClipboardData.
And unfortunately, I can't just do it manually with .Dispose() either, because disposing an object handle obtained from a nint won't actually free the object handle, we can only free it on the original one.
I feel like now I need to have two swapping global variables (due to how the cleanup logic works), which would make an object handle unnecessary...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like now I need to have two swapping global variables
Welp, this is what I have currently
| fixed (byte* pngMimetypeStr = "image/png\0"u8) | ||
| { | ||
| if (SDL3.SDL_strcmp(mimeType, pngMimetypeStr) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no reason to delegate string comparison to SDL. Better to decode the pointer with SDL3.PtrToStringUTF8 and use == on C# strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By using SDL_strcmp, I don't need to copy any data over before doing the comparison.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up doing it anyway to be able to use ImageFormatManager.TryFindFormatByMimeType :)
@Susko3 Can you tell me if this works on Windows now? |
|
Thanks for the PR, I've improved on your idea and made it into #6254. Please give it a test and see if the BMP format works for you. If not, I can change it to PNG. |






This PR supersedes #6151 which aimed at adding support for copying and pasting images from the clipboard on Linux, but had the wrong approach of calling into an external executable (
xclipfor X11).In the mean time I had actually started a native clipboard library in Rust for applications that would support any mime types and be given arbitrary data (the X11 implementation was somewhat finished, but not Wayland), but since osu!framework now uses SDL3, I can just use
SDL_GetClipboardDataandSDL_SetClipboardDataas suggested back then by @Susko3.This is a first attempt at implementing this, and on my Linux machine, the visual tests for copying images are all green! :D
Edit: actually doesn't work every time! D: I'll have to really refactor this code and make sure I understand what's going on.Edit 2: bug identified, it seems to come from the given image itself...
That said, I really don't know what to think of this code, [...]I think the code looks ok now :)