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 options::{Options, Region};
use xlib::Image; use xlib::Image;
use ImageExt;
use Rectangle;
pub fn capture(opt: &Options) -> Result<Image, ScreenshotError> { pub fn capture(opt: &Options) -> Result<Image, ScreenshotError> {
let gui = GUI::new()?; let gui = GUI::new()?;
let window_to_capture = match opt.region { // let window_to_capture = match opt.region {
Region::Fullscreen | Region::Selection => gui.display.get_default_root_window()?, // Region::Fullscreen | Region::Selection => gui.display.get_default_root_window()?,
Region::ActiveWindow => gui.get_active_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)?; 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 { // let final_capture = match opt.region {
// Region::Fullscreen | Region::ActiveWindow => window_capture, // Region::Fullscreen | Region::ActiveWindow => window_capture,

View file

@ -15,6 +15,12 @@ impl GUI {
/// Captures the window and produces a DynamicImage. /// Captures the window and produces a DynamicImage.
pub fn capture_window(&self, window: Window) -> Result<Image, ScreenshotError> { 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()) window.get_image().map_err(|err| err.into())
} }

View file

@ -6,23 +6,34 @@ use png::{self, Encoder, HasParameters};
use xlib::{Image, ImageByteOrder}; use xlib::{Image, ImageByteOrder};
use errors::ScreenshotError; use errors::ScreenshotError;
use Rectangle;
pub trait ImageExt { pub trait ImageExt {
fn to_data_buf(&self) -> Result<Vec<u8>, ScreenshotError>; fn to_data_buf(&self, rect: Rectangle) -> Result<Vec<u8>, ScreenshotError>;
fn write_png(&self, out: impl AsRef<Path>) -> Result<(), ScreenshotError>; fn write_png(
&self,
out: impl AsRef<Path>,
rect: Option<Rectangle>,
) -> Result<(), ScreenshotError>;
} }
impl ImageExt for Image { impl ImageExt for Image {
/// Converts the image buffer into RGB(A). /// Converts the image buffer into RGB(A).
fn to_data_buf(&self) -> Result<Vec<u8>, ScreenshotError> { fn to_data_buf(&self, rect: Rectangle) -> Result<Vec<u8>, ScreenshotError> {
let size = self.get_size(); 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 mut buf = vec![1; size];
let sbuf = self.buffer(); // source buffer let sbuf = self.buffer(); // source buffer
let mut sx = 0usize; // source idx let mut rx = rect.y as usize;
let mut dx = 0usize; // dest idx
if let ImageByteOrder::LSBFirst = self.get_byte_order()? { if let ImageByteOrder::LSBFirst = self.get_byte_order()? {
// LSBFirst // LSBFirst
while dx < size { 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] = sbuf.get_byte(sx + 2).unwrap() as u8;
buf[dx + 1] = sbuf.get_byte(sx + 1).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 + 2] = sbuf.get_byte(sx).unwrap() as u8;
@ -34,19 +45,32 @@ impl ImageExt for Image {
sx += 4; sx += 4;
dx += 4; dx += 4;
} }
rx += 1;
}
} }
Ok(buf) 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 file = File::create(out.as_ref())?;
let ref mut out = BufWriter::new(file); 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); encoder.set(png::ColorType::RGBA).set(png::BitDepth::Eight);
let mut writer = encoder.write_header()?; 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())?; writer.write_image_data(data.as_slice())?;
Ok(()) Ok(())
} }

View file

@ -3,11 +3,11 @@ extern crate screenshot;
extern crate structopt; extern crate structopt;
use failure::Error; use failure::Error;
use screenshot::{capture, ImageExt, Options}; use screenshot::{capture, Options};
use structopt::StructOpt; use structopt::StructOpt;
pub fn main() -> Result<(), Error> { pub fn main() -> Result<(), Error> {
let opt = Options::from_args(); let opt = Options::from_args();
let image = capture(&opt)?; // let image = capture(&opt)?;
image.write_png(opt.outfile).map_err(|err| err.into()) 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> { pub fn connect(display_name: impl AsRef<str>) -> Result<Display, X11Error> {
let display_name = CString::new(display_name.as_ref()).unwrap(); let display_name = CString::new(display_name.as_ref()).unwrap();
let inner = unsafe { x::XOpenDisplay(display_name.as_ptr()) }; let inner = unsafe { x::XOpenDisplay(display_name.as_ptr()) };
if inner.is_null() {
return Err(X11Error::DisplayOpenError);
}
Ok(Display { inner }) Ok(Display { inner })
} }
/// Returns the root window for the given screen. /// Returns the root window for the given screen.
pub fn get_root_window(&self, screen: i32) -> Result<Window, X11Error> { 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 { let window = Window {
display: self.inner, display: self.inner,
inner: unsafe { x::XRootWindow(self.inner, screen) }, inner,
}; };
Ok(window) Ok(window)
} }
/// Returns the root window for the default screen. /// Returns the root window for the default screen.
pub fn get_default_root_window(&self) -> Result<Window, X11Error> { 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 { let window = Window {
display: self.inner, display: self.inner,
inner: unsafe { x::XDefaultRootWindow(self.inner) }, inner,
}; };
Ok(window) 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. /// eturns the focus window and the current focus state.
pub fn get_input_focus(&self) -> Result<(Window, i32), X11Error> { pub fn get_input_focus(&self) -> Result<(Window, i32), X11Error> {
let mut focus_return: x::Window = 0; let mut focus_return: x::Window = 0;

View file

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

View file

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