window kinda works?

This commit is contained in:
Michael Zhang 2018-09-10 21:23:11 -05:00
parent f9126c4f83
commit 201b2e480f
No known key found for this signature in database
GPG key ID: A1B65B603268116B
7 changed files with 142 additions and 32 deletions

View file

@ -4,16 +4,38 @@ use gui::GUI;
use options::{Options, Region};
use xlib::Image;
use ImageExt;
use Rectangle;
pub fn capture(opt: &Options) -> Result<Image, ScreenshotError> {
let gui = GUI::new()?;
let window_to_capture = match opt.region {
Region::Fullscreen | Region::Selection => gui.display.get_default_root_window()?,
Region::ActiveWindow => gui.get_active_window()?,
};
// let window_to_capture = match opt.region {
// Region::Fullscreen | Region::Selection => gui.display.get_default_root_window()?,
// Region::ActiveWindow => gui.get_active_window()?,
// };
let window_to_capture = gui.display.get_default_root_window()?;
let capture = gui.capture_window(window_to_capture)?;
println!("captured the window");
let clip = match opt.region {
Region::ActiveWindow => {
let win = gui.get_active_window()?;
let attr = win.get_attributes()?;
let width = attr.get_width();
let height = attr.get_height();
let root = gui.display.get_default_root_window()?;
let (x, y, _) = gui.display.translate_coordinates(win, 0, 0, root)?;
Some(Rectangle {
x: x as u32,
y: y as u32,
width,
height,
})
}
_ => None,
};
capture.write_png(&opt.outfile, clip)?;
// let final_capture = match opt.region {
// Region::Fullscreen | Region::ActiveWindow => window_capture,

View file

@ -15,6 +15,12 @@ impl GUI {
/// Captures the window and produces a DynamicImage.
pub fn capture_window(&self, window: Window) -> Result<Image, ScreenshotError> {
let attr = window.get_attributes()?;
let width = attr.get_width();
let height = attr.get_height();
let root = self.display.get_default_root_window()?;
let (x, y, _) = self.display.translate_coordinates(window, 0, 0, root)?;
println!("x={} y={} width={} height={}", x, y, width, height);
window.get_image().map_err(|err| err.into())
}

View file

@ -6,47 +6,71 @@ use png::{self, Encoder, HasParameters};
use xlib::{Image, ImageByteOrder};
use errors::ScreenshotError;
use Rectangle;
pub trait ImageExt {
fn to_data_buf(&self) -> Result<Vec<u8>, ScreenshotError>;
fn write_png(&self, out: impl AsRef<Path>) -> Result<(), ScreenshotError>;
fn to_data_buf(&self, rect: Rectangle) -> Result<Vec<u8>, ScreenshotError>;
fn write_png(
&self,
out: impl AsRef<Path>,
rect: Option<Rectangle>,
) -> Result<(), ScreenshotError>;
}
impl ImageExt for Image {
/// Converts the image buffer into RGB(A).
fn to_data_buf(&self) -> Result<Vec<u8>, ScreenshotError> {
let size = self.get_size();
fn to_data_buf(&self, rect: Rectangle) -> Result<Vec<u8>, ScreenshotError> {
let (full_width, _) = (self.get_width() as usize, self.get_height() as usize);
let bytes_per_row = 4 * rect.width as usize;
let size = bytes_per_row * rect.height as usize;
let mut buf = vec![1; size];
let sbuf = self.buffer(); // source buffer
let mut sx = 0usize; // source idx
let mut dx = 0usize; // dest idx
let mut rx = rect.y as usize;
if let ImageByteOrder::LSBFirst = self.get_byte_order()? {
// LSBFirst
while dx < size {
buf[dx] = sbuf.get_byte(sx + 2).unwrap() as u8;
buf[dx + 1] = sbuf.get_byte(sx + 1).unwrap() as u8;
buf[dx + 2] = sbuf.get_byte(sx).unwrap() as u8;
buf[dx + 3] = if self.get_depth() == 32 {
sbuf.get_byte(sx + 3).unwrap() as u8
} else {
255u8
};
sx += 4;
dx += 4;
while rx < rect.height as usize {
let mut sx = (rx * full_width + rect.x as usize) * 4; // source idx
let mut dx = rx * bytes_per_row; // dest idx
let edx = (rx + 1) * bytes_per_row;
// println!("sx={} dx={} edx={}", sx,dx,edx);
while dx < edx {
buf[dx] = sbuf.get_byte(sx + 2).unwrap() as u8;
buf[dx + 1] = sbuf.get_byte(sx + 1).unwrap() as u8;
buf[dx + 2] = sbuf.get_byte(sx).unwrap() as u8;
buf[dx + 3] = if self.get_depth() == 32 {
sbuf.get_byte(sx + 3).unwrap() as u8
} else {
255u8
};
sx += 4;
dx += 4;
}
rx += 1;
}
}
Ok(buf)
}
fn write_png(&self, out: impl AsRef<Path>) -> Result<(), ScreenshotError> {
fn write_png(
&self,
out: impl AsRef<Path>,
rect: Option<Rectangle>,
) -> Result<(), ScreenshotError> {
let rect = rect.unwrap_or_else(|| Rectangle {
x: 0,
y: 0,
width: self.get_width(),
height: self.get_height(),
});
let file = File::create(out.as_ref())?;
let ref mut out = BufWriter::new(file);
let mut encoder = Encoder::new(out, self.get_width(), self.get_height());
let mut encoder = Encoder::new(out, rect.width, rect.height);
encoder.set(png::ColorType::RGBA).set(png::BitDepth::Eight);
let mut writer = encoder.write_header()?;
let data = self.to_data_buf()?;
let data = self.to_data_buf(rect)?;
writer.write_image_data(data.as_slice())?;
Ok(())
}

View file

@ -3,11 +3,11 @@ extern crate screenshot;
extern crate structopt;
use failure::Error;
use screenshot::{capture, ImageExt, Options};
use screenshot::{capture, Options};
use structopt::StructOpt;
pub fn main() -> Result<(), Error> {
let opt = Options::from_args();
let image = capture(&opt)?;
image.write_png(opt.outfile).map_err(|err| err.into())
// let image = capture(&opt)?;
capture(&opt).map(|_| ()).map_err(|err| err.into())
}

View file

@ -18,27 +18,76 @@ impl Display {
pub fn connect(display_name: impl AsRef<str>) -> Result<Display, X11Error> {
let display_name = CString::new(display_name.as_ref()).unwrap();
let inner = unsafe { x::XOpenDisplay(display_name.as_ptr()) };
if inner.is_null() {
return Err(X11Error::DisplayOpenError);
}
Ok(Display { inner })
}
/// Returns the root window for the given screen.
pub fn get_root_window(&self, screen: i32) -> Result<Window, X11Error> {
let inner = unsafe { x::XRootWindow(self.inner, screen) };
if inner == 0 {
return Err(X11Error::GetWindowError);
}
let window = Window {
display: self.inner,
inner: unsafe { x::XRootWindow(self.inner, screen) },
inner,
};
Ok(window)
}
/// Returns the root window for the default screen.
pub fn get_default_root_window(&self) -> Result<Window, X11Error> {
let inner = unsafe { x::XDefaultRootWindow(self.inner) };
if inner == 0 {
return Err(X11Error::GetWindowError);
}
let window = Window {
display: self.inner,
inner: unsafe { x::XDefaultRootWindow(self.inner) },
inner,
};
Ok(window)
}
/// Translate coordinates relative to w1 to coordinates relative to w2.
/// If the coordinates are contained in a mapped child of the destination window, the third return
/// value will hold that child window.
pub fn translate_coordinates(
&self,
w1: Window,
x: i32,
y: i32,
w2: Window,
) -> Result<(i32, i32, Option<Window>), X11Error> {
let mut rx = 0;
let mut ry = 0;
let mut child_return: x::Window = 0;
let status = unsafe {
x::XTranslateCoordinates(
self.inner,
w1.inner,
w2.inner,
x,
y,
&mut rx,
&mut ry,
&mut child_return,
)
};
if status == 0 {
return Err(X11Error::TranslateCoordinatesError);
}
let child = match child_return {
0 => None,
val => Some(Window {
display: self.inner,
inner: val,
}),
};
Ok((rx, ry, child))
}
/// eturns the focus window and the current focus state.
pub fn get_input_focus(&self) -> Result<(Window, i32), X11Error> {
let mut focus_return: x::Window = 0;

View file

@ -2,12 +2,21 @@
#[allow(missing_docs)]
#[derive(Debug, Fail)]
pub enum X11Error {
#[fail(display = "failed to open display")]
DisplayOpenError,
#[fail(display = "failed to get attributes")]
GetAttributesError,
#[fail(display = "failed to get window")]
GetWindowError,
#[fail(display = "invalid byte order")]
InvalidByteOrder,
#[fail(display = "failed to translate coordinates")]
TranslateCoordinatesError,
#[fail(display = "error")]
Error,
}

View file

@ -10,11 +10,11 @@ extern crate libc;
extern crate x11;
mod display;
mod error;
mod errors;
mod image;
mod window;
pub use display::Display;
pub use error::X11Error;
pub use errors::X11Error;
pub use image::{Image, ImageByteOrder, PixBuffer};
pub use window::{Window, WindowAttributes};