Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions Cargo.lock

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

6 changes: 3 additions & 3 deletions libwayshot/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ const SHIFT10BITS_2: u32 = 10;
pub fn create_converter(format: wl_shm::Format) -> Option<Box<dyn Convert>> {
match format {
wl_shm::Format::Xbgr8888 | wl_shm::Format::Abgr8888 => {
Some(Box::new(ConvertNone::default()))
Some(Box::<ConvertNone>::default())
}
wl_shm::Format::Xrgb8888 | wl_shm::Format::Argb8888 => {
Some(Box::new(ConvertRGB8::default()))
Some(Box::<ConvertRGB8>::default())
}
wl_shm::Format::Xbgr2101010 | wl_shm::Format::Abgr2101010 => {
Some(Box::new(ConvertBGR10::default()))
Some(Box::<ConvertBGR10>::default())
}
_ => None,
}
Expand Down
14 changes: 12 additions & 2 deletions libwayshot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ pub enum EncodingFormat {
Ppm,
}

impl From<EncodingFormat> for image::ImageOutputFormat {
fn from(value: EncodingFormat) -> Self {
match value {
EncodingFormat::Jpg => image::ImageFormat::Jpeg.into(),
EncodingFormat::Png => image::ImageFormat::Png.into(),
EncodingFormat::Ppm => image::ImageFormat::Pnm.into(),
}
}
}

struct CaptureFrameState {
formats: Vec<FrameFormat>,
state: Option<FrameState>,
Expand Down Expand Up @@ -378,9 +388,9 @@ fn create_shm_fd() -> std::io::Result<RawFd> {
/// Write an instance of FrameCopy to anything that implements Write trait. Eg: Stdout or a file
/// on the disk.
pub fn write_to_file(
mut output_file: impl Write,
mut output_file: &mut impl Write,
encoding_format: EncodingFormat,
frame_copy: FrameCopy,
frame_copy: &FrameCopy,
) -> Result<(), Box<dyn Error>> {
log::debug!(
"Writing to disk with encoding format: {:#?}",
Expand Down
2 changes: 2 additions & 0 deletions wayshot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ wayland-protocols = { version = "0.30.0", features=["client", "unstable"] }

libwayshot = { path = "../libwayshot" }

image = { version = "0.24", default-features = false, features = ["jpeg", "png", "pnm"] }

[[bin]]
name = "wayshot"
path = "src/wayshot.rs"
247 changes: 176 additions & 71 deletions wayshot/src/wayshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use std::{
cmp, env,
error::Error,
fs::File,
io::{stdout, BufWriter},
io::{stdout, BufWriter, Cursor, Write},
process::exit,
time::{SystemTime, UNIX_EPOCH},
};

use libwayshot::CaptureRegion;
use wayland_client::{
globals::{registry_queue_init, GlobalListContents},
protocol::{wl_output::WlOutput, wl_registry},
Expand Down Expand Up @@ -69,6 +70,16 @@ impl wayland_client::Dispatch<wl_registry::WlRegistry, GlobalListContents> for W
}
}

struct MutiCaptureMessage {
output: WlOutput,
region: CaptureRegion,
}

enum CaptureWay {
Region(CaptureRegion),
WayOutput(WlOutput),
}

fn main() -> Result<(), Box<dyn Error>> {
let args = clap::set_flags().get_matches();
env::set_var("RUST_LOG", "wayshot=info");
Expand Down Expand Up @@ -96,65 +107,109 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(1);
}

let output: WlOutput = if args.is_present("output") {
output::get_wloutput(
args.value_of("output").unwrap().trim().to_string(),
output::get_all_outputs(&mut globals, &mut conn),
)
} else {
output::get_all_outputs(&mut globals, &mut conn)
.first()
.unwrap()
.wl_output
.clone()
};

let frame_copy: libwayshot::FrameCopy = if args.is_present("slurp") {
let capture_area = if args.is_present("slurp") {
if args.value_of("slurp").unwrap() == "" {
log::error!("Failed to recieve geometry.");
exit(1);
}
let region: libwayshot::CaptureRegion = parse_geometry(args.value_of("slurp").unwrap())
.expect("Invalid geometry specification");

let outputs = output::get_all_outputs(&mut globals, &mut conn);
let mut intersecting_outputs: Vec<output::OutputInfo> = Vec::new();
for output in outputs {
let x1: i32 = cmp::max(output.dimensions.x, region.x_coordinate);
let y1: i32 = cmp::max(output.dimensions.y, region.y_coordinate);
let x2: i32 = cmp::min(
output.dimensions.x + output.dimensions.width,
region.x_coordinate + region.width,
);
let y2: i32 = cmp::min(
output.dimensions.y + output.dimensions.height,
region.y_coordinate + region.height,
);

let width = x2 - x1;
let height = y2 - y1;

if !(width <= 0 || height <= 0) {
intersecting_outputs.push(output);
CaptureWay::Region(
parse_geometry(args.value_of("slurp").unwrap())
.expect("Invalid geometry specification"),
)
} else if !args.is_present("output") {
let (mut startx, mut starty) = (0, 0);
let (mut endx, mut endy) = (0, 0);
let outputinfos = output::get_all_outputs(&mut globals, &mut conn);
for outputinfo in outputinfos {
if outputinfo.dimensions.x < startx {
startx = outputinfo.dimensions.x;
}
if outputinfo.dimensions.y < starty {
starty = outputinfo.dimensions.y;
}
if outputinfo.dimensions.x + outputinfo.dimensions.width > endx {
endx = outputinfo.dimensions.x + outputinfo.dimensions.width;
}
if outputinfo.dimensions.y + outputinfo.dimensions.height > endy {
endy = outputinfo.dimensions.y + outputinfo.dimensions.height;
}
}
if intersecting_outputs.is_empty() {
log::error!("Provided capture region doesn't intersect with any outputs!");
exit(1);
}
// NOTE: Figure out box bounds for multi monitor screenshot.

libwayshot::capture_output_frame(
&mut globals,
&mut conn,
cursor_overlay,
output,
Some(region),
)?
CaptureWay::Region(CaptureRegion {
x_coordinate: startx,
y_coordinate: starty,
width: endx - startx,
height: endy - starty,
})
} else {
libwayshot::capture_output_frame(&mut globals, &mut conn, cursor_overlay, output, None)?
CaptureWay::WayOutput(output::get_wloutput(
args.value_of("output").unwrap().trim().to_string(),
output::get_all_outputs(&mut globals, &mut conn),
))
};

let frame_copy: (Vec<libwayshot::FrameCopy>, Option<(i32, i32)>) = match capture_area {
CaptureWay::Region(region) => {
let mut framecopys = Vec::new();

let outputs = output::get_all_outputs(&mut globals, &mut conn);
let mut intersecting_outputs: Vec<MutiCaptureMessage> = Vec::new();
for output in outputs.iter() {
let x1: i32 = cmp::max(output.dimensions.x, region.x_coordinate);
let y1: i32 = cmp::max(output.dimensions.y, region.y_coordinate);
let x2: i32 = cmp::min(
output.dimensions.x + output.dimensions.width,
region.x_coordinate + region.width,
);
let y2: i32 = cmp::min(
output.dimensions.y + output.dimensions.height,
region.y_coordinate + region.height,
);

let width = x2 - x1;
let height = y2 - y1;

if !(width <= 0 || height <= 0) {
let true_x = region.x_coordinate - output.dimensions.x;
let true_y = region.y_coordinate - output.dimensions.y;
let true_region = CaptureRegion {
x_coordinate: true_x,
y_coordinate: true_y,
width: region.width,
height: region.height,
};
intersecting_outputs.push(MutiCaptureMessage {
output: output.wl_output.clone(),
region: true_region,
});
}
}
if intersecting_outputs.is_empty() {
log::error!("Provided capture region doesn't intersect with any outputs!");
exit(1);
}

for ouput_info in intersecting_outputs {
framecopys.push(libwayshot::capture_output_frame(
&mut globals,
&mut conn,
cursor_overlay,
ouput_info.output.clone(),
Some(ouput_info.region),
)?);
}
(framecopys, Some((region.width, region.height)))
}
CaptureWay::WayOutput(output) => (
vec![libwayshot::capture_output_frame(
&mut globals,
&mut conn,
cursor_overlay,
output,
None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be efficient to capture the entire wl_output? Maybe we should consider passing in the intersecting region only.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you mean about entire wl_output? maybe combining all screens together to one image?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mstoekcl one just concat the screens, if you want to capture the whole region, just caculate the biggest region, from the min (x, y) to the max (x, y), make every screen capture once, resize and overlay the images together, then we get the whole screens image

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

)?],
None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once again, maybe we should consider exporting the intersecting capture region and then blit the resulting output to the final buffer.

),
};
let extension = if args.is_present("extension") {
let ext: &str = &args.value_of("extension").unwrap().trim().to_lowercase();
match ext {
Expand All @@ -173,32 +228,82 @@ fn main() -> Result<(), Box<dyn Error>> {
if extension != libwayshot::EncodingFormat::Png {
log::debug!("Using custom extension: {:#?}", extension);
}
if frame_copy.0.len() == 1 {
let frame_copy = &frame_copy.0[0];
if args.is_present("stdout") {
let stdout = stdout();
let mut writer = BufWriter::new(stdout.lock());
libwayshot::write_to_file(&mut writer, extension, frame_copy)?;
} else {
let path = if args.is_present("file") {
args.value_of("file").unwrap().trim().to_string()
} else {
let time = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(n) => n.as_secs().to_string(),
Err(_) => {
log::error!("SystemTime before UNIX EPOCH!");
exit(1);
}
};

if args.is_present("stdout") {
let stdout = stdout();
let writer = BufWriter::new(stdout.lock());
libwayshot::write_to_file(writer, extension, frame_copy)?;
time + match extension {
libwayshot::EncodingFormat::Png => "-wayshot.png",
libwayshot::EncodingFormat::Jpg => "-wayshot.jpg",
libwayshot::EncodingFormat::Ppm => "-wayshot.ppm",
}
};

libwayshot::write_to_file(&mut File::create(path)?, extension, frame_copy)?;
}
} else {
let path = if args.is_present("file") {
args.value_of("file").unwrap().trim().to_string()
let mut images = Vec::new();
let (frame_copy, region) = frame_copy;
let (width, height) = region.unwrap();
for frame_copy in frame_copy {
let mut buff = Cursor::new(Vec::new());
libwayshot::write_to_file(&mut buff, extension, &frame_copy)?;
let image = image::load_from_memory(buff.get_ref())?;
let image = image::imageops::resize(
&image,
width as u32,
height as u32,
image::imageops::FilterType::Gaussian,
);
images.push(image);
}
use image::imageops::overlay;
let mut image_bottom = images[0].clone();
for image in images {
overlay(&mut image_bottom, &image, 0, 0);
}
if args.is_present("stdout") {
let stdout = stdout();
let mut buff = Cursor::new(Vec::new());

let mut writer = BufWriter::new(stdout.lock());
image_bottom.write_to(&mut buff, extension)?;
writer.write_all(buff.get_ref())?;
} else {
let time = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(n) => n.as_secs().to_string(),
Err(_) => {
log::error!("SystemTime before UNIX EPOCH!");
exit(1);
let path = if args.is_present("file") {
args.value_of("file").unwrap().trim().to_string()
} else {
let time = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(n) => n.as_secs().to_string(),
Err(_) => {
log::error!("SystemTime before UNIX EPOCH!");
exit(1);
}
};

time + match extension {
libwayshot::EncodingFormat::Png => "-wayshot.png",
libwayshot::EncodingFormat::Jpg => "-wayshot.jpg",
libwayshot::EncodingFormat::Ppm => "-wayshot.ppm",
}
};

time + match extension {
libwayshot::EncodingFormat::Png => "-wayshot.png",
libwayshot::EncodingFormat::Jpg => "-wayshot.jpg",
libwayshot::EncodingFormat::Ppm => "-wayshot.ppm",
}
};

libwayshot::write_to_file(File::create(path)?, extension, frame_copy)?;
image_bottom.save(path)?;
}
}

Ok(())
}