From 201b2e480ff5818803696c1b9ea7fb562da96fa6 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Mon, 10 Sep 2018 21:23:11 -0500 Subject: [PATCH] window kinda works? --- src/capture.rs | 32 +++++++++++++--- src/gui.rs | 6 +++ src/image.rs | 64 ++++++++++++++++++++++---------- src/main.rs | 6 +-- xlib/src/display.rs | 53 +++++++++++++++++++++++++- xlib/src/{error.rs => errors.rs} | 9 +++++ xlib/src/lib.rs | 4 +- 7 files changed, 142 insertions(+), 32 deletions(-) rename xlib/src/{error.rs => errors.rs} (57%) diff --git a/src/capture.rs b/src/capture.rs index 1cebd69..6ab072d 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -4,16 +4,38 @@ use gui::GUI; use options::{Options, Region}; use xlib::Image; +use ImageExt; +use Rectangle; + pub fn capture(opt: &Options) -> Result { 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, diff --git a/src/gui.rs b/src/gui.rs index 72ceb26..d8cc9f3 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -15,6 +15,12 @@ impl GUI { /// Captures the window and produces a DynamicImage. pub fn capture_window(&self, window: Window) -> Result { + 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()) } diff --git a/src/image.rs b/src/image.rs index 0adae6c..b879c80 100644 --- a/src/image.rs +++ b/src/image.rs @@ -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, ScreenshotError>; - fn write_png(&self, out: impl AsRef) -> Result<(), ScreenshotError>; + fn to_data_buf(&self, rect: Rectangle) -> Result, ScreenshotError>; + fn write_png( + &self, + out: impl AsRef, + rect: Option, + ) -> Result<(), ScreenshotError>; } impl ImageExt for Image { /// Converts the image buffer into RGB(A). - fn to_data_buf(&self) -> Result, ScreenshotError> { - let size = self.get_size(); + fn to_data_buf(&self, rect: Rectangle) -> Result, 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) -> Result<(), ScreenshotError> { + fn write_png( + &self, + out: impl AsRef, + rect: Option, + ) -> 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(()) } diff --git a/src/main.rs b/src/main.rs index 7d88836..6f8e04a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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()) } diff --git a/xlib/src/display.rs b/xlib/src/display.rs index 2673d72..831a849 100644 --- a/xlib/src/display.rs +++ b/xlib/src/display.rs @@ -18,27 +18,76 @@ impl Display { pub fn connect(display_name: impl AsRef) -> Result { 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 { + 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 { + 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), 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; diff --git a/xlib/src/error.rs b/xlib/src/errors.rs similarity index 57% rename from xlib/src/error.rs rename to xlib/src/errors.rs index a38884b..d43daa0 100644 --- a/xlib/src/error.rs +++ b/xlib/src/errors.rs @@ -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, } diff --git a/xlib/src/lib.rs b/xlib/src/lib.rs index 37b38fb..aa8b320 100644 --- a/xlib/src/lib.rs +++ b/xlib/src/lib.rs @@ -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};