mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
source code
This commit is contained in:
123
libs/scrap/src/x11/capturer.rs
Normal file
123
libs/scrap/src/x11/capturer.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
use std::{io, ptr, slice};
|
||||
|
||||
use 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>,
|
||||
}
|
||||
|
||||
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(),
|
||||
};
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
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) -> &'b [u8] {
|
||||
self.get_image();
|
||||
let result = unsafe { slice::from_raw_parts(self.buffer, self.size) };
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
55
libs/scrap/src/x11/display.rs
Normal file
55
libs/scrap/src/x11/display.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::ffi::*;
|
||||
use super::Server;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Display {
|
||||
server: Rc<Server>,
|
||||
default: bool,
|
||||
rect: Rect,
|
||||
root: xcb_window_t,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct Rect {
|
||||
pub x: i16,
|
||||
pub y: i16,
|
||||
pub w: u16,
|
||||
pub h: u16,
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub unsafe fn new(
|
||||
server: Rc<Server>,
|
||||
default: bool,
|
||||
rect: Rect,
|
||||
root: xcb_window_t,
|
||||
) -> Display {
|
||||
Display {
|
||||
server,
|
||||
default,
|
||||
rect,
|
||||
root,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server(&self) -> &Rc<Server> {
|
||||
&self.server
|
||||
}
|
||||
pub fn is_default(&self) -> bool {
|
||||
self.default
|
||||
}
|
||||
pub fn rect(&self) -> Rect {
|
||||
self.rect
|
||||
}
|
||||
pub fn w(&self) -> usize {
|
||||
self.rect.w as _
|
||||
}
|
||||
pub fn h(&self) -> usize {
|
||||
self.rect.h as _
|
||||
}
|
||||
pub fn root(&self) -> xcb_window_t {
|
||||
self.root
|
||||
}
|
||||
}
|
||||
205
libs/scrap/src/x11/ffi.rs
Normal file
205
libs/scrap/src/x11/ffi.rs
Normal file
@@ -0,0 +1,205 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use libc::c_void;
|
||||
|
||||
#[link(name = "xcb")]
|
||||
#[link(name = "xcb-shm")]
|
||||
#[link(name = "xcb-randr")]
|
||||
extern "C" {
|
||||
pub fn xcb_connect(displayname: *const i8, screenp: *mut i32) -> *mut xcb_connection_t;
|
||||
|
||||
pub fn xcb_disconnect(c: *mut xcb_connection_t);
|
||||
|
||||
pub fn xcb_connection_has_error(c: *mut xcb_connection_t) -> i32;
|
||||
|
||||
pub fn xcb_get_setup(c: *mut xcb_connection_t) -> *const xcb_setup_t;
|
||||
|
||||
pub fn xcb_setup_roots_iterator(r: *const xcb_setup_t) -> xcb_screen_iterator_t;
|
||||
|
||||
pub fn xcb_screen_next(i: *mut xcb_screen_iterator_t);
|
||||
|
||||
pub fn xcb_generate_id(c: *mut xcb_connection_t) -> u32;
|
||||
|
||||
pub fn xcb_shm_attach(
|
||||
c: *mut xcb_connection_t,
|
||||
shmseg: xcb_shm_seg_t,
|
||||
shmid: u32,
|
||||
read_only: u8,
|
||||
) -> xcb_void_cookie_t;
|
||||
|
||||
pub fn xcb_shm_detach(c: *mut xcb_connection_t, shmseg: xcb_shm_seg_t) -> xcb_void_cookie_t;
|
||||
|
||||
pub fn xcb_shm_get_image_unchecked(
|
||||
c: *mut xcb_connection_t,
|
||||
drawable: xcb_drawable_t,
|
||||
x: i16,
|
||||
y: i16,
|
||||
width: u16,
|
||||
height: u16,
|
||||
plane_mask: u32,
|
||||
format: u8,
|
||||
shmseg: xcb_shm_seg_t,
|
||||
offset: u32,
|
||||
) -> xcb_shm_get_image_cookie_t;
|
||||
|
||||
pub fn xcb_shm_get_image_reply(
|
||||
c: *mut xcb_connection_t,
|
||||
cookie: xcb_shm_get_image_cookie_t,
|
||||
e: *mut *mut xcb_generic_error_t,
|
||||
) -> *mut xcb_shm_get_image_reply_t;
|
||||
|
||||
pub fn xcb_randr_get_monitors_unchecked(
|
||||
c: *mut xcb_connection_t,
|
||||
window: xcb_window_t,
|
||||
get_active: u8,
|
||||
) -> xcb_randr_get_monitors_cookie_t;
|
||||
|
||||
pub fn xcb_randr_get_monitors_reply(
|
||||
c: *mut xcb_connection_t,
|
||||
cookie: xcb_randr_get_monitors_cookie_t,
|
||||
e: *mut *mut xcb_generic_error_t,
|
||||
) -> *mut xcb_randr_get_monitors_reply_t;
|
||||
|
||||
pub fn xcb_randr_get_monitors_monitors_iterator(
|
||||
r: *const xcb_randr_get_monitors_reply_t,
|
||||
) -> xcb_randr_monitor_info_iterator_t;
|
||||
|
||||
pub fn xcb_randr_monitor_info_next(i: *mut xcb_randr_monitor_info_iterator_t);
|
||||
}
|
||||
|
||||
pub const XCB_IMAGE_FORMAT_Z_PIXMAP: u8 = 2;
|
||||
|
||||
pub type xcb_atom_t = u32;
|
||||
pub type xcb_connection_t = c_void;
|
||||
pub type xcb_window_t = u32;
|
||||
pub type xcb_keycode_t = u8;
|
||||
pub type xcb_visualid_t = u32;
|
||||
pub type xcb_timestamp_t = u32;
|
||||
pub type xcb_colormap_t = u32;
|
||||
pub type xcb_shm_seg_t = u32;
|
||||
pub type xcb_drawable_t = u32;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct xcb_setup_t {
|
||||
pub status: u8,
|
||||
pub pad0: u8,
|
||||
pub protocol_major_version: u16,
|
||||
pub protocol_minor_version: u16,
|
||||
pub length: u16,
|
||||
pub release_number: u32,
|
||||
pub resource_id_base: u32,
|
||||
pub resource_id_mask: u32,
|
||||
pub motion_buffer_size: u32,
|
||||
pub vendor_len: u16,
|
||||
pub maximum_request_length: u16,
|
||||
pub roots_len: u8,
|
||||
pub pixmap_formats_len: u8,
|
||||
pub image_byte_order: u8,
|
||||
pub bitmap_format_bit_order: u8,
|
||||
pub bitmap_format_scanline_unit: u8,
|
||||
pub bitmap_format_scanline_pad: u8,
|
||||
pub min_keycode: xcb_keycode_t,
|
||||
pub max_keycode: xcb_keycode_t,
|
||||
pub pad1: [u8; 4],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct xcb_screen_iterator_t {
|
||||
pub data: *mut xcb_screen_t,
|
||||
pub rem: i32,
|
||||
pub index: i32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct xcb_screen_t {
|
||||
pub root: xcb_window_t,
|
||||
pub default_colormap: xcb_colormap_t,
|
||||
pub white_pixel: u32,
|
||||
pub black_pixel: u32,
|
||||
pub current_input_masks: u32,
|
||||
pub width_in_pixels: u16,
|
||||
pub height_in_pixels: u16,
|
||||
pub width_in_millimeters: u16,
|
||||
pub height_in_millimeters: u16,
|
||||
pub min_installed_maps: u16,
|
||||
pub max_installed_maps: u16,
|
||||
pub root_visual: xcb_visualid_t,
|
||||
pub backing_stores: u8,
|
||||
pub save_unders: u8,
|
||||
pub root_depth: u8,
|
||||
pub allowed_depths_len: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct xcb_randr_monitor_info_iterator_t {
|
||||
pub data: *mut xcb_randr_monitor_info_t,
|
||||
pub rem: i32,
|
||||
pub index: i32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct xcb_randr_monitor_info_t {
|
||||
pub name: xcb_atom_t,
|
||||
pub primary: u8,
|
||||
pub automatic: u8,
|
||||
pub n_output: u16,
|
||||
pub x: i16,
|
||||
pub y: i16,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
pub width_mm: u32,
|
||||
pub height_mm: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct xcb_randr_get_monitors_cookie_t {
|
||||
pub sequence: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct xcb_shm_get_image_cookie_t {
|
||||
pub sequence: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct xcb_void_cookie_t {
|
||||
pub sequence: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct xcb_generic_error_t {
|
||||
pub response_type: u8,
|
||||
pub error_code: u8,
|
||||
pub sequence: u16,
|
||||
pub resource_id: u32,
|
||||
pub minor_code: u16,
|
||||
pub major_code: u8,
|
||||
pub pad0: u8,
|
||||
pub pad: [u32; 5],
|
||||
pub full_sequence: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct xcb_shm_get_image_reply_t {
|
||||
pub response_type: u8,
|
||||
pub depth: u8,
|
||||
pub sequence: u16,
|
||||
pub length: u32,
|
||||
pub visual: xcb_visualid_t,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct xcb_randr_get_monitors_reply_t {
|
||||
pub response_type: u8,
|
||||
pub pad0: u8,
|
||||
pub sequence: u16,
|
||||
pub length: u32,
|
||||
pub timestamp: xcb_timestamp_t,
|
||||
pub n_monitors: u32,
|
||||
pub n_outputs: u32,
|
||||
pub pad1: [u8; 12],
|
||||
}
|
||||
93
libs/scrap/src/x11/iter.rs
Normal file
93
libs/scrap/src/x11/iter.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use libc;
|
||||
|
||||
use super::ffi::*;
|
||||
use super::{Display, Rect, Server};
|
||||
|
||||
//TODO: Do I have to free the displays?
|
||||
|
||||
pub struct DisplayIter {
|
||||
outer: xcb_screen_iterator_t,
|
||||
inner: Option<(xcb_randr_monitor_info_iterator_t, xcb_window_t)>,
|
||||
server: Rc<Server>,
|
||||
}
|
||||
|
||||
impl DisplayIter {
|
||||
pub unsafe fn new(server: Rc<Server>) -> DisplayIter {
|
||||
let mut outer = xcb_setup_roots_iterator(server.setup());
|
||||
let inner = Self::next_screen(&mut outer, &server);
|
||||
DisplayIter {
|
||||
outer,
|
||||
inner,
|
||||
server,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_screen(
|
||||
outer: &mut xcb_screen_iterator_t,
|
||||
server: &Server,
|
||||
) -> Option<(xcb_randr_monitor_info_iterator_t, xcb_window_t)> {
|
||||
if outer.rem == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let root = (*outer.data).root;
|
||||
|
||||
let cookie = xcb_randr_get_monitors_unchecked(
|
||||
server.raw(),
|
||||
root,
|
||||
1, //TODO: I don't know if this should be true or false.
|
||||
);
|
||||
|
||||
let response = xcb_randr_get_monitors_reply(server.raw(), cookie, ptr::null_mut());
|
||||
|
||||
let inner = xcb_randr_get_monitors_monitors_iterator(response);
|
||||
|
||||
libc::free(response as *mut _);
|
||||
xcb_screen_next(outer);
|
||||
|
||||
Some((inner, root))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for DisplayIter {
|
||||
type Item = Display;
|
||||
|
||||
fn next(&mut self) -> Option<Display> {
|
||||
loop {
|
||||
if let Some((ref mut inner, root)) = self.inner {
|
||||
// If there is something in the current screen, return that.
|
||||
if inner.rem != 0 {
|
||||
unsafe {
|
||||
let data = &*inner.data;
|
||||
|
||||
let display = Display::new(
|
||||
self.server.clone(),
|
||||
data.primary != 0,
|
||||
Rect {
|
||||
x: data.x,
|
||||
y: data.y,
|
||||
w: data.width,
|
||||
h: data.height,
|
||||
},
|
||||
root,
|
||||
);
|
||||
|
||||
xcb_randr_monitor_info_next(inner);
|
||||
return Some(display);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If there is no current screen, the screen iterator is empty.
|
||||
return None;
|
||||
}
|
||||
|
||||
// The current screen was empty, so try the next screen.
|
||||
self.inner = Self::next_screen(&mut self.outer, &self.server);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
libs/scrap/src/x11/mod.rs
Normal file
10
libs/scrap/src/x11/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
pub use self::capturer::*;
|
||||
pub use self::display::*;
|
||||
pub use self::iter::*;
|
||||
pub use self::server::*;
|
||||
|
||||
mod capturer;
|
||||
mod display;
|
||||
mod ffi;
|
||||
mod iter;
|
||||
mod server;
|
||||
122
libs/scrap/src/x11/server.rs
Normal file
122
libs/scrap/src/x11/server.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::ffi::*;
|
||||
use super::DisplayIter;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Server {
|
||||
raw: *mut xcb_connection_t,
|
||||
screenp: i32,
|
||||
setup: *const xcb_setup_t,
|
||||
}
|
||||
|
||||
/*
|
||||
use std::cell::RefCell;
|
||||
thread_local! {
|
||||
static SERVER: RefCell<Option<Rc<Server>>> = RefCell::new(None);
|
||||
}
|
||||
*/
|
||||
|
||||
impl Server {
|
||||
pub fn displays(slf: Rc<Server>) -> DisplayIter {
|
||||
unsafe { DisplayIter::new(slf) }
|
||||
}
|
||||
|
||||
pub fn default() -> Result<Rc<Server>, Error> {
|
||||
Ok(Rc::new(Server::connect(ptr::null())?))
|
||||
/*
|
||||
let mut res = Err(Error::from(0));
|
||||
SERVER.with(|xdo| {
|
||||
if let Ok(mut server) = xdo.try_borrow_mut() {
|
||||
if server.is_some() {
|
||||
unsafe {
|
||||
if 0 != xcb_connection_has_error(server.as_ref().unwrap().raw) {
|
||||
*server = None;
|
||||
println!("Reset x11 connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
if server.is_none() {
|
||||
println!("New x11 connection");
|
||||
match Server::connect(ptr::null()) {
|
||||
Ok(s) => {
|
||||
let s = Rc::new(s);
|
||||
res = Ok(s.clone());
|
||||
*server = Some(s);
|
||||
}
|
||||
Err(err) => {
|
||||
res = Err(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = Ok(server.as_ref().map(|x| x.clone()).unwrap());
|
||||
}
|
||||
}
|
||||
});
|
||||
res
|
||||
*/
|
||||
}
|
||||
|
||||
pub fn connect(addr: *const i8) -> Result<Server, Error> {
|
||||
unsafe {
|
||||
let mut screenp = 0;
|
||||
let raw = xcb_connect(addr, &mut screenp);
|
||||
|
||||
let error = xcb_connection_has_error(raw);
|
||||
if error != 0 {
|
||||
xcb_disconnect(raw);
|
||||
Err(Error::from(error))
|
||||
} else {
|
||||
let setup = xcb_get_setup(raw);
|
||||
Ok(Server {
|
||||
raw,
|
||||
screenp,
|
||||
setup,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> *mut xcb_connection_t {
|
||||
self.raw
|
||||
}
|
||||
pub fn screenp(&self) -> i32 {
|
||||
self.screenp
|
||||
}
|
||||
pub fn setup(&self) -> *const xcb_setup_t {
|
||||
self.setup
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Server {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
xcb_disconnect(self.raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Error {
|
||||
Generic,
|
||||
UnsupportedExtension,
|
||||
InsufficientMemory,
|
||||
RequestTooLong,
|
||||
ParseError,
|
||||
InvalidScreen,
|
||||
}
|
||||
|
||||
impl From<i32> for Error {
|
||||
fn from(x: i32) -> Error {
|
||||
use self::Error::*;
|
||||
match x {
|
||||
2 => UnsupportedExtension,
|
||||
3 => InsufficientMemory,
|
||||
4 => RequestTooLong,
|
||||
5 => ParseError,
|
||||
6 => InvalidScreen,
|
||||
_ => Generic,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user