Commit 63126b8ac7c9
tarball1 file changed+64-13
@@ -9,7 +9,7 @@ use crate::state::State;
99
use monolith_config::SelectionAccess;1010
use smithay::desktop::Window;1111
use smithay::reexports::calloop::generic::Generic;12
-use smithay::reexports::calloop::{Interest, Mode, PostAction, RegistrationToken};12
+use smithay::reexports::calloop::{Interest, LoopHandle, Mode, PostAction, RegistrationToken};1313
use smithay::reexports::rustix::fs::{OFlags, fcntl_setfl};1414
use smithay::reexports::rustix::io::{FdFlags, fcntl_getfd, fcntl_setfd};1515
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;@@ -26,7 +26,8 @@ use smithay::wayland::selection::primary_selection::{
2626
use smithay::wayland::xwayland_shell::{XWaylandShellHandler, XWaylandShellState};2727
use smithay::xwayland::xwm::{Reorder, ResizeEdge, WmWindowProperty, WmWindowType, XwmId};2828
use smithay::xwayland::{X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler};29
-use std::io::{Read, Write};29
+use std::fs::File;30
+use std::io::{ErrorKind, Read, Write};3031
use std::os::fd::AsFd;3132
use std::os::fd::OwnedFd;3233
use std::os::linux::net::SocketAddrExt;@@ -40,6 +41,10 @@ const UTF8_TEXT: [&str; 4] = [
4041
"text/plain",4142
];4243
44
+/// The most text a STRING request relays, so a source that never stops cannot45
+/// grow the compositor without bound.46
+const RELAY_CAP: usize = 16 << 20;47
+4348
fn utf8_source(mimes: &[String]) -> Option<&str> {4449
UTF8_TEXT.into_iter().find(|t| mimes.iter().any(|m| m == t))4550
}@@ -61,19 +66,65 @@ fn latin1(text: &[u8]) -> Vec<u8> {
6166
}6267
6368
/// Hands back the write end of a pipe whose UTF-8 contents reach `out` as64
-/// Latin-1, which is what ICCCM says STRING holds.65
-fn latin1_relay(out: OwnedFd) -> std::io::Result<OwnedFd> {66
- let (mut read, write) = std::io::pipe()?;67
- fcntl_setfl(&out, OFlags::empty())?;68
- std::thread::spawn(move || {69
- let mut text = Vec::new();70
- if read.read_to_end(&mut text).is_ok() {71
- let _ = std::fs::File::from(out).write_all(&latin1(&text));72
- }73
- });69
+/// Latin-1, which is what ICCCM says STRING holds. Text past `RELAY_CAP` is70
+/// dropped along with the transfer.71
+fn latin1_relay(handle: &LoopHandle<'static, State>, out: OwnedFd) -> std::io::Result<OwnedFd> {72
+ let (read, write) = std::io::pipe()?;73
+ fcntl_setfl(&read, OFlags::NONBLOCK)?;74
+ fcntl_setfl(&out, OFlags::NONBLOCK)?;75
+ let mut out = Some(File::from(out));76
+ let mut text = Vec::new();77
+ let next = handle.clone();78
+ handle79
+ .insert_source(80
+ Generic::new(read, Interest::READ, Mode::Level),81
+ move |_, read, _| {82
+ let mut chunk = [0u8; 8192];83
+ loop {84
+ match (&**read).read(&mut chunk) {85
+ Ok(0) => break,86
+ Ok(n) if text.len() + n > RELAY_CAP => return Ok(PostAction::Remove),87
+ Ok(n) => text.extend_from_slice(&chunk[..n]),88
+ Err(e) if e.kind() == ErrorKind::Interrupted => {}89
+ Err(e) if e.kind() == ErrorKind::WouldBlock => {90
+ return Ok(PostAction::Continue);91
+ }92
+ Err(_) => return Ok(PostAction::Remove),93
+ }94
+ }95
+ if let Some(out) = out.take() {96
+ write_when_ready(&next, out, latin1(&text));97
+ }98
+ Ok(PostAction::Remove)99
+ },100
+ )101
+ .map_err(|e| std::io::Error::other(e.to_string()))?;74102
Ok(write.into())75103
}76104
105
+fn write_when_ready(handle: &LoopHandle<'static, State>, out: File, data: Vec<u8>) {106
+ let mut sent = 0;107
+ let inserted = handle.insert_source(108
+ Generic::new(out, Interest::WRITE, Mode::Level),109
+ move |_, out, _| {110
+ while sent < data.len() {111
+ match (&**out).write(&data[sent..]) {112
+ Ok(n) => sent += n,113
+ Err(e) if e.kind() == ErrorKind::Interrupted => {}114
+ Err(e) if e.kind() == ErrorKind::WouldBlock => {115
+ return Ok(PostAction::Continue);116
+ }117
+ Err(_) => break,118
+ }119
+ }120
+ Ok(PostAction::Remove)121
+ },122
+ );123
+ if let Err(e) = inserted {124
+ tracing::warn!("cannot relay STRING to X11: {e}");125
+ }126
+}127
+77128
pub struct Xwayland {78129
pub display: u32,79130
listeners: Vec<UnixListener>,@@ -427,7 +478,7 @@ impl XwmHandler for State {
427478
let mimes = x.wayland_selection(selection).as_deref()?;428479
utf8_source(mimes).map(str::to_owned)429480
});430
- let relay = source.map(|s| latin1_relay(fd).map(|w| (s, w)));481
+ let relay = source.map(|s| latin1_relay(&self.core.handle, fd).map(|w| (s, w)));431482
match relay {432483
Some(Ok(relay)) => relay,433484
Some(Err(e)) => {