Skip to content

clipboard data transfer api - #4658

Open
timon-schelling wants to merge 8 commits into
rust-windowing:masterfrom
timon-schelling:clipboard-wayland
Open

clipboard data transfer api#4658
timon-schelling wants to merge 8 commits into
rust-windowing:masterfrom
timon-schelling:clipboard-wayland

Conversation

@timon-schelling

@timon-schelling timon-schelling commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Add ActiveEventLoop::clipboard and ActiveEventLoop::set_clipboard for
interacting with the system clipboard through the data transfer API, implemented on Wayland, macOS and Windows.

  • Tested on all platforms changed
  • Added an entry to the changelog module if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality

Follow-up of #4571

Addition to public API consists of two new methods on ActiveEventLoop, both defaulting to NotSupported:

fn clipboard(&self) -> Result<Option<DataTransferId>, RequestError>;
fn set_clipboard(&self, send_data: Box<dyn DataTransferSend>) -> Result<(), RequestError>;

Works like the DnD API. clipboard() gives you a DataTransferId, or None if
the clipboard is empty. Pass it to data_transfer() for the types and
fetch_data_transfer() for the data, which arrives as DataTransferReceived.
set_clipboard() takes a DataTransferSend, encoded lazily when another app
asks for a type, and dropped when someone else takes over the clipboard.

Tested with new clipboard example: press C to copy, V to paste, logs the advertised types etc.
Tested with plaintext and images on Wayland (hyprland, niri and kwin), macOS 16 and Windows 11.
Also tested in Graphite's desktop app.

Changes to be aware of that are not strictly required for the feature but I think desirable:

  • dnd modules renamed to data_transfer, and DndState to
    DataTransferState
  • Fixes pre-existing bug on Wayland where a completed fetch did not set dispatched_events (wayland: mark dispatched events when a data transfer fetch completes)
  • On Windows flushs OLE clipboard on shutdown to make sure other applications can still read the data (not an expert on win32 common behavior but I found at least qt doing the same and Microsoft docs also suggest this)
  • Moved OLE initialization into a function that calls the underlying ole2.h API function at most once per thread.

@timon-schelling
timon-schelling marked this pull request as ready for review August 5, 2026 18:12

@ogoffart ogoffart left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for working on this.

One problem with the API is that this doesn't support multiple clipboard like the middle-click paste vs ctrl+c/v.

I'm thinking the clipboard and set_clipboard functions should take an enum telling what keyboard we want.

Comment thread winit-wayland/src/event_loop/mod.rs
Comment thread winit-wayland/src/data_transfer.rs Outdated
@timon-schelling

Copy link
Copy Markdown
Contributor Author

Thanks a lot for working on this.

One problem with the API is that this doesn't support multiple clipboard like the middle-click paste vs ctrl+c/v.

I'm thinking the clipboard and set_clipboard functions should take an enum telling what keyboard we want.

Thank for the review.
Assuming you mean clipboard not keyboard.

Isn't the primary selection just a Linux thing?
I assumed that it should only be accessible via platform extension trait and that it should not really influence the main API design?

If it should be part of the general API I would also suggest taking an enum with Clipboard::Selection and Clipboard::Primary.

@ogoffart

