Desktop: Isolate CEF-rendered UI into separate crate and process (#4321)

* Extract CEF rendered UI into a separate process and crate

* Review

* Review

* Review

* Review

* Remove necessary workarounds

* Block on frame copy ack

* Crop and resample frames correctly

* Skip blank frames

* Fix deps

* Fix fmt

* Fix clippy warning

* Review

* Fix todo
This commit is contained in:
Timon
2026-07-14 14:51:20 -07:00
committed by Keavon Chambers
parent 97a43e66fb
commit ba0a97aefe
82 changed files with 4768 additions and 1291 deletions
+265
View File
@@ -0,0 +1,265 @@
use ipc_channel::ipc::IpcSender;
use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
use std::sync::{Arc, Mutex};
use super::RecvResult;
use crate::frames::surface::FrameSurface;
use crate::remote::HostConfig;
use crate::remote::messages::EventMessage;
pub(crate) const FRAME_SOCKET_CHILD_FD: RawFd = 3;
#[repr(C)]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
struct FrameDescriptor {
seq: u64,
modifier: u64,
width: u32,
height: u32,
format: u32,
plane_count: u32,
strides: [u32; 4],
offsets: [u32; 4],
content_x: u32,
content_y: u32,
content_width: u32,
content_height: u32,
source_width: u32,
source_height: u32,
}
const DESCRIPTOR_BYTES: usize = std::mem::size_of::<FrameDescriptor>();
const MAX_PLANES: usize = 4;
pub(crate) fn socketpair() -> std::io::Result<(OwnedFd, OwnedFd)> {
let mut fds = [0 as RawFd; 2];
// SAFETY: socketpair call; on success fds are owned by us.
let result = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_SEQPACKET | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()) };
if result != 0 {
return Err(std::io::Error::last_os_error());
}
// SAFETY: socketpair succeeded, so both fds are valid and not owned elsewhere.
Ok(unsafe { (OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])) })
}
pub(crate) struct PlaneSender {
socket: OwnedFd,
}
impl PlaneSender {
pub(crate) fn from_config(config: &HostConfig, _events: Arc<Mutex<IpcSender<EventMessage>>>) -> Option<Self> {
let fd = config.frame_socket_fd?;
// SAFETY: the spawner dup2'd this fd for us; nothing else owns it.
let socket = unsafe { OwnedFd::from_raw_fd(fd) };
// Restore CLOEXEC so subprocesses don't inherit the socket.
// SAFETY: plain fcntl on an fd we own.
if unsafe { libc::fcntl(socket.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC) } != 0 {
tracing::warn!("Failed to set CLOEXEC on the frame socket: {}", std::io::Error::last_os_error());
}
Some(Self { socket })
}
pub(crate) fn stage(&self, info: &cef::AcceleratedPaintInfo) -> Option<StagedFrame> {
let coded_size = &info.extra.coded_size;
if coded_size.width <= 0 || coded_size.height <= 0 {
tracing::error!("Accelerated paint delivered an invalid coded size: {}x{}", coded_size.width, coded_size.height);
return None;
}
let plane_count = (info.plane_count.max(0) as usize).min(info.planes.len());
let mut fds = Vec::with_capacity(plane_count);
let mut strides = [0u32; 4];
let mut offsets = [0u32; 4];
for (i, plane) in info.planes[..plane_count].iter().enumerate() {
// SAFETY: CEF keeps the plane fds valid for the `on_accelerated_paint` callback.
let fd = unsafe { BorrowedFd::borrow_raw(plane.fd) };
match fd.try_clone_to_owned() {
Ok(owned) => fds.push(owned),
Err(e) => {
tracing::error!("Failed to duplicate DMA-BUF plane fd: {e}");
return None;
}
}
strides[i] = plane.stride;
offsets[i] = plane.offset as u32;
}
let content = crate::frames::import::ContentRect::try_from(info).unwrap_or_default();
Some(StagedFrame {
descriptor: FrameDescriptor {
seq: 0,
modifier: info.modifier,
width: coded_size.width as u32,
height: coded_size.height as u32,
format: *info.format.as_ref() as u32,
plane_count: plane_count as u32,
strides,
offsets,
content_x: content.x,
content_y: content.y,
content_width: content.width,
content_height: content.height,
source_width: content.source_width,
source_height: content.source_height,
},
fds,
})
}
pub(crate) fn send(&self, seq: u64, frame: StagedFrame) -> std::io::Result<()> {
let mut descriptor = frame.descriptor;
descriptor.seq = seq;
debug_assert!(frame.fds.len() <= MAX_PLANES);
let fd_bytes = frame.fds.len() * std::mem::size_of::<RawFd>();
let mut iov = libc::iovec {
iov_base: &descriptor as *const FrameDescriptor as *mut libc::c_void,
iov_len: DESCRIPTOR_BYTES,
};
let mut cmsg_buffer = [0u8; unsafe { libc::CMSG_SPACE((MAX_PLANES * std::mem::size_of::<RawFd>()) as u32) } as usize];
let mut msg: libc::msghdr = unsafe { std::mem::zeroed() };
msg.msg_iov = &mut iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_buffer.as_mut_ptr().cast();
msg.msg_controllen = unsafe { libc::CMSG_SPACE(fd_bytes as u32) } as _;
unsafe {
let cmsg = libc::CMSG_FIRSTHDR(&msg);
(*cmsg).cmsg_level = libc::SOL_SOCKET;
(*cmsg).cmsg_type = libc::SCM_RIGHTS;
(*cmsg).cmsg_len = libc::CMSG_LEN(fd_bytes as u32) as _;
let data = libc::CMSG_DATA(cmsg) as *mut RawFd;
for (i, fd) in frame.fds.iter().enumerate() {
data.add(i).write_unaligned(fd.as_raw_fd());
}
}
loop {
// SAFETY: msg and everything it points to are valid for the duration of the call.
let sent = unsafe { libc::sendmsg(self.socket.as_raw_fd(), &msg, libc::MSG_NOSIGNAL) };
if sent >= 0 {
return Ok(());
}
let error = std::io::Error::last_os_error();
if error.kind() != std::io::ErrorKind::Interrupted {
return Err(error);
}
}
}
}
pub(crate) struct StagedFrame {
descriptor: FrameDescriptor,
fds: Vec<OwnedFd>,
}
pub(crate) struct PlaneReceiver {
socket: OwnedFd,
}
impl PlaneReceiver {
pub(crate) fn new(socket: OwnedFd) -> Self {
Self { socket }
}
pub(crate) fn recv_blocking(&self) -> std::io::Result<RecvResult> {
self.recv(false)
}
pub(crate) fn try_recv(&self) -> std::io::Result<RecvResult> {
self.recv(true)
}
fn recv(&self, nonblocking: bool) -> std::io::Result<RecvResult> {
let mut descriptor: FrameDescriptor = bytemuck::Zeroable::zeroed();
let mut iov = libc::iovec {
iov_base: (&mut descriptor as *mut FrameDescriptor).cast(),
iov_len: DESCRIPTOR_BYTES,
};
// SAFETY: pure size computation.
let mut cmsg_buffer = [0u8; unsafe { libc::CMSG_SPACE((MAX_PLANES * std::mem::size_of::<RawFd>()) as u32) } as usize];
let mut msg: libc::msghdr = unsafe { std::mem::zeroed() };
msg.msg_iov = &mut iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_buffer.as_mut_ptr().cast();
msg.msg_controllen = cmsg_buffer.len() as _;
let flags = libc::MSG_CMSG_CLOEXEC | if nonblocking { libc::MSG_DONTWAIT } else { 0 };
let received = loop {
// SAFETY: msg and everything it points to are valid for the duration of the call.
let received = unsafe { libc::recvmsg(self.socket.as_raw_fd(), &mut msg, flags) };
if received >= 0 {
break received;
}
let error = std::io::Error::last_os_error();
match error.kind() {
std::io::ErrorKind::Interrupted => continue,
std::io::ErrorKind::WouldBlock => return Ok(RecvResult::WouldBlock),
_ => return Err(error),
}
};
let mut fds = Vec::new();
// SAFETY: traversing the cmsgs recvmsg just filled; SCM_RIGHTS payload is fds now owned by us.
unsafe {
let mut cmsg = libc::CMSG_FIRSTHDR(&msg);
while !cmsg.is_null() {
if (*cmsg).cmsg_level == libc::SOL_SOCKET && (*cmsg).cmsg_type == libc::SCM_RIGHTS {
let data = libc::CMSG_DATA(cmsg) as *const RawFd;
let count = ((*cmsg).cmsg_len as usize - libc::CMSG_LEN(0) as usize) / std::mem::size_of::<RawFd>();
for i in 0..count {
fds.push(OwnedFd::from_raw_fd(data.add(i).read_unaligned()));
}
}
cmsg = libc::CMSG_NXTHDR(&msg, cmsg);
}
}
if received == 0 {
return Ok(RecvResult::Closed);
}
if received as usize != DESCRIPTOR_BYTES || (msg.msg_flags & libc::MSG_CTRUNC) != 0 {
return Err(std::io::Error::other(format!(
"malformed frame message: {received} bytes, flags {:#x} ({} fds)",
msg.msg_flags,
fds.len()
)));
}
Ok(RecvResult::Frame(WireFrame { descriptor, fds }))
}
}
pub(crate) struct WireFrame {
descriptor: FrameDescriptor,
fds: Vec<OwnedFd>,
}
impl WireFrame {
pub(crate) fn seq(&self) -> u64 {
self.descriptor.seq
}
pub(crate) fn import(self, surface: &FrameSurface) -> Option<wgpu::Texture> {
let descriptor = self.descriptor;
let format = super::wire_color_type(descriptor.format)?;
let plane_count = (descriptor.plane_count as usize).min(self.fds.len());
let content = crate::frames::import::ContentRect {
x: descriptor.content_x,
y: descriptor.content_y,
width: descriptor.content_width,
height: descriptor.content_height,
source_width: descriptor.source_width,
source_height: descriptor.source_height,
};
let importer = crate::frames::import::dmabuf::DmaBufImporter::from_parts(
self.fds,
descriptor.strides[..plane_count].to_vec(),
descriptor.offsets[..plane_count].to_vec(),
descriptor.modifier,
descriptor.width,
descriptor.height,
format,
);
surface.import_texture(importer, content)
}
}
+305
View File
@@ -0,0 +1,305 @@
use ipc_channel::ipc::IpcSender;
use std::ffi::CString;
use std::sync::{Arc, Mutex};
use mach2::kern_return::KERN_SUCCESS;
use mach2::message::{
MACH_MSG_PORT_DESCRIPTOR, MACH_MSG_SUCCESS, MACH_MSG_TIMEOUT_NONE, MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND, MACH_MSGH_BITS_COMPLEX, MACH_RCV_MSG, MACH_RCV_TIMED_OUT, MACH_RCV_TIMEOUT,
MACH_SEND_MSG, mach_msg, mach_msg_body_t, mach_msg_header_t,
};
use mach2::port::{MACH_PORT_NULL, mach_port_t};
use mach2::traps::mach_task_self;
use objc2_io_surface::IOSurfaceRef;
use super::RecvResult;
use crate::frames::surface::FrameSurface;
use crate::remote::HostConfig;
use crate::remote::messages::EventMessage;
// From libSystem, stable since 10.0, not coverd by mach2
unsafe extern "C" {
static bootstrap_port: mach_port_t;
fn bootstrap_check_in(bp: mach_port_t, service_name: *const std::ffi::c_char, sp: *mut mach_port_t) -> mach2::kern_return::kern_return_t;
fn bootstrap_look_up(bp: mach_port_t, service_name: *const std::ffi::c_char, sp: *mut mach_port_t) -> mach2::kern_return::kern_return_t;
}
// `mach_msg_port_descriptor_t` kernel ABI
#[repr(C)]
#[derive(Clone, Copy)]
struct PortDescriptor {
name: mach_port_t,
pad1: u32,
pad2: u16,
disposition: u8,
descriptor_type: u8,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct FrameDescriptor {
seq: u64,
width: u32,
height: u32,
format: u32,
content_x: u32,
content_y: u32,
content_width: u32,
content_height: u32,
source_width: u32,
source_height: u32,
_pad: u32,
}
#[repr(C)]
struct FrameMessage {
header: mach_msg_header_t,
body: mach_msg_body_t,
surface: PortDescriptor,
descriptor: FrameDescriptor,
}
#[repr(C)]
struct FrameMessageBuffer {
message: FrameMessage,
trailer: [u8; 64],
}
struct SendRight(mach_port_t);
// SAFETY: mach port names are task-wide; rights may be used from any thread.
unsafe impl Send for SendRight {}
unsafe impl Sync for SendRight {}
impl Drop for SendRight {
fn drop(&mut self) {
// SAFETY: we own one reference on this send right.
unsafe { mach2::mach_port::mach_port_deallocate(mach_task_self(), self.0) };
}
}
pub(crate) fn create_service(name: &str) -> std::io::Result<mach_port_t> {
let c_name = CString::new(name).map_err(std::io::Error::other)?;
let mut port: mach_port_t = MACH_PORT_NULL;
// SAFETY: plain bootstrap call; on success we own the service's receive right.
let result = unsafe { bootstrap_check_in(bootstrap_port, c_name.as_ptr(), &mut port) };
if result != KERN_SUCCESS {
return Err(std::io::Error::other(format!("bootstrap_check_in failed: {result:#x}")));
}
Ok(port)
}
fn look_up_service(name: &str) -> std::io::Result<SendRight> {
let c_name = CString::new(name).map_err(std::io::Error::other)?;
let mut port: mach_port_t = MACH_PORT_NULL;
// SAFETY: plain bootstrap call; on success we own a send right.
let result = unsafe { bootstrap_look_up(bootstrap_port, c_name.as_ptr(), &mut port) };
if result != KERN_SUCCESS {
return Err(std::io::Error::other(format!("bootstrap_look_up failed: {result:#x}")));
}
Ok(SendRight(port))
}
pub(crate) struct PlaneSender {
service: SendRight,
}
impl PlaneSender {
pub(crate) fn from_config(config: &HostConfig, _events: Arc<Mutex<IpcSender<EventMessage>>>) -> Option<Self> {
let name = config.frame_service.as_deref()?;
match look_up_service(name) {
Ok(service) => Some(Self { service }),
Err(e) => {
tracing::error!("Failed to look up the accelerated frame service, falling back to software frames: {e}");
None
}
}
}
pub(crate) fn stage(&self, info: &cef::AcceleratedPaintInfo) -> Option<StagedFrame> {
let coded_size = &info.extra.coded_size;
if coded_size.width <= 0 || coded_size.height <= 0 {
tracing::error!("Accelerated paint delivered an invalid coded size: {}x{}", coded_size.width, coded_size.height);
return None;
}
let Some(surface) = std::ptr::NonNull::new(info.shared_texture_io_surface.cast::<IOSurfaceRef>()) else {
tracing::error!("Accelerated paint delivered a null IOSurface");
return None;
};
// SAFETY: CEF keeps the surface valid for the `on_accelerated_paint` callback.
let port = unsafe { surface.as_ref() }.create_mach_port();
if port == MACH_PORT_NULL {
tracing::error!("Failed to wrap the IOSurface in a mach port");
return None;
}
let content = crate::frames::import::ContentRect::try_from(info).unwrap_or_default();
Some(StagedFrame {
descriptor: FrameDescriptor {
seq: 0,
width: coded_size.width as u32,
height: coded_size.height as u32,
format: *info.format.as_ref() as u32,
content_x: content.x,
content_y: content.y,
content_width: content.width,
content_height: content.height,
source_width: content.source_width,
source_height: content.source_height,
_pad: 0,
},
surface: SendRight(port),
})
}
pub(crate) fn send(&self, seq: u64, frame: StagedFrame) -> std::io::Result<()> {
let mut descriptor = frame.descriptor;
descriptor.seq = seq;
let mut message = FrameMessage {
header: mach_msg_header_t {
msgh_bits: MACH_MSG_TYPE_COPY_SEND | MACH_MSGH_BITS_COMPLEX,
msgh_size: std::mem::size_of::<FrameMessage>() as u32,
msgh_remote_port: self.service.0,
msgh_local_port: MACH_PORT_NULL,
msgh_voucher_port: MACH_PORT_NULL,
msgh_id: 0,
},
body: mach_msg_body_t { msgh_descriptor_count: 1 },
surface: PortDescriptor {
name: frame.surface.0,
pad1: 0,
pad2: 0,
disposition: MACH_MSG_TYPE_MOVE_SEND as u8,
descriptor_type: MACH_MSG_PORT_DESCRIPTOR as u8,
},
descriptor,
};
// SAFETY: message is a well-formed complex message of the declared size.
let result = unsafe {
mach_msg(
&mut message.header,
MACH_SEND_MSG,
std::mem::size_of::<FrameMessage>() as u32,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL,
)
};
if result != MACH_MSG_SUCCESS {
return Err(std::io::Error::other(format!("mach_msg send failed: {result:#x}")));
}
// Kernel took ownership of the surface and moves it to the receiver. We must not drop.
std::mem::forget(frame.surface);
Ok(())
}
}
pub(crate) struct StagedFrame {
descriptor: FrameDescriptor,
surface: SendRight,
}
pub(crate) struct PlaneReceiver {
port: mach_port_t,
}
impl PlaneReceiver {
pub(crate) fn new(port: mach_port_t) -> Self {
Self { port }
}
pub(crate) fn recv_blocking(&self) -> std::io::Result<RecvResult> {
self.recv(false)
}
pub(crate) fn try_recv(&self) -> std::io::Result<RecvResult> {
self.recv(true)
}
fn recv(&self, nonblocking: bool) -> std::io::Result<RecvResult> {
// SAFETY: zeroed is a valid representation for these plain-data structs.
let mut buffer: FrameMessageBuffer = unsafe { std::mem::zeroed() };
let (options, timeout) = if nonblocking {
(MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0)
} else {
(MACH_RCV_MSG, MACH_MSG_TIMEOUT_NONE)
};
// SAFETY: the buffer is large enough for the message plus the basic trailer.
let result = unsafe {
mach_msg(
&mut buffer.message.header,
options,
0,
std::mem::size_of::<FrameMessageBuffer>() as u32,
self.port,
timeout,
MACH_PORT_NULL,
)
};
if result == MACH_RCV_TIMED_OUT {
return Ok(RecvResult::WouldBlock);
}
if result != MACH_MSG_SUCCESS {
return Err(std::io::Error::other(format!("mach_msg receive failed: {result:#x}")));
}
let received_complex = buffer.message.header.msgh_bits & MACH_MSGH_BITS_COMPLEX != 0;
let descriptor_count = if received_complex { buffer.message.body.msgh_descriptor_count } else { 0 };
let surface = (descriptor_count == 1 && buffer.message.surface.descriptor_type == MACH_MSG_PORT_DESCRIPTOR as u8).then(|| SendRight(buffer.message.surface.name));
if buffer.message.header.msgh_size as usize != std::mem::size_of::<FrameMessage>() {
return Err(std::io::Error::other(format!("malformed frame message: {} bytes", buffer.message.header.msgh_size)));
}
let Some(surface) = surface else {
return Err(std::io::Error::other("frame message carried no surface port"));
};
Ok(RecvResult::Frame(WireFrame {
descriptor: buffer.message.descriptor,
surface,
}))
}
}
pub(crate) struct WireFrame {
descriptor: FrameDescriptor,
surface: SendRight,
}
impl WireFrame {
pub(crate) fn seq(&self) -> u64 {
self.descriptor.seq
}
pub(crate) fn import(self, surface: &FrameSurface) -> Option<wgpu::Texture> {
let WireFrame { descriptor, surface: port } = self;
let format = super::wire_color_type(descriptor.format)?;
// Lookup takes its own reference on the surface, port can be dropped.
let io_surface = IOSurfaceRef::lookup_from_mach_port(port.0);
drop(port);
let Some(io_surface) = io_surface else {
tracing::error!("Failed to look up the IOSurface for frame {}", descriptor.seq);
return None;
};
let io_surface_ref: &IOSurfaceRef = &io_surface;
let content = crate::frames::import::ContentRect {
x: descriptor.content_x,
y: descriptor.content_y,
width: descriptor.content_width,
height: descriptor.content_height,
source_width: descriptor.source_width,
source_height: descriptor.source_height,
};
let importer = crate::frames::import::iosurface::IOSurfaceImporter::from_parts(io_surface_ref as *const _ as *mut std::os::raw::c_void, descriptor.width, descriptor.height, format);
surface.import_texture(importer, content)
}
}
+175
View File
@@ -0,0 +1,175 @@
use ipc_channel::ipc::IpcSender;
use std::sync::{Arc, Mutex};
use windows::Win32::Foundation::{CloseHandle, DUPLICATE_CLOSE_SOURCE, DUPLICATE_SAME_ACCESS, DuplicateHandle, HANDLE};
use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcess, PROCESS_DUP_HANDLE};
use crate::frames::import::ContentRect;
use crate::frames::surface::FrameSurface;
use crate::remote::HostConfig;
use crate::remote::messages::EventMessage;
struct MainProcess(HANDLE);
// SAFETY: process handles may be used and closed from any thread.
unsafe impl Send for MainProcess {}
unsafe impl Sync for MainProcess {}
impl MainProcess {
fn open(pid: u32) -> windows::core::Result<Self> {
// SAFETY: plain OpenProcess call; on success the handle is ours to close.
unsafe { OpenProcess(PROCESS_DUP_HANDLE, false, pid).map(Self) }
}
fn duplicate_into(&self, handle: HANDLE) -> windows::core::Result<u64> {
let mut target = HANDLE::default();
// SAFETY: both process handles are valid; `target` receives the duplicate.
unsafe { DuplicateHandle(GetCurrentProcess(), handle, self.0, &mut target, 0, false, DUPLICATE_SAME_ACCESS)? };
Ok(target.0 as u64)
}
fn close_in_main(&self, handle: u64) {
let mut reclaimed = HANDLE::default();
// SAFETY: `handle` came from `duplicate_into` and is valid.
unsafe {
if let Err(e) = DuplicateHandle(self.0, HANDLE(handle as _), GetCurrentProcess(), &mut reclaimed, 0, false, DUPLICATE_CLOSE_SOURCE) {
tracing::warn!("Failed to reclaim a frame handle from the main process: {e}");
}
if !reclaimed.is_invalid() {
let _ = CloseHandle(reclaimed);
}
}
}
}
impl Drop for MainProcess {
fn drop(&mut self) {
// SAFETY: we own the process handle.
unsafe {
let _ = CloseHandle(self.0);
}
}
}
pub(crate) struct PlaneSender {
main: Arc<MainProcess>,
events: Arc<Mutex<IpcSender<EventMessage>>>,
}
impl PlaneSender {
pub(crate) fn from_config(config: &HostConfig, events: Arc<Mutex<IpcSender<EventMessage>>>) -> Option<Self> {
match MainProcess::open(config.main_pid) {
Ok(main) => Some(Self { main: Arc::new(main), events }),
Err(e) => {
tracing::error!("Failed to open the main process for handle duplication, falling back to software frames: {e}");
None
}
}
}
pub(crate) fn stage(&self, info: &cef::AcceleratedPaintInfo) -> Option<StagedFrame> {
let coded_size = &info.extra.coded_size;
if coded_size.width <= 0 || coded_size.height <= 0 {
tracing::error!("Accelerated paint delivered an invalid coded size: {}x{}", coded_size.width, coded_size.height);
return None;
}
let handle = match self.main.duplicate_into(HANDLE(info.shared_texture_handle)) {
Ok(handle) => handle,
Err(e) => {
tracing::error!("Failed to duplicate the shared texture handle into the main process: {e}");
return None;
}
};
Some(StagedFrame {
handle: HandleInMain { handle, main: self.main.clone() },
width: coded_size.width as u32,
height: coded_size.height as u32,
format: *info.format.as_ref() as u32,
content: ContentRect::try_from(info).ok(),
})
}
pub(crate) fn send(&self, seq: u64, frame: StagedFrame) -> std::io::Result<()> {
let message = EventMessage::AcceleratedFrame {
seq,
handle: frame.handle.handle,
width: frame.width,
height: frame.height,
format: frame.format,
content: frame.content,
};
let sender = self.events.lock().map_err(|_| std::io::Error::other("the host message sender lock is poisoned"))?;
match sender.send(message) {
Ok(()) => {
// Dropping the handle would reclaim it. We must not drop.
std::mem::forget(frame.handle);
Ok(())
}
Err(e) => Err(std::io::Error::other(e.to_string())),
}
}
}
pub(crate) struct StagedFrame {
handle: HandleInMain,
width: u32,
height: u32,
format: u32,
content: Option<ContentRect>,
}
struct HandleInMain {
handle: u64,
main: Arc<MainProcess>,
}
impl Drop for HandleInMain {
fn drop(&mut self) {
self.main.close_in_main(self.handle);
}
}
pub(crate) struct WireFrame {
seq: u64,
handle: ReceivedHandle,
width: u32,
height: u32,
format: u32,
content: Option<ContentRect>,
}
impl WireFrame {
pub(crate) fn new(seq: u64, handle: u64, width: u32, height: u32, format: u32, content: Option<ContentRect>) -> Self {
Self {
seq,
handle: ReceivedHandle(handle),
width,
height,
format,
content,
}
}
pub(crate) fn seq(&self) -> u64 {
self.seq
}
pub(crate) fn import(self, surface: &FrameSurface) -> Option<wgpu::Texture> {
let format = super::wire_color_type(self.format)?;
let content = self.content.unwrap_or_default();
surface.import_texture(crate::frames::import::d3d11::D3D11Importer::from_parts(self.handle.0, self.width, self.height, format), content)
}
}
struct ReceivedHandle(u64);
impl Drop for ReceivedHandle {
fn drop(&mut self) {
// SAFETY: the host duplicated this handle into our process for us to own.
if let Err(e) = unsafe { CloseHandle(HANDLE(self.0 as _)) } {
tracing::warn!("Failed to close a remote frame handle: {e}");
}
}
}