separate crate for safe bindings
This commit is contained in:
parent
d725f2cd16
commit
a9511b94de
16 changed files with 317 additions and 178 deletions
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
shiet.png
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
shiet.png
|
||||||
|
|
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -141,7 +141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.11"
|
version = "0.3.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -199,11 +199,10 @@ name = "screenshot"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"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)",
|
"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)",
|
"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)",
|
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"x11 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"xlib 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -340,7 +339,16 @@ version = "2.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
||||||
|
"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]
|
[metadata]
|
||||||
|
@ -362,7 +370,7 @@ dependencies = [
|
||||||
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
||||||
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"
|
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"
|
||||||
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe"
|
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe"
|
||||||
"checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f"
|
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
|
||||||
"checksum png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f54b9600d584d3b8a739e1662a595fab051329eff43f20e7d8cc22872962145b"
|
"checksum png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f54b9600d584d3b8a739e1662a595fab051329eff43f20e7d8cc22872962145b"
|
||||||
"checksum proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "ffe022fb8c8bd254524b0b3305906c1921fa37a84a644e29079a9e62200c3901"
|
"checksum proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "ffe022fb8c8bd254524b0b3305906c1921fa37a84a644e29079a9e62200c3901"
|
||||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||||
|
|
|
@ -4,13 +4,15 @@ description = "Screenshot capture utility."
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
authors = ["Michael Zhang <failed.down@gmail.com>"]
|
authors = ["Michael Zhang <failed.down@gmail.com>"]
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = [".", "xlib"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["dylib", "rlib"]
|
crate-type = ["dylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
png = "0.12"
|
png = "0.12"
|
||||||
libc = "0.2"
|
|
||||||
structopt = "0.2"
|
structopt = "0.2"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
x11 = { version = "2.18", features = ["xlib"] }
|
xlib = { path = "xlib" }
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
use errors::ScreenshotError;
|
use errors::ScreenshotError;
|
||||||
|
|
||||||
use gui::GUI;
|
use gui::GUI;
|
||||||
use image::Image;
|
|
||||||
use options::{Options, Region};
|
use options::{Options, Region};
|
||||||
|
use xlib::Image;
|
||||||
|
|
||||||
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.get_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 mut capture = gui.window_capture(window_to_capture)?;
|
let capture = gui.capture_window(window_to_capture)?;
|
||||||
println!("captured the window");
|
println!("captured the window");
|
||||||
|
|
||||||
// let final_capture = match opt.region {
|
// let final_capture = match opt.region {
|
||||||
|
@ -20,78 +20,10 @@ pub fn capture(opt: &Options) -> Result<Image, ScreenshotError> {
|
||||||
// Region::Selection => gui.interactive_select(&window_capture),
|
// Region::Selection => gui.interactive_select(&window_capture),
|
||||||
// };
|
// };
|
||||||
|
|
||||||
if let Region::Selection = opt.region {
|
// if let Region::Selection = opt.region {
|
||||||
let region = gui.interactive_select(&capture)?;
|
// let region = gui.interactive_select(&capture)?;
|
||||||
capture.apply_region(®ion);
|
// capture.apply_region(®ion);
|
||||||
};
|
// };
|
||||||
|
|
||||||
Ok(capture)
|
Ok(capture)
|
||||||
|
|
||||||
// let display;
|
|
||||||
// let screen;
|
|
||||||
// match (Display::get_default(), Screen::get_default()) {
|
|
||||||
// (Some(d), Some(s)) => {
|
|
||||||
// display = d;
|
|
||||||
// screen = s;
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// bail!("Failed to open screen and display.");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// display.sync();
|
|
||||||
|
|
||||||
// // first, choose the window
|
|
||||||
// let window_opt = match options.region {
|
|
||||||
// Region::Fullscreen | Region::Selection => screen.get_root_window(),
|
|
||||||
// Region::ActiveWindow => screen.get_active_window(),
|
|
||||||
// };
|
|
||||||
// let window: GdkWindow;
|
|
||||||
// match window_opt {
|
|
||||||
// Some(window_) => window = window_,
|
|
||||||
// None => bail!("Failed to locate root window."),
|
|
||||||
// }
|
|
||||||
// window.process_updates(true);
|
|
||||||
|
|
||||||
// // take a screenshot of it
|
|
||||||
// let width: i32 = window.get_width();
|
|
||||||
// let height: i32 = window.get_height();
|
|
||||||
// let pixbuf: Pixbuf = match window.get_pixbuf(0, 0, width, height) {
|
|
||||||
// Some(pixbuf) => Ok(pixbuf),
|
|
||||||
// None => Err(ScreenshotError::InvalidPixbuf),
|
|
||||||
// }?;
|
|
||||||
|
|
||||||
// // launch selection
|
|
||||||
// let pixbuf = match options.region {
|
|
||||||
// Region::Selection => select_area(pixbuf)?,
|
|
||||||
// _ => pixbuf,
|
|
||||||
// };
|
|
||||||
// let width = pixbuf.get_width();
|
|
||||||
// let height = pixbuf.get_height();
|
|
||||||
|
|
||||||
// // create and draw to the surface
|
|
||||||
// let surface = match ImageSurface::create(Format::Rgb24, width, height) {
|
|
||||||
// Ok(surface) => Ok(surface),
|
|
||||||
// Err(_status) => Err(ScreenshotError::SurfaceCreateFail),
|
|
||||||
// }?;
|
|
||||||
// let ctx = Context::new(&surface);
|
|
||||||
// ctx.set_source_pixbuf(&pixbuf, 0.0, 0.0);
|
|
||||||
// ctx.paint();
|
|
||||||
|
|
||||||
// // write surface to file
|
|
||||||
|
|
||||||
// let now = time::now();
|
|
||||||
// let path = time::strftime(&options.outfile.as_os_str().to_str().unwrap(), &now)?;
|
|
||||||
// let mut file = File::create(path)?;
|
|
||||||
// surface.write_to_png(&mut file)?;
|
|
||||||
|
|
||||||
// if options.clipboard {
|
|
||||||
// match Clipboard::get_default(&display) {
|
|
||||||
// Some(clipboard) => {
|
|
||||||
// clipboard.set_image(&pixbuf);
|
|
||||||
// clipboard.store();
|
|
||||||
// }
|
|
||||||
// None => eprintln!("Failed to copy to the clipboard."),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
pub enum ScreenshotError {
|
pub enum ScreenshotError {
|
||||||
#[fail(display = "x11 error: {}", message)]
|
#[fail(display = "x11 error")]
|
||||||
XError { message: String },
|
X11Error(#[cause] ::xlib::X11Error),
|
||||||
|
|
||||||
#[fail(display = "io error")]
|
#[fail(display = "io error")]
|
||||||
IOError(#[cause] ::std::io::Error),
|
IOError(#[cause] ::std::io::Error),
|
||||||
|
@ -19,6 +19,12 @@ impl From<::std::io::Error> for ScreenshotError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<::xlib::X11Error> for ScreenshotError {
|
||||||
|
fn from(err: ::xlib::X11Error) -> Self {
|
||||||
|
ScreenshotError::X11Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<::png::EncodingError> for ScreenshotError {
|
impl From<::png::EncodingError> for ScreenshotError {
|
||||||
fn from(err: ::png::EncodingError) -> Self {
|
fn from(err: ::png::EncodingError) -> Self {
|
||||||
ScreenshotError::PngEncodingError(err)
|
ScreenshotError::PngEncodingError(err)
|
||||||
|
|
75
src/gui.rs
75
src/gui.rs
|
@ -1,85 +1,36 @@
|
||||||
use std::ffi::CString;
|
use xlib::{Display, Image, Window};
|
||||||
|
|
||||||
use libc;
|
|
||||||
use x11::xlib::*;
|
|
||||||
|
|
||||||
use errors::ScreenshotError;
|
use errors::ScreenshotError;
|
||||||
use image::Image;
|
|
||||||
use Rectangle;
|
use Rectangle;
|
||||||
|
|
||||||
pub struct GUI {
|
pub struct GUI {
|
||||||
display: *mut Display,
|
pub(crate) display: Display,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GUI {
|
impl GUI {
|
||||||
pub fn new() -> Result<Self, ScreenshotError> {
|
pub fn new() -> Result<Self, ScreenshotError> {
|
||||||
let display_str = CString::new(":0").unwrap();
|
let display = Display::connect(":0")?;
|
||||||
let display = unsafe { XOpenDisplay(display_str.as_ptr()) };
|
|
||||||
if display.is_null() {
|
|
||||||
return Err(ScreenshotError::XError {
|
|
||||||
message: format!("failed to open display"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// unsafe { XGrabServer(display) };
|
|
||||||
Ok(GUI { display })
|
Ok(GUI { display })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_window_attributes(
|
|
||||||
&self,
|
|
||||||
window: Window,
|
|
||||||
) -> Result<*mut XWindowAttributes, ScreenshotError> {
|
|
||||||
let attr_size = ::std::mem::size_of::<XWindowAttributes>();
|
|
||||||
let attr = unsafe { libc::malloc(attr_size) as *mut XWindowAttributes };
|
|
||||||
let result = unsafe { XGetWindowAttributes(self.display, window, attr) };
|
|
||||||
if result == 0 {
|
|
||||||
return Err(ScreenshotError::XError {
|
|
||||||
message: format!("failed to get window attributes"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Captures the window and produces a DynamicImage.
|
/// Captures the window and produces a DynamicImage.
|
||||||
pub fn window_capture(&self, window: Window) -> Result<Image, ScreenshotError> {
|
pub fn capture_window(&self, window: Window) -> Result<Image, ScreenshotError> {
|
||||||
let attr = self.get_window_attributes(window)?;
|
window.get_image().map_err(|err| err.into())
|
||||||
println!("got window attributes");
|
|
||||||
let image = unsafe {
|
|
||||||
XGetImage(
|
|
||||||
self.display,
|
|
||||||
window,
|
|
||||||
(*attr).x,
|
|
||||||
(*attr).y,
|
|
||||||
(*attr).width as u32,
|
|
||||||
(*attr).height as u32,
|
|
||||||
0xffffffff,
|
|
||||||
ZPixmap,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
Ok(Image::from(self.display, image))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the full screen.
|
|
||||||
pub fn get_root_window(&self) -> Window {
|
|
||||||
unsafe { XRootWindow(self.display, 0) as Window }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the active window.
|
/// Get the active window.
|
||||||
pub fn get_active_window(&self) -> Window {
|
pub fn get_active_window(&self) -> Result<Window, ScreenshotError> {
|
||||||
let mut window: Window = self.get_root_window();
|
Ok(self.display.get_default_root_window()?)
|
||||||
let mut revert_to_return: i32 = RevertToParent;
|
// let mut window: Window = self.display.get_default_root_window();
|
||||||
unsafe { XGetInputFocus(self.display, &mut window, &mut revert_to_return) };
|
// let mut revert_to_return: i32 = RevertToParent;
|
||||||
unsafe { XMapRaised(self.display, window) };
|
// unsafe { XGetInputFocus(self.display, &mut window, &mut revert_to_return) };
|
||||||
window
|
// unsafe { XMapRaised(self.display, window) };
|
||||||
|
// window
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Brings up an interactive selection GUI.
|
/// Brings up an interactive selection GUI.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn interactive_select(&self, _capture: &Image) -> Result<Rectangle, ScreenshotError> {
|
pub fn interactive_select(&self, _capture: &Image) -> Result<Rectangle, ScreenshotError> {
|
||||||
Err(ScreenshotError::Error)
|
Err(ScreenshotError::Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for GUI {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// unsafe { XUngrabServer(self.display) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
47
src/image.rs
47
src/image.rs
|
@ -3,39 +3,31 @@ use std::io::BufWriter;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use png::{self, Encoder, HasParameters};
|
use png::{self, Encoder, HasParameters};
|
||||||
use x11::xlib::*;
|
use xlib::{Image, ImageByteOrder};
|
||||||
|
|
||||||
use errors::ScreenshotError;
|
use errors::ScreenshotError;
|
||||||
use Rectangle;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub trait ImageExt {
|
||||||
pub struct Image {
|
fn to_data_buf(&self) -> Result<Vec<u8>, ScreenshotError>;
|
||||||
display: *mut Display,
|
fn write_png(&self, out: impl AsRef<Path>) -> Result<(), ScreenshotError>;
|
||||||
inner: *mut XImage,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl ImageExt for Image {
|
||||||
pub fn from(display: *mut Display, inner: *mut XImage) -> Self {
|
|
||||||
Image { inner, display }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply_region(&mut self, _region: &Rectangle) {}
|
|
||||||
|
|
||||||
/// Converts the image buffer into RGB(A).
|
/// Converts the image buffer into RGB(A).
|
||||||
fn to_data_buf(&self) -> Vec<u8> {
|
fn to_data_buf(&self) -> Result<Vec<u8>, ScreenshotError> {
|
||||||
let im = unsafe { *self.inner };
|
let size = self.get_size();
|
||||||
let size = 4usize * im.width as usize * im.height as usize;
|
|
||||||
let mut buf = vec![1; size];
|
let mut buf = vec![1; size];
|
||||||
let sbuf = unsafe { ::std::slice::from_raw_parts(im.data, size) }; // source buffer
|
let sbuf = self.buffer(); // source buffer
|
||||||
let mut sx = 0usize; // source idx
|
let mut sx = 0usize; // source idx
|
||||||
let mut dx = 0usize; // dest idx
|
let mut dx = 0usize; // dest idx
|
||||||
if im.byte_order == LSBFirst {
|
if let ImageByteOrder::LSBFirst = self.get_byte_order()? {
|
||||||
|
// LSBFirst
|
||||||
while dx < size {
|
while dx < size {
|
||||||
buf[dx] = sbuf[sx + 2] as u8;
|
buf[dx] = sbuf.get_byte(sx + 2).unwrap() as u8;
|
||||||
buf[dx + 1] = sbuf[sx + 1] as u8;
|
buf[dx + 1] = sbuf.get_byte(sx + 1).unwrap() as u8;
|
||||||
buf[dx + 2] = sbuf[sx] as u8;
|
buf[dx + 2] = sbuf.get_byte(sx).unwrap() as u8;
|
||||||
buf[dx + 3] = if im.depth == 32 {
|
buf[dx + 3] = if self.get_depth() == 32 {
|
||||||
sbuf[sx + 3] as u8
|
sbuf.get_byte(sx + 3).unwrap() as u8
|
||||||
} else {
|
} else {
|
||||||
255u8
|
255u8
|
||||||
};
|
};
|
||||||
|
@ -43,19 +35,18 @@ impl Image {
|
||||||
dx += 4;
|
dx += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_png(&self, out: impl AsRef<Path>) -> Result<(), ScreenshotError> {
|
fn write_png(&self, out: impl AsRef<Path>) -> Result<(), ScreenshotError> {
|
||||||
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 (width, height) = unsafe { ((*self.inner).width as u32, (*self.inner).height as u32) };
|
let mut encoder = Encoder::new(out, self.get_width(), self.get_height());
|
||||||
let mut encoder = Encoder::new(out, width, 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()?;
|
||||||
writer.write_image_data(data.as_slice())?;
|
writer.write_image_data(data.as_slice())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
extern crate libc;
|
|
||||||
extern crate png;
|
extern crate png;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate structopt;
|
extern crate structopt;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate x11;
|
extern crate xlib;
|
||||||
|
|
||||||
mod capture;
|
mod capture;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
@ -16,8 +15,10 @@ mod options;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
pub use capture::capture;
|
pub use capture::capture;
|
||||||
|
pub use image::ImageExt;
|
||||||
pub use options::Options;
|
pub use options::Options;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Rectangle {
|
pub struct Rectangle {
|
||||||
pub x: u32,
|
pub x: u32,
|
||||||
pub y: u32,
|
pub y: u32,
|
||||||
|
|
|
@ -3,7 +3,7 @@ extern crate screenshot;
|
||||||
extern crate structopt;
|
extern crate structopt;
|
||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use screenshot::{capture, Options};
|
use screenshot::{capture, ImageExt, Options};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
pub fn main() -> Result<(), Error> {
|
pub fn main() -> Result<(), Error> {
|
||||||
|
|
10
xlib/Cargo.toml
Normal file
10
xlib/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "xlib"
|
||||||
|
description = "Safe-ish bindings to xlib (or at least the parts that I need)"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Michael Zhang <failed.down@gmail.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
failure = "0.1"
|
||||||
|
libc = "0.2"
|
||||||
|
x11 = { version = "2.18", features = ["xlib"] }
|
47
xlib/src/display.rs
Normal file
47
xlib/src/display.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
use x11::xlib as x;
|
||||||
|
|
||||||
|
use Window;
|
||||||
|
use X11Error;
|
||||||
|
|
||||||
|
/// A connection to an X server.
|
||||||
|
pub struct Display {
|
||||||
|
inner: *mut x::Display,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display {
|
||||||
|
/// Opens a new connection to an X server.
|
||||||
|
///
|
||||||
|
/// On POSIX-conformant systems, the display name or DISPLAY environment variable can be a string in the format:
|
||||||
|
/// hostname:number.screen_number
|
||||||
|
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()) };
|
||||||
|
Ok(Display { inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the root window for the given screen.
|
||||||
|
pub fn get_root_window(&self, screen: i32) -> Result<Window, X11Error> {
|
||||||
|
let window = Window {
|
||||||
|
display: self.inner,
|
||||||
|
inner: unsafe { x::XRootWindow(self.inner, screen) },
|
||||||
|
};
|
||||||
|
Ok(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the root window for the default screen.
|
||||||
|
pub fn get_default_root_window(&self) -> Result<Window, X11Error> {
|
||||||
|
let window = Window {
|
||||||
|
display: self.inner,
|
||||||
|
inner: unsafe { x::XDefaultRootWindow(self.inner) },
|
||||||
|
};
|
||||||
|
Ok(window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Display {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { x::XCloseDisplay(self.inner) };
|
||||||
|
}
|
||||||
|
}
|
13
xlib/src/error.rs
Normal file
13
xlib/src/error.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/// Any error that can be raised when using this library.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
pub enum X11Error {
|
||||||
|
#[fail(display = "failed to get attributes")]
|
||||||
|
GetAttributesError,
|
||||||
|
|
||||||
|
#[fail(display = "invalid byte order")]
|
||||||
|
InvalidByteOrder,
|
||||||
|
|
||||||
|
#[fail(display = "error")]
|
||||||
|
Error,
|
||||||
|
}
|
77
xlib/src/image.rs
Normal file
77
xlib/src/image.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
use x11::xlib as x;
|
||||||
|
|
||||||
|
use X11Error;
|
||||||
|
|
||||||
|
/// A handle to an XImage.
|
||||||
|
pub struct Image {
|
||||||
|
pub(super) inner: *mut x::XImage,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Image byte order
|
||||||
|
pub enum ImageByteOrder {
|
||||||
|
/// Least significant byte first
|
||||||
|
LSBFirst,
|
||||||
|
/// Most significant byte first
|
||||||
|
MSBFirst,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The buffer pointed to by an XImage.
|
||||||
|
pub struct PixBuffer {
|
||||||
|
pub(self) buf: *mut u8,
|
||||||
|
pub(self) size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Image {
|
||||||
|
/// Get the size (in bytes) of the data buffer.
|
||||||
|
pub fn get_size(&self) -> usize {
|
||||||
|
4 * self.get_width() as usize * self.get_height() as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the image width
|
||||||
|
pub fn get_width(&self) -> u32 {
|
||||||
|
unsafe { (*self.inner).width as u32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the image height
|
||||||
|
pub fn get_height(&self) -> u32 {
|
||||||
|
unsafe { (*self.inner).height as u32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the image depth
|
||||||
|
pub fn get_depth(&self) -> u32 {
|
||||||
|
unsafe { (*self.inner).depth as u32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get byte order
|
||||||
|
pub fn get_byte_order(&self) -> Result<ImageByteOrder, X11Error> {
|
||||||
|
let byte_order = unsafe { (*self.inner).byte_order };
|
||||||
|
match byte_order {
|
||||||
|
x::LSBFirst => Ok(ImageByteOrder::LSBFirst),
|
||||||
|
x::MSBFirst => Ok(ImageByteOrder::MSBFirst),
|
||||||
|
_ => Err(X11Error::InvalidByteOrder),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a PixBuffer
|
||||||
|
pub fn buffer(&self) -> PixBuffer {
|
||||||
|
let size = self.get_size();
|
||||||
|
let buf = unsafe { (*self.inner).data as *mut u8 };
|
||||||
|
PixBuffer { buf, size }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Image {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { x::XDestroyImage(self.inner) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PixBuffer {
|
||||||
|
/// Gets the byte at the index of the data buffer.
|
||||||
|
pub fn get_byte(&self, index: usize) -> Option<u8> {
|
||||||
|
if index > self.size {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(unsafe { *self.buf.offset(index as isize) as u8 })
|
||||||
|
}
|
||||||
|
}
|
20
xlib/src/lib.rs
Normal file
20
xlib/src/lib.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//! 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 libc;
|
||||||
|
extern crate x11;
|
||||||
|
|
||||||
|
mod display;
|
||||||
|
mod error;
|
||||||
|
mod image;
|
||||||
|
mod window;
|
||||||
|
|
||||||
|
pub use display::Display;
|
||||||
|
pub use error::X11Error;
|
||||||
|
pub use image::{Image, ImageByteOrder, PixBuffer};
|
||||||
|
pub use window::{Window, WindowAttributes};
|
79
xlib/src/window.rs
Normal file
79
xlib/src/window.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use libc;
|
||||||
|
use x11::xlib as x;
|
||||||
|
|
||||||
|
use Image;
|
||||||
|
use X11Error;
|
||||||
|
|
||||||
|
/// A wrapper around a window handle.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Window {
|
||||||
|
pub(super) display: *mut x::Display,
|
||||||
|
pub(super) inner: x::Window,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Window Attributes
|
||||||
|
pub struct WindowAttributes {
|
||||||
|
pub(self) inner: *mut x::XWindowAttributes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window {
|
||||||
|
/// Get window attributes.
|
||||||
|
pub fn get_attributes(&self) -> Result<WindowAttributes, X11Error> {
|
||||||
|
let attr = unsafe {
|
||||||
|
libc::malloc(mem::size_of::<x::XWindowAttributes>()) as *mut x::XWindowAttributes
|
||||||
|
};
|
||||||
|
let result = unsafe { x::XGetWindowAttributes(self.display, self.inner, attr) };
|
||||||
|
match result {
|
||||||
|
0 => Err(X11Error::GetAttributesError),
|
||||||
|
_ => Ok(WindowAttributes { inner: attr }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Capture a snapshot of this window.
|
||||||
|
pub fn get_image(&self) -> Result<Image, X11Error> {
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowAttributes {
|
||||||
|
/// Gets the width of the window
|
||||||
|
pub fn get_x(&self) -> i32 {
|
||||||
|
unsafe { (*self.inner).x as i32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the height of the window
|
||||||
|
pub fn get_y(&self) -> i32 {
|
||||||
|
unsafe { (*self.inner).y as i32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the width of the window
|
||||||
|
pub fn get_width(&self) -> u32 {
|
||||||
|
unsafe { (*self.inner).width as u32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the height of the window
|
||||||
|
pub fn get_height(&self) -> u32 {
|
||||||
|
unsafe { (*self.inner).height as u32 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WindowAttributes {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { libc::free(self.inner as *mut libc::c_void) };
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue