atagen

monolith

public
Star0
Watch0

Commit 3de753c486d2

tarball

atagen <boss@atagen.co>2026-10-19 07:56 UTC

2 files changed+30-3
xwayland: replay Wayland selections when the xwm starts

Xwayland starts lazily, so a copy made before the first X11 client connected reached no xwm and X11 apps saw nothing until the next copy. Xwayland now remembers the mime types of the current Wayland-owned clipboard and primary selections and hands them to X11Wm::new_selection on Ready.
Modified file: crates/monolith/src/seat.rs+7 additions−2 deletions
@@ -47,8 +47,13 @@ impl SelectionHandler for State {
4747 source: Option<SelectionSource>,
4848 _seat: Seat<Self>,
4949 ) {
50- if let Some(wm) = self.services.xwayland.as_mut().and_then(|x| x.wm.as_mut())
51- && let Err(e) = wm.new_selection(ty, source.map(|s| s.mime_types()))
50+ let mimes = source.map(|s| s.mime_types());
51+ let Some(x) = self.services.xwayland.as_mut() else {
52+ return;
53+ };
54+ *x.wayland_selection(ty) = mimes.clone();
55+ if let Some(wm) = x.wm.as_mut()
56+ && let Err(e) = wm.new_selection(ty, mimes)
5257 {
5358 tracing::warn!("cannot set the X11 {ty:?} selection: {e:?}");
5459 }
Modified file: crates/monolith/src/xwayland.rs+23 additions−1 deletions
@@ -37,6 +37,17 @@ pub struct Xwayland {
3737 pub wm: Option<X11Wm>,
3838 /// Override-redirect windows such as menus and tooltips, bottom to top.
3939 pub unmanaged: Vec<X11Surface>,
40+ wayland_clipboard: Option<Vec<String>>,
41+ wayland_primary: Option<Vec<String>>,
42+}
43+
44+impl Xwayland {
45+ pub fn wayland_selection(&mut self, ty: SelectionTarget) -> &mut Option<Vec<String>> {
46+ match ty {
47+ SelectionTarget::Clipboard => &mut self.wayland_clipboard,
48+ SelectionTarget::Primary => &mut self.wayland_primary,
49+ }
50+ }
4051 }
4152
4253 impl Drop for Xwayland {
@@ -130,6 +141,8 @@ pub fn init(state: &mut State) {
130141 running: false,
131142 wm: None,
132143 unmanaged: Vec::new(),
144+ wayland_clipboard: None,
145+ wayland_primary: None,
133146 });
134147 }
135148
@@ -203,9 +216,14 @@ fn start(state: &mut State, fired: usize) {
203216 XWaylandEvent::Ready { x11_socket, .. } => {
204217 let dh = state.core.display.clone();
205218 match X11Wm::start_wm(state.core.handle.clone(), &dh, x11_socket, client.clone()) {
206- Ok(wm) => {
219+ Ok(mut wm) => {
207220 tracing::info!("Xwayland ready");
208221 if let Some(x) = state.services.xwayland.as_mut() {
222+ for ty in [SelectionTarget::Clipboard, SelectionTarget::Primary] {
223+ if let Some(mimes) = x.wayland_selection(ty).clone() {
224+ logged("selection", wm.new_selection(ty, Some(mimes)));
225+ }
226+ }
209227 x.wm = Some(wm);
210228 }
211229 }
@@ -371,6 +389,10 @@ impl XwmHandler for State {
371389 SelectionTarget::Clipboard => set_data_device_selection(dh, seat, mime_types, ()),
372390 SelectionTarget::Primary => set_primary_selection(dh, seat, mime_types, ()),
373391 }
392+ // set_*_selection echoes through SelectionHandler::new_selection, but an X11 owner dies with Xwayland and must not be replayed
393+ if let Some(x) = self.services.xwayland.as_mut() {
394+ *x.wayland_selection(selection) = None;
395+ }
374396 }
375397
376398 fn cleared_selection(&mut self, _xwm: XwmId, selection: SelectionTarget) {