ogoffart commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Yes, I meant clipboard.
Yes, it is a linux thing.
Selection and Primary are the same thing.
But yes, an enum such as Clipboard::Selection and Clipboard::Clipboard. (i'm not very good at naming things)

@timon-schelling

Copy link
Copy Markdown
Contributor Author

Yes, I meant clipboard.
Yes, it is a linux thing.
Selection and Primary are the same thing.
But yes, an enum such as Clipboard::Selection and Clipboard::Clipboard. (i'm not very good at naming things)

An you would prefer that being part of the cross platform api (would be easier to implement I guess)?
And not a ActiveEventLoopClipboardPrimaryExt:: clipboard_primary?

If it should be a cross platform thing what should platforms that don't have the primary selection do? Return unsupported?

@kchibisov

Copy link
Copy Markdown
Member

I think one other point is that this API should be brought to every platform that is supported by other clipboard crates, so x11/wayland/windows/macOS at least, so users who rely on special crates for clipboard can just replace them with winit clipboard...

And yeah, on linux we'd need an extra trait for set_primary_clipboard etc,

@timon-schelling

Copy link
Copy Markdown
Contributor Author

I'm working on/thinking about the mac implementation, and I could also work on x11 (my testing would be limited to Xwayland) but not sure when I would be able to work on Windows, likely better if someone else with more Windows knowledge works on that.

Personally I would prefer every platform being in it's own (stacked) PR but if maintainers prefer this being one giant PR sure.

And I will add a trait for primary selection, definitely the shape I prefer.

@timon-schelling timon-schelling changed the title wayland: clipboard data transfer api clipboard data transfer api Aug 6, 2026
@timon-schelling

Copy link
Copy Markdown
Contributor Author

appkit implementation is now in shape I'm happy with, less complicated than initially anticipated. I'm not making good progress with x11 though and haven't looked much into the windows side.

@timon-schelling
timon-schelling force-pushed the clipboard-wayland branch 2 times, most recently from 9006b1a to 2d95644 Compare August 18, 2026 00:40
Some(window_id) => state.events_sink.push_window_event(event, window_id),
None => {
// Clipboard is not tied to a window, dispatch to all.
for window_id in state.windows.borrow().keys() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a bit silly. Maybe this should be an application-wide event? Or is that out of scope for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data transfer received event for DnD has associated window. I think that's the reason why it was originally designed like this.

So I have to associate the event with a window when using the preexisting event.

I could make the the Event not associated with a window but that makes using the DnD side worse.

Or I associate a window with the clipboard data transfer received events. In this case using the focused window instead of dipatching for all windows is an option (if desired).

Personally I would like to changed the DnD side to not have the window association with the recive event. Applications would be able to track that themselves via the data transfer id. But that requires adding a handler for application events. Is that a direction maintainers would be open to, or should I keep it window-scoped for this PR?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this feels like the most sensible approach, to add a handler per application rather than per window.

@0HyperCube

0HyperCube commented Sep 1, 2026

Copy link
Copy Markdown

Hi, I'm collaborating with Timon into adding the X11 backend. I have a mostly working implementation at timon-schelling#1.

X11 requires an event to get the possible TARGETS for pasting such as STRING, text/html, etc. The result is delivered as a separate event some time later. This makes it impossible to integrate with the current API where the types are immediately available when pasting. Using the XFixes extension it is possible to listen for when the selection changes to request the updated TARGETS. Would it be fine to add a dependency on the XFixes extension for this purpose?

To avoid XFixes, it would be possible to poll to see where the selection changes. Alternatively the API could be adjusted so that users do not know the available types when issuing the paste request (although this would likely be annoying).

I have not currently added support for the MULITPLE target. It is not supported by xclip and I'm not confident I understand how it works without a reference implementation. Is it desired to support this? For pasting multiple types, I currently have implemented a queue system. I think it is fine to ignore it as well behaving clients should not request it since support is not advertised.

Also when the program ends and it owned the clipboard, it is not possible to paste. I think it is possible to send the data to a clipboard manager when closing. Is support for this desired?

Unfortunately the X11 implementation is already quite long and complex. A lot of this is due to support for the INCR mechanism and because X11 is really quite annoying and verbose to deal with. Any advice on simplifying it would be welcome. Thanks for your time looking into this.

@kchibisov

Copy link
Copy Markdown
Member

X11 requires an event to get the possible TARGETS for pasting such as STRING, text/html, etc. The result is delivered as a separate event some time later. This makes it impossible to integrate with the current API where the types are immediately available when pasting. Using the XFixes extension it is possible to listen for when the selection changes to request the updated TARGETS. Would it be fine to add a dependency on the XFixes extension for this purpose?

Isn't this is the same as Wayland here? But besides, I think xfixes is available in a lot of places so maybe fine.

@0HyperCube

Copy link
Copy Markdown

Isn't this is the same as Wayland here?

I don't think so? The code in this PR does something like:

let offer = seat.data_device()?.data().selection_offer()?;
offer.with_mime_types(|types| {
    types.iter().map(|mime| MimeType::parse(mime.clone())).collect()
});

Which appears inspects the mime types synchronously.

But besides, I think xfixes is available in a lot of places so maybe fine.

Sounds good; thanks for the input @kchibisov.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants