rustdesk/libs/scrap/src/x11/capturer.rs
fufesou b733ad9379 refact register_breakdown_handler
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-02-19 10:19:28 +08:00

131 lines
3.3 KiB
Rust

use std::{io, ptr, slice};
use hbb_common::libc;
use super::ffi::*;
use super::Display;
pub struct Capturer {
display: Display,
shmid: i32,
xcbid: u32,
buffer: *const u8,
size: usize,
use_yuv: bool,
yuv: Vec<u8>,
saved_raw_data: Vec<u8>, // for faster compare and copy
}
impl Capturer {
pub fn new(display: Display, use_yuv: bool) -> io::Result<Capturer> {
// Calculate dimensions.
let pixel_width = 4;
let rect = display.rect();
let size = (rect.w as usize) * (rect.h as usize) * pixel_width;
// Create a shared memory segment.
let shmid = unsafe {
libc::shmget(
libc::IPC_PRIVATE,
size,
// Everyone can do anything.
libc::IPC_CREAT | 0o777,
)
};
if shmid == -1 {
return Err(io::Error::last_os_error());
}
// Attach the segment to a readable address.
let buffer = unsafe { libc::shmat(shmid, ptr::null(), libc::SHM_RDONLY) } as *mut u8;
if buffer as isize == -1 {
return Err(io::Error::last_os_error());
}
// Attach the segment to XCB.
let server = display.server().raw();
let xcbid = unsafe { xcb_generate_id(server) };
unsafe {
xcb_shm_attach(
server,
xcbid,
shmid as u32,
0, // False, i.e. not read-only.
);
}
let c = Capturer {
display,
shmid,
xcbid,
buffer,
size,
use_yuv,
yuv: Vec::new(),
saved_raw_data: Vec::new(),
};
Ok(c)
}
pub fn set_use_yuv(&mut self, use_yuv: bool) {
self.use_yuv = use_yuv;
}
pub fn display(&self) -> &Display {
&self.display
}
fn get_image(&self) {
let rect = self.display.rect();
unsafe {
let request = xcb_shm_get_image_unchecked(
self.display.server().raw(),
self.display.root(),
rect.x,
rect.y,
rect.w,
rect.h,
!0,
XCB_IMAGE_FORMAT_Z_PIXMAP,
self.xcbid,
0,
);
let response =
xcb_shm_get_image_reply(self.display.server().raw(), request, ptr::null_mut());
libc::free(response as *mut _);
}
}
pub fn frame<'b>(&'b mut self) -> std::io::Result<&'b [u8]> {
self.get_image();
let result = unsafe { slice::from_raw_parts(self.buffer, self.size) };
crate::would_block_if_equal(&mut self.saved_raw_data, result)?;
Ok(if self.use_yuv {
crate::common::bgra_to_i420(self.display.w(), self.display.h(), &result, &mut self.yuv);
&self.yuv[..]
} else {
result
})
}
}
impl Drop for Capturer {
fn drop(&mut self) {
unsafe {
// Detach segment from XCB.
xcb_shm_detach(self.display.server().raw(), self.xcbid);
// Detach segment from our space.
libc::shmdt(self.buffer as *mut _);
// Destroy the shared memory segment.
libc::shmctl(self.shmid, libc::IPC_RMID, ptr::null_mut());
}
}
}