Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backend/x11 #22

Merged
merged 33 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6fc3334
adding dependencies
TornaxO7 Nov 10, 2023
a40d9f1
xorg: adding backend implementation
TornaxO7 Nov 10, 2023
be41630
reformatted code
TornaxO7 Nov 10, 2023
5d50f3b
xorg: fixing height bug...
TornaxO7 Nov 10, 2023
5db429d
xorg: adding doc and removing unused code
TornaxO7 Nov 10, 2023
f0e9baa
xor: improvign docs
TornaxO7 Nov 10, 2023
de2d40d
Merge branch 'main' of github.com:eneoli/flakeshot into backend/x11
TornaxO7 Nov 10, 2023
2ae777d
updating Cargo.lock
TornaxO7 Nov 10, 2023
5d69a39
xorg: adding example
TornaxO7 Nov 10, 2023
7e3f3d8
xorg: fixing `backend::get_images` condition
TornaxO7 Nov 10, 2023
c5d82cc
x11: improving variable name
TornaxO7 Nov 10, 2023
a871393
xorg: adding credits!
TornaxO7 Nov 10, 2023
e32da24
Merge branch 'main' into backend/x11
TornaxO7 Nov 11, 2023
bd162aa
pushing current changes
TornaxO7 Nov 12, 2023
29afedd
x11: cleanup code
TornaxO7 Nov 12, 2023
d4d40db
Merge branch 'main' of github.com:eneoli/flakeshot into backend/x11
TornaxO7 Nov 12, 2023
3f3f5d8
updating `Cargo.lock`
TornaxO7 Nov 12, 2023
9da0db7
Merge branch 'main' of github.com:eneoli/flakeshot into backend/x11
TornaxO7 Nov 12, 2023
268c03c
Merge branch 'main' of github.com:eneoli/flakeshot into backend/x11
TornaxO7 Nov 12, 2023
67e8945
Merge branch 'main' of github.com:eneoli/flakeshot into backend/x11
TornaxO7 Nov 13, 2023
e14a9db
Merge branch 'main' of github.com:eneoli/flakeshot into backend/x11
TornaxO7 Nov 13, 2023
6eef01f
Merge branch 'main' of github.com:eneoli/flakeshot into backend/x11
TornaxO7 Nov 13, 2023
4ace28f
Merge branch 'backend/x11' of github.com:eneoli/flakeshot into backen…
TornaxO7 Nov 13, 2023
139b49b
updating cargo lock
TornaxO7 Nov 13, 2023
17eeeef
Merge branch 'main' into backend/x11
TornaxO7 Nov 13, 2023
dced80a
pushing changes
TornaxO7 Nov 14, 2023
03a2a79
adjust x11 api
TornaxO7 Nov 14, 2023
992dff3
cleanup code
TornaxO7 Nov 14, 2023
c16d527
fix doc code
TornaxO7 Nov 14, 2023
adc6201
adjust enums and struct
TornaxO7 Nov 15, 2023
158158f
improve check if xorg is running or not
TornaxO7 Nov 15, 2023
cf95915
fixing algorithm
TornaxO7 Nov 15, 2023
fd0a38c
renaming attribute
TornaxO7 Nov 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 60 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ authors = ["eneoli", "TornaxO7"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0"
arboard = "3.2"
clap = { version = "4.4", features = ["derive", "cargo"] }
image = "0.24"
thiserror = "1.0"
x11rb = { version = "0.12", features = ["randr"] }
tokio = { version = "1.34", features = ["full"] }
arboard = "3.2"
clap = { version = "4.4", features = ["derive", "cargo"] }
anyhow = "1.0"
42 changes: 31 additions & 11 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
//! Contains the different backends to get the screenshot from.

pub mod wayland;
pub mod xorg;
pub mod x11;

/// A general backend error enum.
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// Represents that an error occured while trying to get a screenshot on X11.
#[error(transparent)]
X11(#[from] x11::Error),
}

pub type Pixel = u16;

pub enum OutputIdentifier {
X11(u32),
Wayland {
id: u32,
name: String,
description: String,
},
#[derive(Debug)]
pub enum MonitorInfo {
X11 { name: u32 },
Wayland { name: String, description: String },
}

#[derive(Debug)]
pub struct OutputInfo {
pub identifier: OutputIdentifier,
pub width: Pixel,
pub height: Pixel,
pub x: i16,
pub y: i16,

pub id: u32,
pub monitor_info: MonitorInfo,
}

pub async fn create_screenshots() -> anyhow::Result<Vec<(OutputInfo, image::DynamicImage)>> {
todo!()
/// The main function of this module.
///
/// This function returns an rgb-image for each screen (or "monitor" in other
/// words).
pub async fn create_screenshots() -> Result<Vec<(OutputInfo, image::DynamicImage)>, Error> {
let xorg_is_running = { x11rb::connect(None).is_ok() };

if xorg_is_running {
x11::get_images().map_err(Error::from)
} else {
todo!()
}
}
162 changes: 162 additions & 0 deletions src/backend/x11.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//! Backend implementation for X11.
//!
//! # Credits
//! This module is inspired by the [`screenshots`] crate.
//!
//! [`screenshots`]: https://crates.io/crates/screenshots
use image::{DynamicImage, RgbImage, RgbaImage};
use x11rb::{
connection::Connection,
protocol::xproto::{ImageFormat, ImageOrder, Screen},
rust_connection::RustConnection,
};

use super::{MonitorInfo, OutputInfo};

/// A general enum with possible errors as values which can occur while
/// operating with the xorg-server.
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Couldn't connect to the xorg server: {0}")]
ConnectError(#[from] x11rb::errors::ConnectError),

#[error("The connection broke with the xorg server: {0}")]
ConnectionError(#[from] x11rb::errors::ConnectionError),

#[error("Couldn't request an image from the xorg-server: {0}")]
ReplyError(#[from] x11rb::errors::ReplyError),
}

/// The main function of this module.
///
/// This function collects, from each screen (a.k.a your monitors) a screenshot
/// and returns it.
///
/// # Example
/// ```no_run
/// use flakeshot::backend::x11::get_images;
/// use std::fs::File;
/// use image::ImageOutputFormat;
///
/// fn main() {
/// let mut file = File::create("./targets/example_screenshot.png").unwrap();
/// let images = get_images().unwrap();
///
/// // we will only use the first screenshot for this example
/// let first_screen = images.first().unwrap();
/// let image = &first_screen.1;
///
/// image.write_to(&mut file, ImageOutputFormat::Png).unwrap();
/// }
/// ```
pub fn get_images() -> Result<Vec<(OutputInfo, image::DynamicImage)>, Error> {
use x11rb::protocol::randr::ConnectionExt;

let (conn, _) = x11rb::connect(None)?;
let setup = conn.setup();

let mut images = Vec::with_capacity(setup.roots.len());

for screen in &setup.roots {
let image = get_image(&conn, &screen)?;

let monitors = conn.randr_get_monitors(screen.root, true)?.reply()?;
for monitor in &monitors.monitors {
let monitor_info = MonitorInfo::X11 { name: monitor.name };

let output_info = OutputInfo {
id: screen.root,

width: monitor.width,
height: monitor.height,
x: monitor.x,
y: monitor.y,

monitor_info,
};

images.push((output_info, image.clone()));
}
}

Ok(images)
}

fn get_image(conn: &RustConnection, screen: &Screen) -> Result<DynamicImage, Error> {
use x11rb::protocol::xproto::ConnectionExt;
const ALL_BITS: u32 = u32::MAX;

let setup = &conn.setup();
let width_u16 = screen.width_in_pixels;
let height_u16 = screen.height_in_pixels;

let width_u32 = u32::from(width_u16);
let height_u32 = u32::from(height_u16);

let (image_bytes, pixmap_format) = {
let image_reply = conn
.get_image(
ImageFormat::Z_PIXMAP,
screen.root,
0,
0,
width_u16,
height_u16,
ALL_BITS,
)
.map_err(Error::from)?
.reply()
.map_err(Error::from)?;

let pixmap_format = setup
.pixmap_formats
.iter()
.find(|format| format.depth == image_reply.depth)
.unwrap();

(image_reply.data, pixmap_format)
};

let bit_order = setup.bitmap_format_bit_order;
let image = match pixmap_format.bits_per_pixel {
24 => get_rgb_image(width_u32, height_u32, image_bytes, bit_order),
32 => get_rgba_image(width_u32, height_u32, image_bytes, bit_order),
_ => unimplemented!(
"We don't support {}-bit RGB values",
pixmap_format.bits_per_pixel
),
};

Ok(image)
}

fn get_rgb_image(
width: u32,
height: u32,
image_bytes: Vec<u8>,
bit_order: ImageOrder,
) -> DynamicImage {
let mut rgb_image = RgbImage::from_vec(width, height, image_bytes).unwrap();
if bit_order == ImageOrder::LSB_FIRST {
for rgb in rgb_image.pixels_mut() {
rgb.0.reverse();
}
}
DynamicImage::ImageRgb8(rgb_image)
}

fn get_rgba_image(
width: u32,
height: u32,
image_bytes: Vec<u8>,
bit_order: ImageOrder,
) -> DynamicImage {
let mut rgba_image = RgbaImage::from_vec(width, height, image_bytes).unwrap();

if bit_order == ImageOrder::LSB_FIRST {
for rgba in rgba_image.pixels_mut() {
rgba.0[0..3].reverse();
}
}
DynamicImage::ImageRgba8(rgba_image)
}
1 change: 0 additions & 1 deletion src/backend/xorg/mod.rs

This file was deleted.

7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
pub mod backend;
pub mod cli;

pub enum Error {}
/// An enum error which contains all possible error sources while executing flakeshot.
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("An error occured in the backend: {0}")]
Backend(#[from] backend::Error),
}