forked from michael/leanshot
window kinda works?
This commit is contained in:
parent
f9126c4f83
commit
201b2e480f
7 changed files with 142 additions and 32 deletions
|
@ -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,
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
44
src/image.rs
44
src/image.rs
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
|
@ -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};
|
||||||
|
|
Loading…
Reference in a new issue