diff --git a/Cargo.lock b/Cargo.lock index 408f6f16..8c906b72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -665,6 +665,19 @@ dependencies = [ "ahash", ] +[[package]] +name = "jay-clientmem" +version = "0.1.0" +dependencies = [ + "jay-cpu-worker", + "jay-gfx-types", + "jay-tracy", + "jay-utils", + "log", + "thiserror", + "uapi", +] + [[package]] name = "jay-cmm" version = "0.1.0" @@ -701,6 +714,7 @@ dependencies = [ "jay-async-engine", "jay-bufio", "jay-bugs", + "jay-clientmem", "jay-cmm", "jay-config", "jay-cpu-worker", diff --git a/Cargo.toml b/Cargo.toml index 6fa6ee7a..308701d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ members = [ "video-types", "gfx-types", "theme", + "clientmem", "pango", "libinput", "toml-config", @@ -93,6 +94,7 @@ jay-logger = { version = "0.1.0", path = "logger" } jay-video-types = { version = "0.1.0", path = "video-types" } jay-gfx-types = { version = "0.1.0", path = "gfx-types" } jay-theme = { version = "0.1.0", path = "theme" } +jay-clientmem = { version = "0.1.0", path = "clientmem" } jay-pango = { version = "0.1.0", path = "pango" } jay-libinput = { version = "0.1.0", path = "libinput" } @@ -158,4 +160,4 @@ opt-level = 3 [features] rc_tracking = [] it = ["jay-async-engine/it", "jay-cpu-worker/it"] -tracy = ["jay-tracy/tracy", "jay-async-engine/tracy", "jay-cpu-worker/tracy"] +tracy = ["jay-tracy/tracy", "jay-async-engine/tracy", "jay-cpu-worker/tracy", "jay-clientmem/tracy"] diff --git a/clientmem/Cargo.toml b/clientmem/Cargo.toml new file mode 100644 index 00000000..91a42d15 --- /dev/null +++ b/clientmem/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "jay-clientmem" +version = "0.1.0" +edition = "2024" +license = "GPL-3.0-only" + +[dependencies] +jay-cpu-worker = { version = "0.1.0", path = "../cpu-worker" } +jay-gfx-types = { version = "0.1.0", path = "../gfx-types" } +jay-tracy = { version = "0.1.0", path = "../tracy" } +jay-utils = { version = "0.1.0", path = "../utils" } + +log = "0.4.20" +thiserror = "2.0.11" +uapi = "0.2.13" + +[features] +tracy = ["jay-tracy/tracy"] diff --git a/clientmem/src/lib.rs b/clientmem/src/lib.rs new file mode 100644 index 00000000..7b947944 --- /dev/null +++ b/clientmem/src/lib.rs @@ -0,0 +1,331 @@ +use { + jay_cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker}, + jay_gfx_types::{ShmMemory, ShmMemoryBacking}, + jay_utils::{ + oserror::{OsError, OsErrorExt2}, + page_size::page_size, + vec_ext::VecExt, + }, + std::{ + cell::Cell, + error::Error, + mem::{ManuallyDrop, MaybeUninit}, + ops::Deref, + ptr, + rc::Rc, + sync::atomic::{Ordering, compiler_fence}, + }, + thiserror::Error, + uapi::{ + OwnedFd, Pod, + c::{self, raise}, + ftruncate, + }, +}; + +#[derive(Copy, Clone, Debug)] +pub struct ClientMemClient<'a> { + pub comm: &'a str, + pub id: u64, +} + +#[derive(Debug, Error)] +pub enum ClientMemError { + #[error("Could not install the sigbus handler")] + SigactionFailed(#[source] jay_utils::oserror::OsError), + #[error("A SIGBUS occurred while accessing mapped memory")] + Sigbus, + #[error("mmap failed")] + MmapFailed(#[source] jay_utils::oserror::OsError), + #[error("Length was not a multiple of the data element size")] + InvalidLength, +} + +pub struct ClientMem { + fd: ManuallyDrop>, + failed: Cell, + sigbus_impossible: bool, + data: *const [Cell], + cpu: Option>, +} + +#[derive(Clone)] +pub struct ClientMemOffset { + mem: Rc, + offset: usize, + data: *const [Cell], +} + +impl ClientMem { + pub fn new( + fd: &Rc, + len: usize, + read_only: bool, + client: Option>, + cpu: Option<&Rc>, + is_udmabuf: bool, + ) -> Result { + Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED, is_udmabuf) + } + + pub fn new_private( + fd: &Rc, + len: usize, + read_only: bool, + client: Option>, + cpu: Option<&Rc>, + ) -> Result { + Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE, false) + } + + fn new2( + fd: &Rc, + len: usize, + read_only: bool, + client: Option>, + cpu: Option<&Rc>, + flags: c::c_int, + is_udmabuf: bool, + ) -> Result { + let mut sigbus_impossible = is_udmabuf; + let mut real_size = None; + if !sigbus_impossible + && let Ok(seals) = uapi::fcntl_get_seals(fd.raw()) + && seals & c::F_SEAL_SHRINK != 0 + && let Ok(stat) = uapi::fstat(fd.raw()) + { + real_size = Some(stat.st_size as usize); + sigbus_impossible = stat.st_size as u64 >= len as u64; + } + if !sigbus_impossible && let Some(client) = client { + log::debug!( + "Client {} ({}) has created a shm buffer that might cause SIGBUS", + client.comm, + client.id, + ); + } + let len = len.next_multiple_of(page_size()); + if let Some(real_size) = real_size + && real_size < len + { + let _ = ftruncate(fd.raw(), len as _); + } + let data = if len == 0 { + &mut [][..] + } else { + let prot = match read_only { + true => c::PROT_READ, + false => c::PROT_READ | c::PROT_WRITE, + }; + unsafe { + let data = c::mmap64(ptr::null_mut(), len, prot, flags, fd.raw(), 0); + if data == c::MAP_FAILED { + return Err(ClientMemError::MmapFailed(OsError::default())); + } + std::slice::from_raw_parts_mut(data as *mut Cell, len) + } + }; + Ok(Self { + fd: ManuallyDrop::new(fd.clone()), + failed: Cell::new(false), + sigbus_impossible, + data, + cpu: cpu.cloned(), + }) + } + + pub fn len(&self) -> usize { + self.data.len() + } + + pub fn offset(self: &Rc, offset: usize, len: usize) -> ClientMemOffset { + let mem = unsafe { &*self.data }; + ClientMemOffset { + mem: self.clone(), + offset, + data: &mem[offset..][..len], + } + } + + pub fn fd(&self) -> &Rc { + &self.fd + } + + pub fn is_sealed_memfd(&self) -> bool { + self.sigbus_impossible + } +} + +impl ClientMemOffset { + pub fn pool(&self) -> &ClientMem { + &self.mem + } + + pub fn offset(&self) -> usize { + self.offset + } + + pub fn ptr(&self) -> *const [Cell] { + self.data + } + + pub fn access]) -> T>(&self, f: F) -> Result { + unsafe { + if self.mem.sigbus_impossible { + return Ok(f(&*self.data)); + } + let mref = MemRef { + mem: &*self.mem, + outer: MEM.get(), + }; + MEM.set(&mref); + compiler_fence(Ordering::SeqCst); + let res = f(&*self.data); + MEM.set(mref.outer); + compiler_fence(Ordering::SeqCst); + match self.mem.failed.get() { + true => Err(ClientMemError::Sigbus), + _ => Ok(res), + } + } + } + + pub fn read(&self, dst: &mut Vec) -> Result<(), ClientMemError> { + if self.data.len().checked_rem(std::mem::size_of::()) != Some(0) { + return Err(ClientMemError::InvalidLength); + } + self.access(|v| { + let len_elements = v.len() / std::mem::size_of::(); + dst.reserve(len_elements); + let (_, unused) = dst.split_at_spare_mut_bytes_ext(); + unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v)); + unsafe { + dst.set_len(dst.len() + len_elements); + } + }) + } +} + +impl Drop for ClientMem { + fn drop(&mut self) { + let fd = unsafe { ManuallyDrop::take(&mut self.fd) }; + if let Some(cpu) = &self.cpu { + let pending = cpu.submit(Box::new(CloseMemWork { + fd: Rc::try_unwrap(fd).ok(), + data: self.data, + })); + pending.detach(); + } else { + unsafe { + c::munmap(self.data as _, self.len()); + } + } + } +} + +struct MemRef { + mem: *const ClientMem, + outer: *const MemRef, +} + +thread_local! { + static MEM: Cell<*const MemRef> = const { Cell::new(ptr::null()) }; +} + +unsafe fn kill() -> ! { + unsafe { + c::signal(c::SIGBUS, c::SIG_DFL); + raise(c::SIGBUS); + } + unreachable!(); +} + +unsafe extern "C" fn sigbus(sig: i32, info: &c::siginfo_t, _ucontext: *mut c::c_void) { + unsafe { + assert_eq!(sig, c::SIGBUS); + let mut memr_ptr = MEM.get(); + while !memr_ptr.is_null() { + let memr = &*memr_ptr; + let mem = &*memr.mem; + let lo = mem.data as *mut u8 as usize; + let hi = lo + mem.len(); + let fault_addr = info.si_addr() as usize; + if fault_addr < lo || fault_addr >= hi { + memr_ptr = memr.outer; + continue; + } + let res = c::mmap64( + lo as _, + hi - lo, + c::PROT_WRITE | c::PROT_READ, + c::MAP_ANONYMOUS | c::MAP_PRIVATE | c::MAP_FIXED, + -1, + 0, + ); + if res == c::MAP_FAILED { + kill(); + } + mem.failed.set(true); + return; + } + kill(); + } +} + +pub fn init() -> Result<(), ClientMemError> { + unsafe { + let mut action: c::sigaction = MaybeUninit::zeroed().assume_init(); + action.sa_sigaction = + sigbus as unsafe extern "C" fn(i32, &c::siginfo_t, *mut c::c_void) as _; + action.sa_flags = c::SA_NODEFER | c::SA_SIGINFO; + let res = c::sigaction(c::SIGBUS, &action, ptr::null_mut()); + uapi::map_err!(res) + .map(drop) + .map_os_err(ClientMemError::SigactionFailed) + } +} + +struct CloseMemWork { + fd: Option, + data: *const [Cell], +} + +unsafe impl Send for CloseMemWork {} + +impl CpuJob for CloseMemWork { + fn work(&mut self) -> &mut dyn CpuWork { + self + } + + fn completed(self: Box) { + // nothing + } +} + +impl CpuWork for CloseMemWork { + fn run(&mut self) -> Option> { + jay_tracy::zone!("CloseMemWork"); + self.fd.take(); + unsafe { + c::munmap(self.data as _, self.data.len()); + } + None + } +} + +impl ShmMemory for ClientMemOffset { + fn len(&self) -> usize { + self.data.len() + } + + fn safe_access(&self) -> ShmMemoryBacking { + match self.mem.is_sealed_memfd() { + true => ShmMemoryBacking::Ptr(self.data), + false => ShmMemoryBacking::Fd(self.mem.fd.deref().clone(), self.offset), + } + } + + fn access(&self, f: &mut dyn FnMut(&[Cell])) -> Result<(), Box> { + self.access(f).map_err(|e| e.into()) + } +} diff --git a/src/clientmem.rs b/src/clientmem.rs index 681713da..39eb4ef9 100644 --- a/src/clientmem.rs +++ b/src/clientmem.rs @@ -1,329 +1,8 @@ -use { - crate::{ - client::Client, - cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker}, - gfx_api::{ShmMemory, ShmMemoryBacking}, - utils::{ - oserror::{OsError, OsErrorExt2}, - page_size::page_size, - vec_ext::VecExt, - }, - }, - std::{ - cell::Cell, - error::Error, - mem::{ManuallyDrop, MaybeUninit}, - ops::Deref, - ptr, - rc::Rc, - sync::atomic::{Ordering, compiler_fence}, - }, - thiserror::Error, - uapi::{ - OwnedFd, Pod, - c::{self, raise}, - ftruncate, - }, -}; +pub use jay_clientmem::*; -#[derive(Debug, Error)] -pub enum ClientMemError { - #[error("Could not install the sigbus handler")] - SigactionFailed(#[source] crate::utils::oserror::OsError), - #[error("A SIGBUS occurred while accessing mapped memory")] - Sigbus, - #[error("mmap failed")] - MmapFailed(#[source] crate::utils::oserror::OsError), - #[error("Length was not a multiple of the data element size")] - InvalidLength, -} - -pub struct ClientMem { - fd: ManuallyDrop>, - failed: Cell, - sigbus_impossible: bool, - data: *const [Cell], - cpu: Option>, -} - -#[derive(Clone)] -pub struct ClientMemOffset { - mem: Rc, - offset: usize, - data: *const [Cell], -} - -impl ClientMem { - pub fn new( - fd: &Rc, - len: usize, - read_only: bool, - client: Option<&Client>, - cpu: Option<&Rc>, - is_udmabuf: bool, - ) -> Result { - Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED, is_udmabuf) - } - - pub fn new_private( - fd: &Rc, - len: usize, - read_only: bool, - client: Option<&Client>, - cpu: Option<&Rc>, - ) -> Result { - Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE, false) - } - - fn new2( - fd: &Rc, - len: usize, - read_only: bool, - client: Option<&Client>, - cpu: Option<&Rc>, - flags: c::c_int, - is_udmabuf: bool, - ) -> Result { - let mut sigbus_impossible = is_udmabuf; - let mut real_size = None; - if !sigbus_impossible - && let Ok(seals) = uapi::fcntl_get_seals(fd.raw()) - && seals & c::F_SEAL_SHRINK != 0 - && let Ok(stat) = uapi::fstat(fd.raw()) - { - real_size = Some(stat.st_size as usize); - sigbus_impossible = stat.st_size as u64 >= len as u64; - } - if !sigbus_impossible && let Some(client) = client { - log::debug!( - "Client {} ({}) has created a shm buffer that might cause SIGBUS", - client.pid_info.comm, - client.id, - ); - } - let len = len.next_multiple_of(page_size()); - if let Some(real_size) = real_size - && real_size < len - { - let _ = ftruncate(fd.raw(), len as _); - } - let data = if len == 0 { - &mut [][..] - } else { - let prot = match read_only { - true => c::PROT_READ, - false => c::PROT_READ | c::PROT_WRITE, - }; - unsafe { - let data = c::mmap64(ptr::null_mut(), len, prot, flags, fd.raw(), 0); - if data == c::MAP_FAILED { - return Err(ClientMemError::MmapFailed(OsError::default())); - } - std::slice::from_raw_parts_mut(data as *mut Cell, len) - } - }; - Ok(Self { - fd: ManuallyDrop::new(fd.clone()), - failed: Cell::new(false), - sigbus_impossible, - data, - cpu: cpu.cloned(), - }) - } - - pub fn len(&self) -> usize { - self.data.len() - } - - pub fn offset(self: &Rc, offset: usize, len: usize) -> ClientMemOffset { - let mem = unsafe { &*self.data }; - ClientMemOffset { - mem: self.clone(), - offset, - data: &mem[offset..][..len], - } - } - - pub fn fd(&self) -> &Rc { - &self.fd - } - - pub fn is_sealed_memfd(&self) -> bool { - self.sigbus_impossible - } -} - -impl ClientMemOffset { - pub fn pool(&self) -> &ClientMem { - &self.mem - } - - pub fn offset(&self) -> usize { - self.offset - } - - #[expect(dead_code)] - pub fn ptr(&self) -> *const [Cell] { - self.data - } - - pub fn access]) -> T>(&self, f: F) -> Result { - unsafe { - if self.mem.sigbus_impossible { - return Ok(f(&*self.data)); - } - let mref = MemRef { - mem: &*self.mem, - outer: MEM.get(), - }; - MEM.set(&mref); - compiler_fence(Ordering::SeqCst); - let res = f(&*self.data); - MEM.set(mref.outer); - compiler_fence(Ordering::SeqCst); - match self.mem.failed.get() { - true => Err(ClientMemError::Sigbus), - _ => Ok(res), - } - } - } - - pub fn read(&self, dst: &mut Vec) -> Result<(), ClientMemError> { - if self.data.len().checked_rem(std::mem::size_of::()) != Some(0) { - return Err(ClientMemError::InvalidLength); - } - self.access(|v| { - let len_elements = v.len() / std::mem::size_of::(); - dst.reserve(len_elements); - let (_, unused) = dst.split_at_spare_mut_bytes_ext(); - unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v)); - unsafe { - dst.set_len(dst.len() + len_elements); - } - }) - } -} - -impl Drop for ClientMem { - fn drop(&mut self) { - let fd = unsafe { ManuallyDrop::take(&mut self.fd) }; - if let Some(cpu) = &self.cpu { - let pending = cpu.submit(Box::new(CloseMemWork { - fd: Rc::try_unwrap(fd).ok(), - data: self.data, - })); - pending.detach(); - } else { - unsafe { - c::munmap(self.data as _, self.len()); - } - } - } -} - -struct MemRef { - mem: *const ClientMem, - outer: *const MemRef, -} - -thread_local! { - static MEM: Cell<*const MemRef> = const { Cell::new(ptr::null()) }; -} - -unsafe fn kill() -> ! { - unsafe { - c::signal(c::SIGBUS, c::SIG_DFL); - raise(c::SIGBUS); - } - unreachable!(); -} - -unsafe extern "C" fn sigbus(sig: i32, info: &c::siginfo_t, _ucontext: *mut c::c_void) { - unsafe { - assert_eq!(sig, c::SIGBUS); - let mut memr_ptr = MEM.get(); - while !memr_ptr.is_null() { - let memr = &*memr_ptr; - let mem = &*memr.mem; - let lo = mem.data as *mut u8 as usize; - let hi = lo + mem.len(); - let fault_addr = info.si_addr() as usize; - if fault_addr < lo || fault_addr >= hi { - memr_ptr = memr.outer; - continue; - } - let res = c::mmap64( - lo as _, - hi - lo, - c::PROT_WRITE | c::PROT_READ, - c::MAP_ANONYMOUS | c::MAP_PRIVATE | c::MAP_FIXED, - -1, - 0, - ); - if res == c::MAP_FAILED { - kill(); - } - mem.failed.set(true); - return; - } - kill(); - } -} - -pub fn init() -> Result<(), ClientMemError> { - unsafe { - let mut action: c::sigaction = MaybeUninit::zeroed().assume_init(); - action.sa_sigaction = - sigbus as unsafe extern "C" fn(i32, &c::siginfo_t, *mut c::c_void) as _; - action.sa_flags = c::SA_NODEFER | c::SA_SIGINFO; - let res = c::sigaction(c::SIGBUS, &action, ptr::null_mut()); - uapi::map_err!(res) - .map(drop) - .map_os_err(ClientMemError::SigactionFailed) - } -} - -struct CloseMemWork { - fd: Option, - data: *const [Cell], -} - -unsafe impl Send for CloseMemWork {} - -impl CpuJob for CloseMemWork { - fn work(&mut self) -> &mut dyn CpuWork { - self - } - - fn completed(self: Box) { - // nothing - } -} - -impl CpuWork for CloseMemWork { - fn run(&mut self) -> Option> { - zone!("CloseMemWork"); - self.fd.take(); - unsafe { - c::munmap(self.data as _, self.data.len()); - } - None - } -} - -impl ShmMemory for ClientMemOffset { - fn len(&self) -> usize { - self.data.len() - } - - fn safe_access(&self) -> ShmMemoryBacking { - match self.mem.is_sealed_memfd() { - true => ShmMemoryBacking::Ptr(self.data), - false => ShmMemoryBacking::Fd(self.mem.fd.deref().clone(), self.offset), - } - } - - fn access(&self, f: &mut dyn FnMut(&[Cell])) -> Result<(), Box> { - self.access(f).map_err(|e| e.into()) +pub fn client_mem_client(client: &crate::client::Client) -> ClientMemClient<'_> { + ClientMemClient { + comm: &client.pid_info.comm, + id: client.id.raw(), } } diff --git a/src/ifs/jay_input.rs b/src/ifs/jay_input.rs index 26a3bcec..d7bc2f9b 100644 --- a/src/ifs/jay_input.rs +++ b/src/ifs/jay_input.rs @@ -4,7 +4,7 @@ use { InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod, InputDeviceId, }, client::{Client, ClientError}, - clientmem::{ClientMem, ClientMemError}, + clientmem::{ClientMem, ClientMemError, client_mem_client}, ifs::wl_seat::WlSeatGlobal, kbvm::{KbvmError, KbvmMap}, leaks::Tracker, @@ -204,7 +204,7 @@ impl JayInput { keymap, len, true, - Some(&self.client), + Some(client_mem_client(&self.client)), None, )?) .offset(0, len); diff --git a/src/ifs/wl_seat/zwp_virtual_keyboard_v1.rs b/src/ifs/wl_seat/zwp_virtual_keyboard_v1.rs index ceaec57b..af194118 100644 --- a/src/ifs/wl_seat/zwp_virtual_keyboard_v1.rs +++ b/src/ifs/wl_seat/zwp_virtual_keyboard_v1.rs @@ -2,7 +2,7 @@ use { crate::{ backend::KeyState, client::{Client, ClientError}, - clientmem::{ClientMem, ClientMemError}, + clientmem::{ClientMem, ClientMemError, client_mem_client}, ifs::{ wl_seat::{ WlSeatGlobal, @@ -59,7 +59,8 @@ impl ZwpVirtualKeyboardV1RequestHandler for ZwpVirtualKeyboardV1 { return Err(ZwpVirtualKeyboardV1Error::OversizedKeymap); } let size = req.size as usize - 1; - let client_mem = ClientMem::new_private(&req.fd, size, true, Some(&self.client), None) + let client_mem = + ClientMem::new_private(&req.fd, size, true, Some(client_mem_client(&self.client)), None) .map(Rc::new) .map_err(ZwpVirtualKeyboardV1Error::MapKeymap)?; let mut map = vec![]; diff --git a/src/ifs/wl_shm_pool.rs b/src/ifs/wl_shm_pool.rs index 6f037dbe..3cb22823 100644 --- a/src/ifs/wl_shm_pool.rs +++ b/src/ifs/wl_shm_pool.rs @@ -1,7 +1,7 @@ use { crate::{ client::{Client, ClientError}, - clientmem::{ClientMem, ClientMemError}, + clientmem::{ClientMem, ClientMemError, client_mem_client}, format::{formats, map_wayland_format_id}, ifs::wl_buffer::{WlBuffer, WlBufferError}, leaks::Tracker, @@ -40,7 +40,7 @@ impl WlShmPool { &fd, len, false, - Some(client), + Some(client_mem_client(client)), Some(&client.state.cpu_worker), false, )?)), @@ -97,7 +97,7 @@ impl WlShmPoolRequestHandler for WlShmPool { &self.fd, len, false, - Some(&self.client), + Some(client_mem_client(&self.client)), Some(&self.client.state.cpu_worker), false, )?)); diff --git a/src/ifs/zwlr_gamma_control_v1.rs b/src/ifs/zwlr_gamma_control_v1.rs index 8467c6d1..1da5457f 100644 --- a/src/ifs/zwlr_gamma_control_v1.rs +++ b/src/ifs/zwlr_gamma_control_v1.rs @@ -2,7 +2,7 @@ use { crate::{ backend::{BackendGammaLut, BackendGammaLutElement}, client::{Client, ClientError, ClientId}, - clientmem::{ClientMem, ClientMemError}, + clientmem::{ClientMem, ClientMemError, client_mem_client}, ifs::{ wl_output::OutputGlobalOpt, zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1, }, @@ -119,7 +119,7 @@ impl ZwlrGammaControlV1RequestHandler for ZwlrGammaControlV1 { &req.fd, data_size, true, - Some(&self.client), + Some(client_mem_client(&self.client)), None, )?) .offset(0, data_size) diff --git a/src/ifs/zwp_linux_buffer_params_v1.rs b/src/ifs/zwp_linux_buffer_params_v1.rs index eac91ff6..9f017aa0 100644 --- a/src/ifs/zwp_linux_buffer_params_v1.rs +++ b/src/ifs/zwp_linux_buffer_params_v1.rs @@ -1,7 +1,7 @@ use { crate::{ client::ClientError, - clientmem::{ClientMem, ClientMemError}, + clientmem::{ClientMem, ClientMemError, client_mem_client}, gfx_api::GfxError, ifs::{ wl_buffer::{WlBuffer, WlBufferError}, @@ -119,7 +119,7 @@ impl ZwpLinuxBufferParamsV1 { &p.fd, size, true, - Some(&self.parent.client), + Some(client_mem_client(&self.parent.client)), Some(&self.parent.client.state.cpu_worker), true, )