diff --git a/Cargo.lock b/Cargo.lock index 4e36a3c..d4d4ade 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,15 +104,6 @@ dependencies = [ "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "imlib2" -version = "0.1.0" -dependencies = [ - "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "imlib2-sys 0.1.0", - "x11 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "imlib2-sys" version = "0.1.0" @@ -212,11 +203,12 @@ name = "screenshot" version = "0.3.0" dependencies = [ "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "imlib2 0.1.0", + "imlib2-sys 0.1.0", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "xlib 0.1.0", + "x11 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -356,16 +348,6 @@ dependencies = [ "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "xlib" -version = "0.1.0" -dependencies = [ - "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "imlib2 0.1.0", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "x11 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [metadata] "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" diff --git a/Cargo.toml b/Cargo.toml index 754bad3..8c21337 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ version = "0.3.0" authors = ["Michael Zhang "] [workspace] -members = [".", "imlib2", "imlib2/imlib2-sys", "xlib"] +members = [".", "imlib2-sys"] [lib] crate-type = ["dylib", "rlib"] @@ -13,7 +13,8 @@ crate-type = ["dylib", "rlib"] [dependencies] failure = "0.1" png = "0.12" -imlib2 = { path = "imlib2" } +imlib2-sys = { path = "imlib2-sys" } +libc = "0.2" structopt = "0.2" time = "0.1" -xlib = { path = "xlib" } +x11 = { version = "2.18", features = ["xlib"] } diff --git a/imlib2/imlib2-sys/Cargo.toml b/imlib2-sys/Cargo.toml similarity index 100% rename from imlib2/imlib2-sys/Cargo.toml rename to imlib2-sys/Cargo.toml diff --git a/imlib2/imlib2-sys/build.rs b/imlib2-sys/build.rs similarity index 100% rename from imlib2/imlib2-sys/build.rs rename to imlib2-sys/build.rs diff --git a/imlib2/imlib2-sys/lib.rs b/imlib2-sys/lib.rs similarity index 100% rename from imlib2/imlib2-sys/lib.rs rename to imlib2-sys/lib.rs diff --git a/imlib2/Cargo.toml b/imlib2/Cargo.toml deleted file mode 100644 index 78672c8..0000000 --- a/imlib2/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "imlib2" -version = "0.1.0" -authors = ["Michael Zhang "] - -[lib] -path = "lib.rs" - -[dependencies] -failure = "0.1" -imlib2-sys = { path = "imlib2-sys" } -x11 = { version = "2.18", features = ["xlib"] } diff --git a/imlib2/lib.rs b/imlib2/lib.rs deleted file mode 100644 index 12c6bc0..0000000 --- a/imlib2/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Safe-ish bindings to imlib2 (at least the only parts I need). - -#![deny(missing_docs)] - -#[macro_use] -extern crate failure; -extern crate imlib2_sys; -extern crate x11; - -mod errors; -mod image; - -pub use errors::Error; -pub use image::Image; -pub use imlib2_sys::{Drawable, Pixmap}; - -/// Set the display for the imlib context. -pub fn context_set_display(display: *mut x11::xlib::Display) { - unsafe { imlib2_sys::imlib_context_set_display(display as *mut imlib2_sys::_XDisplay) }; -} - -/// Set the visual for the imlib context. -pub fn context_set_visual(visual: *mut x11::xlib::Visual) { - unsafe { imlib2_sys::imlib_context_set_visual(visual as *mut imlib2_sys::Visual) }; -} diff --git a/src/capture.rs b/src/capture.rs index 548117c..5999b98 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -11,7 +11,7 @@ pub fn capture(opt: &Options) -> Result<(), ScreenshotError> { _ => gui.display.get_default_root_window()?, }; - let capture = gui.capture_window(window_to_capture)?; + let capture = gui.capture_window(&opt, window_to_capture)?; capture.save_image(&opt.outfile)?; Ok(()) diff --git a/src/gui.rs b/src/gui.rs index 96b6ef6..8aba5cf 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -1,9 +1,11 @@ use imlib2::{self, Image as Image2}; -use xlib::{Display, Image, Window, Visual}; +use xlib::{Display, EventKind, Visual, Window}; use errors::ScreenshotError; +use Options; use Rectangle; use SelectWindow; +use Region; pub struct GUI { pub(crate) display: Display, @@ -15,19 +17,31 @@ impl GUI { Ok(GUI { display }) } - /// Captures the window and produces a DynamicImage. - pub fn capture_window(&self, window: Window) -> Result { + /// Captures the window and produces an Image. + pub fn capture_window(&self, opt: &Options, window: Window) -> Result { let attr = window.get_attributes()?; - let width = attr.get_width() as i32; - let height = attr.get_height() as i32; + let mut width = attr.get_width(); + let mut height = attr.get_height(); let root = self.display.get_default_root_window()?; - let (x, y, _) = self.display.translate_coordinates(window, 0, 0, root)?; + let (mut x, mut y, _) = self.display.translate_coordinates(window, 0, 0, root)?; - imlib2::context_set_display(self.display.as_raw()); + imlib2::context_set_display(&self.display); let visual = Visual::default(&self.display, 0); - imlib2::context_set_visual(visual.as_raw()); + imlib2::context_set_visual(&visual); - Image2::create_from_drawable(window, 0, x, y, width, height, true).map_err(|err| err.into()) + match opt.region { + Region::Selection => { + let region = self.interactive_select()?; + x = region.x; + y = region.y; + width = region.width; + height = region.height; + }, + _ => () + } + + Image2::create_from_drawable(window, 0, x, y, width as i32, height as i32, true) + .map_err(|err| err.into()) } /// Get the active window. @@ -40,8 +54,19 @@ impl GUI { /// Brings up an interactive selection GUI. #[allow(dead_code)] - pub fn interactive_select(&self, _capture: &Image) -> Result { + pub fn interactive_select(&self) -> Result { let window = SelectWindow::new(&self.display); + let root = self.display.get_default_root_window()?; + + let root_im = root.get_image(); + + let mut done = 0; + while done == 0 && self.display.pending()? > 0 { + let ev = self.display.next_event()?; + match ev.kind() { + EventKind::None => (), + } + } Err(ScreenshotError::Error) } } diff --git a/imlib2/errors.rs b/src/imlib2/errors.rs similarity index 100% rename from imlib2/errors.rs rename to src/imlib2/errors.rs diff --git a/imlib2/image.rs b/src/imlib2/image.rs similarity index 77% rename from imlib2/image.rs rename to src/imlib2/image.rs index e40ef81..e4e9031 100644 --- a/imlib2/image.rs +++ b/src/imlib2/image.rs @@ -2,9 +2,9 @@ use std::ffi::CString; use std::path::Path; use imlib2_sys as im; -use x11; -use Error; +use imlib2::Error; +use xlib::Drawable; /// A simple wrapper around Imlib_Image pub struct Image { @@ -14,7 +14,7 @@ pub struct Image { impl Image { /// Creates an Image from a pixmap. pub fn create_from_drawable( - drawable: impl AsRef, + drawable: impl Drawable, pixmap: im::Pixmap, x: i32, y: i32, @@ -22,7 +22,7 @@ impl Image { height: i32, grab_x: bool, ) -> Result { - unsafe { im::imlib_context_set_drawable(*drawable.as_ref()) }; + unsafe { im::imlib_context_set_drawable(drawable.as_drawable()) }; let image = unsafe { im::imlib_create_image_from_drawable( pixmap, @@ -33,15 +33,21 @@ impl Image { if grab_x { 1 } else { 0 }, ) as im::Imlib_Image }; + unsafe { im::imlib_context_set_image(image) }; Ok(Image { inner: image }) } /// Save this image pub fn save_image(&self, file: impl AsRef) -> Result<(), Error> { - unsafe { im::imlib_context_set_image((*self).inner) }; let mut error = 0; let path = CString::new(file.as_ref().to_str().unwrap()).unwrap(); unsafe { im::imlib_save_image_with_error_return(path.as_ptr(), &mut error) }; Ok(()) } } + +impl Drop for Image { + fn drop(&mut self) { + unsafe { im::imlib_free_image() }; + } +} diff --git a/src/imlib2/mod.rs b/src/imlib2/mod.rs new file mode 100644 index 0000000..179a91a --- /dev/null +++ b/src/imlib2/mod.rs @@ -0,0 +1,26 @@ +//! Safe-ish bindings to imlib2 (at least the only parts I need). + +#![deny(missing_docs)] + +mod errors; +mod image; + +use imlib2_sys; + +pub use self::errors::Error; +pub use self::image::Image; +pub use imlib2_sys::{Drawable, Pixmap}; + +use xlib::{Display, Visual}; + +/// Set the display for the imlib context. +pub fn context_set_display(display: &Display) { + unsafe { + imlib2_sys::imlib_context_set_display(display.as_raw() as *mut imlib2_sys::_XDisplay) + }; +} + +/// Set the visual for the imlib context. +pub fn context_set_visual(visual: &Visual) { + unsafe { imlib2_sys::imlib_context_set_visual(visual.as_raw() as *mut imlib2_sys::Visual) }; +} diff --git a/src/lib.rs b/src/lib.rs index 6a041c2..ae3fcf9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,14 @@ +#![feature(try_from)] + #[macro_use] extern crate failure; -extern crate imlib2; +extern crate imlib2_sys; +extern crate libc; extern crate png; #[macro_use] extern crate structopt; extern crate time; -extern crate xlib; +extern crate x11; mod capture; mod errors; @@ -13,9 +16,13 @@ mod gui; mod options; mod window; +pub mod imlib2; +pub mod xlib; + use structopt::StructOpt; use xlib::Rectangle; +use errors::ScreenshotError; pub use capture::capture; -pub use options::Options; +pub use options::{Options, Region}; pub use window::SelectWindow; diff --git a/src/main.rs b/src/main.rs index 6f8e04a..9a29d50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,5 @@ use structopt::StructOpt; pub fn main() -> Result<(), Error> { let opt = Options::from_args(); - // let image = capture(&opt)?; capture(&opt).map(|_| ()).map_err(|err| err.into()) } diff --git a/src/options.rs b/src/options.rs index 35ff500..ce01a96 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,20 +1,16 @@ use std::path::PathBuf; -#[derive(StructOpt)] +use ScreenshotError; + pub enum Region { - #[structopt(name = "fullscreen")] Fullscreen, - - #[structopt(name = "window")] ActiveWindow, - - #[structopt(name = "select")] Selection, } #[derive(StructOpt)] pub struct Options { - #[structopt(subcommand)] + #[structopt(parse(try_from_str = "Region::from_str"))] pub region: Region, #[structopt(short = "o", long = "out", parse(from_os_str))] @@ -23,3 +19,14 @@ pub struct Options { #[structopt(short = "c")] pub clipboard: bool, } + +impl Region { + pub fn from_str(x: &str) -> Result { + match x { + "fullscreen" => Ok(Region::Fullscreen), + "window" => Ok(Region::ActiveWindow), + "select" | "selection" => Ok(Region::Selection), + _ => Err(ScreenshotError::Error), + } + } +} diff --git a/src/window.rs b/src/window.rs index 8093a1f..80beb08 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,4 +1,4 @@ -use xlib::{Display, Window, Rectangle}; +use xlib::{Display, Rectangle, Window}; use errors::ScreenshotError; diff --git a/xlib/src/display.rs b/src/xlib/display.rs similarity index 84% rename from xlib/src/display.rs rename to src/xlib/display.rs index f2790f4..ae78063 100644 --- a/xlib/src/display.rs +++ b/src/xlib/display.rs @@ -1,16 +1,21 @@ use std::ffi::CString; +use libc; use x11::xlib as x; -use Visual; -use Window; -use X11Error; +use xlib::{Event, Visual, Window, X11Error}; /// A connection to an X server. pub struct Display { inner: *mut x::Display, } +/// Something that's part of a display. +pub trait GetDisplay { + /// Get the current display + fn get_display(&self) -> *mut x::Display; +} + impl Display { /// Opens a new connection to an X server. /// @@ -25,6 +30,18 @@ impl Display { Ok(Display { inner }) } + /// Get the next event, blocks until an event is reached. + pub fn next_event(&self) -> Result { + let event = + unsafe { libc::malloc(::std::mem::size_of::()) as *mut x::XAnyEvent }; + Event::from_raw(event) + } + + /// Returns the number of events that are still pending + pub fn pending(&self) -> Result { + Ok(unsafe { x::XPending(self.inner) }) + } + /// Gets the raw X Display handle pub fn as_raw(&self) -> *mut x::Display { self.inner diff --git a/src/xlib/drawable.rs b/src/xlib/drawable.rs new file mode 100644 index 0000000..e768730 --- /dev/null +++ b/src/xlib/drawable.rs @@ -0,0 +1,26 @@ +use x11::xlib as x; + +use xlib::{GetDisplay, Image, Rectangle, X11Error}; + +/// Anything that's drawable +pub trait Drawable: GetDisplay { + /// Get drawable handle + fn as_drawable(&self) -> x::Drawable; + + /// Capture a snapshot of this drawable, clipped by rect. + fn get_image(&self, rect: Rectangle) -> Result { + let image = unsafe { + x::XGetImage( + self.get_display(), + self.as_drawable(), + rect.x as i32, + rect.y as i32, + rect.width, + rect.height, + 0xffffffff, + x::ZPixmap, + ) + }; + Ok(Image { inner: image }) + } +} diff --git a/xlib/src/errors.rs b/src/xlib/errors.rs similarity index 100% rename from xlib/src/errors.rs rename to src/xlib/errors.rs diff --git a/src/xlib/event.rs b/src/xlib/event.rs new file mode 100644 index 0000000..414a01e --- /dev/null +++ b/src/xlib/event.rs @@ -0,0 +1,36 @@ +use libc; +use x11::xlib as x; + +use xlib::X11Error; + +/// An x11 Event +pub struct Event { + inner: *mut x::XAnyEvent, + kind: EventKind, +} + +/// Type of event +pub enum EventKind { + /// None event + None, +} + +impl Event { + /// Returns the EventKind of this event + pub fn kind(&self) -> &EventKind { + &self.kind + } + + pub(super) fn from_raw(event: *mut x::XAnyEvent) -> Result { + Ok(Event { + inner: event, + kind: EventKind::None, + }) + } +} + +impl Drop for Event { + fn drop(&mut self) { + unsafe { libc::free(self.inner as *mut libc::c_void) }; + } +} diff --git a/src/xlib/gc.rs b/src/xlib/gc.rs new file mode 100644 index 0000000..5588ec5 --- /dev/null +++ b/src/xlib/gc.rs @@ -0,0 +1,2 @@ +/// Graphics context +pub struct GC {} diff --git a/xlib/src/image.rs b/src/xlib/image.rs similarity index 98% rename from xlib/src/image.rs rename to src/xlib/image.rs index ee34794..2f048a7 100644 --- a/xlib/src/image.rs +++ b/src/xlib/image.rs @@ -1,6 +1,6 @@ use x11::xlib as x; -use X11Error; +use xlib::X11Error; /// A handle to an XImage. pub struct Image { diff --git a/src/xlib/mod.rs b/src/xlib/mod.rs new file mode 100644 index 0000000..5cb5a7d --- /dev/null +++ b/src/xlib/mod.rs @@ -0,0 +1,25 @@ +//! Safe-ish bindings to parts of x11's xlib module. +//! +//! I need this for my project. + +#![deny(missing_docs)] + +mod display; +mod drawable; +mod errors; +mod event; +mod gc; +mod image; +mod rect; +mod visual; +mod window; + +pub use self::display::{Display, GetDisplay}; +pub use self::drawable::Drawable; +pub use self::errors::X11Error; +pub use self::event::{Event, EventKind}; +pub use self::gc::GC; +pub use self::image::{Image, ImageByteOrder, PixBuffer}; +pub use self::rect::Rectangle; +pub use self::visual::Visual; +pub use self::window::{Window, WindowAttributes}; diff --git a/xlib/src/rect.rs b/src/xlib/rect.rs similarity index 76% rename from xlib/src/rect.rs rename to src/xlib/rect.rs index 89429be..1dd4023 100644 --- a/xlib/src/rect.rs +++ b/src/xlib/rect.rs @@ -2,9 +2,9 @@ #[derive(Debug)] pub struct Rectangle { /// x - pub x: u32, + pub x: i32, /// y - pub y: u32, + pub y: i32, /// width pub width: u32, /// height @@ -13,7 +13,7 @@ pub struct Rectangle { impl Rectangle { /// Create a new Rectangle from u32s - pub fn new(x: u32, y: u32, width: u32, height: u32) -> Self { + pub fn new(x: i32, y: i32, width: u32, height: u32) -> Self { Rectangle { x, y, @@ -21,4 +21,4 @@ impl Rectangle { height, } } -} \ No newline at end of file +} diff --git a/xlib/src/visual.rs b/src/xlib/visual.rs similarity index 95% rename from xlib/src/visual.rs rename to src/xlib/visual.rs index 264d5e8..601dcdb 100644 --- a/xlib/src/visual.rs +++ b/src/xlib/visual.rs @@ -1,6 +1,6 @@ use x11::xlib as x; -use Display; +use xlib::Display; /// A wrapper around a Visual pub struct Visual { diff --git a/xlib/src/window.rs b/src/xlib/window.rs similarity index 83% rename from xlib/src/window.rs rename to src/xlib/window.rs index e71b680..97e53e3 100644 --- a/xlib/src/window.rs +++ b/src/xlib/window.rs @@ -1,13 +1,10 @@ use std::mem; -use imlib2::Drawable; +// use imlib2::Drawable; use libc; use x11::xlib as x; -use Display; -use Image; -use Rectangle; -use X11Error; +use xlib::{Display, Drawable, GetDisplay, Image, Rectangle, X11Error}; /// A wrapper around a window handle. #[derive(Copy, Clone, PartialEq, Eq)] @@ -67,36 +64,44 @@ impl Window { } } - /// Capture a snapshot of this window. - pub fn get_image(&self) -> Result { - let attr = self.get_attributes()?; - let image = unsafe { - x::XGetImage( - self.display, - self.inner, - attr.get_x(), - attr.get_y(), - attr.get_width(), - attr.get_height(), - 0xffffffff, - x::ZPixmap, - ) - }; - Ok(Image { inner: image }) - } - /// Get the raw window handle pub fn as_raw(&self) -> x::Window { self.inner } -} -impl AsRef for Window { - fn as_ref(&self) -> &Drawable { - &self.inner + /// Get image + pub fn get_image(&self) -> Result { + let attr = self.get_attributes()?; + Drawable::get_image( + self, + Rectangle::new( + attr.get_x(), + attr.get_y(), + attr.get_width(), + attr.get_height(), + ), + ) } } +impl GetDisplay for Window { + fn get_display(&self) -> *mut x::Display { + self.display + } +} + +impl Drawable for Window { + fn as_drawable(&self) -> x::Drawable { + self.inner + } +} + +// impl AsRef for Window { +// fn as_ref(&self) -> &Drawable { +// &self.inner +// } +// } + impl WindowAttributes { /// Gets the width of the window pub fn get_x(&self) -> i32 { diff --git a/xlib/Cargo.toml b/xlib/Cargo.toml deleted file mode 100644 index 393a2d7..0000000 --- a/xlib/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "xlib" -description = "Safe-ish bindings to xlib (or at least the parts that I need)" -version = "0.1.0" -authors = ["Michael Zhang "] - -[dependencies] -failure = "0.1" -imlib2 = { path = "../imlib2" } -libc = "0.2" -x11 = { version = "2.18", features = ["xlib"] } diff --git a/xlib/src/lib.rs b/xlib/src/lib.rs deleted file mode 100644 index b6abef4..0000000 --- a/xlib/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Safe-ish bindings to parts of x11's xlib module. -//! -//! I need this for my project. - -#![deny(missing_docs)] - -#[macro_use] -extern crate failure; -extern crate imlib2; -extern crate libc; -extern crate x11; - -mod display; -mod errors; -mod image; -mod visual; -mod rect; -mod window; - -pub use display::Display; -pub use errors::X11Error; -pub use image::{Image, ImageByteOrder, PixBuffer}; -pub use visual::Visual; -pub use rect::Rectangle; -pub use window::{Window, WindowAttributes}; -