atagen

monolith

public
Star0
Watch0

Commit dd5b0783344d

tarball

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

3 files changed+17-10
xwayland: gate X11 selection writes by the selection access setting

The setting only covered reads, so any X11 client could overwrite the Wayland clipboard and primary selection at will. new_selection now applies the same check as allow_selection_access, and the config docs say it covers both.
Modified file: crates/config/default.toml+3 additions−3 deletions
@@ -79,9 +79,9 @@ repeat_delay = 200
7979 repeat_rate = 25
8080
8181 [xwayland]
82-# Which X11 clients may read a Wayland-owned clipboard or primary selection.
83-# focused allows only the window holding keyboard focus, any allows every X11
84-# client, which clipboard managers and headless xclip need.
82+# Which X11 clients may read the Wayland clipboard or primary selection, and
83+# set it. focused allows only the window holding keyboard focus, any allows
84+# every X11 client, which clipboard managers and headless xclip need.
8585 selection_access = "focused"
8686
8787 # libinput settings, bare metal only. Anything left out keeps the device's
Modified file: crates/config/src/lib.rs+1 additions−1 deletions
@@ -72,7 +72,7 @@ impl Default for General {
7272 #[derive(Deserialize, Debug, Clone, PartialEq)]
7373 #[serde(default, deny_unknown_fields)]
7474 pub struct Xwayland {
75- /// Which X11 clients may read a Wayland-owned selection.
75+ /// Which X11 clients may read the Wayland selections and set them.
7676 pub selection_access: SelectionAccess,
7777 }
7878
Modified file: crates/monolith/src/xwayland.rs+13 additions−6 deletions
@@ -459,11 +459,7 @@ impl XwmHandler for State {
459459 }
460460
461461 fn allow_selection_access(&mut self, xwm: XwmId, _selection: SelectionTarget) -> bool {
462- self.settings.config.xwayland.selection_access == SelectionAccess::Any
463- || matches!(
464- self.input.seat.get_keyboard().and_then(|k| k.current_focus()),
465- Some(Focus::X11(x)) if x.xwm_id() == Some(xwm)
466- )
462+ self.x11_selection_allowed(xwm)
467463 }
468464
469465 fn send_selection(
@@ -503,7 +499,10 @@ impl XwmHandler for State {
503499 }
504500 }
505501
506- fn new_selection(&mut self, _xwm: XwmId, selection: SelectionTarget, mime_types: Vec<String>) {
502+ fn new_selection(&mut self, xwm: XwmId, selection: SelectionTarget, mime_types: Vec<String>) {
503+ if !self.x11_selection_allowed(xwm) {
504+ return;
505+ }
507506 let (dh, seat) = (&self.core.display, &self.input.seat);
508507 match selection {
509508 SelectionTarget::Clipboard => set_data_device_selection(dh, seat, mime_types, ()),
@@ -565,6 +564,14 @@ pub fn fixed_size(window: &X11Surface) -> Option<Size<i32, Logical>> {
565564 }
566565
567566 impl State {
567+ fn x11_selection_allowed(&self, xwm: XwmId) -> bool {
568+ self.settings.config.xwayland.selection_access == SelectionAccess::Any
569+ || matches!(
570+ self.input.seat.get_keyboard().and_then(|k| k.current_focus()),
571+ Some(Focus::X11(x)) if x.xwm_id() == Some(xwm)
572+ )
573+ }
574+
568575 /// Clears `selection` when an X11 client owns it, since a client-owned
569576 /// selection carries no user data. Xwayland dying takes the owner with it
570577 /// without a cleared_selection call.