push some restructure

This commit is contained in:
Michael Zhang 2020-06-26 13:51:31 -05:00
parent 15220bed4b
commit 77393ca68c
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
42 changed files with 870 additions and 1803 deletions

9
.editorconfig Normal file
View file

@ -0,0 +1,9 @@
root = true
[*]
end_of_file = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
indent_style = space
indent_size = 4

View file

@ -1,44 +0,0 @@
language: rust
sudo: false
cache: cargo
dist: trusty
addons:
apt:
packages:
- libx11-dev
- libimlib2-dev
- build-essential
- mesa-common-dev
env:
global:
- PROJECT_NAME=leanshot
matrix:
include:
- os: linux
rust: stable
env: TARGET=x86_64-unknown-linux-gnu
fast_finish: true
before_script: rustup target add $TARGET || echo ok
script:
- if [ -z "$TRAVIS_TAG" ]; then cargo check --all --target $TARGET; fi
before_deploy: ./ci/package $TRAVIS_TAG $TARGET
deploy:
- provider: releases
api_key:
secure: ZCA3+LjTzliSC3m9DjcEYZOsRhaI5r9LAHfuw5HuaLYbprbEVwqLCTV1ioe7F9dLPembqGoGEsVnWAsrAUP0C9BRUuOrZMYvTOS3qTwSX7pPc/fsNUzP9uc6XPIMQ7/QeakmpgVYvObt9/1Wxui715nPmj9h+YIB4P3BUT8VsufzG0LogNSTwkhicM19O9oFhoCg39qQBueBe2SrS35dqvofSoJlEk2wSBBesQ9xuw9goP8ZkTKD3w/ZmPUS9RuWi1FSzsUcnLGCEMwYxLk9CBnG9I0G8kvDF194UYq7YnlaK4sn4AG5UxkMYcWN573vb+KX56VoHa57Z7qNIoUy1Kk0MatIjPIxd2LQJSTl7I5sckfoq61wihRYYj8R1qI4V4mqBX+MHFwyfs7/XT2pVfPHeflvJ4MMZW5t+OYWv8LfwFgufbLyYS79ZkBIjVnk2ligSv8kSPcuTtnSzp6UJlHsEbRDlWcciAYA92d2bggz7BrSG+X1eDAVto+bGdAeAp5zgRPVKwbpA9aD52y2GmlMh4Z+GcjhF6U7z+2AYRalH9X8d4mSdQ7KuhFfigoR/84tO+kC5QeKnf3FfgXPzOimt8xCsd2MFDrg2sPvgFkQGyM0ihB9NnNBfiFrQrRP3+DyTCI3TqOfkz8QKe4h7DxefR1t+YvheNZjGtC8C+E=
file_glob: true
file:
- leanshot-*
on:
condition: $TRAVIS_RUST_VERSION = stable
tags: true
skip_cleanup: true
notifications:
email: false

1102
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,19 +4,24 @@ description = "Screenshot capture for Linux."
version = "0.4.0"
repository = "https://github.com/iptq/leanshot"
license-file = "LICENSE"
edition = "2018"
authors = ["Michael Zhang <failed.down@gmail.com>"]
[workspace]
members = [".", "imlib2", "imlib2/imlib2-sys", "xlib"]
members = [".", "x11"]
[features]
default = ["x11"]
x11 = ["leanshot_x11"]
[dependencies]
failure = "0.1"
gl = "0.11"
glutin = "0.19"
imlib2 = { version = "0.1", path = "./imlib2" }
leanshot_xlib = { version = "0.2", path = "./xlib" }
nanovg = { version = "1.0.2", features = ["gl3"] }
# gl = "0.11"
# glutin = "0.19"
# nanovg = { version = "1.0.2", features = ["gl3"] }
png = "0.14"
structopt = "0.2"
time = "0.1"
x11 = { version = "2.18", features = ["xlib"] }
# time = "0.1"
anyhow = "1.0.31"
image = "0.23.5"
leanshot_x11 = { version = "0.2", path = "x11", optional = true, features = ["xlib"] }

View file

@ -1,4 +1,4 @@
Copyright 2018 Michael Zhang
Copyright 2018-2020 Michael Zhang
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View file

@ -1,11 +0,0 @@
[package]
name = "imlib2"
version = "0.1.1"
description = "imlib2 bindings"
license-file = "../LICENSE"
authors = ["Michael Zhang <failed.down@gmail.com>"]
[dependencies]
failure = "0.1"
imlib2-sys = { version = "0.1", path = "imlib2-sys" }
leanshot_xlib = { version = "0.2", path = "../xlib" }

View file

@ -1,13 +0,0 @@
[package]
name = "imlib2-sys"
version = "0.1.0"
description = "ffi for imlib2"
license-file = "../../LICENSE"
authors = ["Michael Zhang <failed.down@gmail.com>"]
build = "build.rs"
[lib]
path = "lib.rs"
[build-dependencies]
bindgen = "0.47"

View file

@ -1,20 +0,0 @@
// bindgen build script by remexre
extern crate bindgen;
use std::env;
use std::path::PathBuf;
use bindgen::Builder;
fn main() {
println!("cargo:rustc-link-lib=Imlib2");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
Builder::default()
.header("wrapper.h")
.blacklist_type("max_align_t")
.generate()
.expect("Unable to generate bindings")
.write_to_file(out_path.join("bindings.rs"))
.expect("Unable to write bindings");
}

View file

@ -1,5 +0,0 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

View file

@ -1 +0,0 @@
#include "Imlib2.h"

View file

@ -1,7 +0,0 @@
/// Enumerated error type.
#[allow(missing_docs)]
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "error")]
Error,
}

View file

@ -1,77 +0,0 @@
use std::ffi::CString;
use std::path::Path;
use imlib2_sys as im;
use xlib::Drawable;
use Error;
/// A simple wrapper around Imlib_Image
pub struct Image {
pub(crate) inner: im::Imlib_Image,
}
impl Image {
/// Creates an Image from a pixmap.
pub fn create_from_drawable(
drawable: impl Drawable,
pixmap: im::Pixmap,
x: i32,
y: i32,
width: i32,
height: i32,
grab_x: bool,
) -> Result<Self, Error> {
unsafe { im::imlib_context_set_drawable(drawable.as_drawable()) };
let image = unsafe {
im::imlib_create_image_from_drawable(
pixmap,
x,
y,
width,
height,
if grab_x { 1 } else { 0 },
) as im::Imlib_Image
};
unsafe { im::imlib_context_set_image(image) };
Ok(Image { inner: image })
}
/// Get width
pub fn get_width(&self) -> i32 {
unsafe {
im::imlib_context_set_image(self.inner);
im::imlib_image_get_width()
}
}
/// Get height
pub fn get_height(&self) -> i32 {
unsafe {
im::imlib_context_set_image(self.inner);
im::imlib_image_get_height()
}
}
/// Save this image
pub fn save_image(&self, file: impl AsRef<Path>) -> Result<(), Error> {
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(())
}
/// Get raw image
pub fn as_raw(&self) -> im::Imlib_Image {
self.inner
}
}
impl Drop for Image {
fn drop(&mut self) {
unsafe {
im::imlib_context_set_image(self.inner);
im::imlib_free_image();
}
}
}

View file

@ -1,70 +0,0 @@
//! Safe-ish bindings to imlib2 (at least the only parts I need).
#[macro_use]
extern crate failure;
extern crate imlib2_sys;
extern crate leanshot_xlib as xlib;
mod errors;
mod image;
pub use errors::Error;
pub use 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) };
}
/// Set the visual for the imlib context.
pub fn context_set_image(image: &Image) {
unsafe { imlib2_sys::imlib_context_set_image(image.as_raw() as imlib2_sys::Imlib_Image) };
}
/// Get a pointer to the raw image data for the current image.
pub fn image_get_data() -> *mut u32 {
unsafe { imlib2_sys::imlib_image_get_data_for_reading_only() }
}
/// Create cropped image
pub fn create_cropped_image(x: i32, y: i32, width: u32, height: u32) -> Result<Image, Error> {
let inner =
unsafe { imlib2_sys::imlib_create_cropped_image(x, y, width as i32, height as i32) };
if inner.is_null() {
return Err(Error::Error);
}
Ok(Image { inner })
}
/// Create scaled cropped image
pub fn create_scaled_cropped_image(
x: i32,
y: i32,
width: u32,
height: u32,
) -> Result<Image, Error> {
let inner = unsafe {
imlib2_sys::imlib_create_cropped_scaled_image(
x,
y,
width as i32,
height as i32,
width as i32,
height as i32,
)
};
if inner.is_null() {
return Err(Error::Error);
}
Ok(Image { inner })
}

View file

@ -1,22 +0,0 @@
use crate::errors::ScreenshotError;
use crate::gui::GUI;
use crate::options::{Options, Region};
use imlib2;
/// The main capture routine.
pub fn capture(opt: &Options) -> Result<(), ScreenshotError> {
let gui = GUI::new()?;
let window_to_capture = match opt.region {
Region::ActiveWindow => gui.get_active_window()?,
_ => gui.display.get_default_root_window()?,
};
let capture = gui.capture_window(&opt, window_to_capture)?;
imlib2::context_set_image(&capture);
capture.save_image(&opt.outfile)?;
Ok(())
}

View file

@ -1,41 +0,0 @@
#[derive(Debug, Fail)]
pub enum ScreenshotError {
#[fail(display = "x11 error")]
X11Error(#[cause] crate::xlib::X11Error),
#[fail(display = "imlib2 error")]
ImlibError(#[cause] ::imlib2::Error),
#[fail(display = "io error")]
IOError(#[cause] ::std::io::Error),
#[fail(display = "png encoding error")]
PngEncodingError(#[cause] ::png::EncodingError),
#[fail(display = "error")]
Error,
}
impl From<::std::io::Error> for ScreenshotError {
fn from(err: ::std::io::Error) -> Self {
ScreenshotError::IOError(err)
}
}
impl From<crate::xlib::X11Error> for ScreenshotError {
fn from(err: crate::xlib::X11Error) -> Self {
ScreenshotError::X11Error(err)
}
}
impl From<::imlib2::Error> for ScreenshotError {
fn from(err: ::imlib2::Error) -> Self {
ScreenshotError::ImlibError(err)
}
}
impl From<::png::EncodingError> for ScreenshotError {
fn from(err: ::png::EncodingError) -> Self {
ScreenshotError::PngEncodingError(err)
}
}

View file

@ -1,346 +1,67 @@
use crate::xlib::{Display, Visual, Window};
use imlib2::{self, Image as Image2};
use anyhow::Result;
use image::RgbaImage;
use x11::{
xlib::{Display, Window},
Rectangle,
};
use crate::errors::ScreenshotError;
use crate::Options;
use crate::Rectangle;
use crate::Region;
use crate::platform::{x11::X11, Platform};
use crate::{Options, Region};
pub struct GUI {
pub(crate) display: Display,
pub struct GUI<P: Platform> {
inner: P,
}
impl GUI {
pub fn new() -> Result<Self, ScreenshotError> {
let display = Display::connect(":0")?;
Ok(GUI { display })
impl GUI<X11> {
pub fn new() -> Result<Self> {
let x11 = X11::new()?;
Ok(GUI { inner: x11 })
}
}
impl<P: Platform> GUI<P> {
/// Captures the window and produces an Image.
pub fn capture_window(&self, opt: &Options, window: Window) -> Result<Image2, ScreenshotError> {
pub fn capture_window(&self, opt: &Options, window: Window) -> Result<RgbaImage> {
let attr = window.get_attributes()?;
let width = attr.get_width();
let height = attr.get_height();
let root = attr.get_root();
let (x, y, _) = self.display.translate_coordinates(window, 0, 0, root)?;
imlib2::context_set_display(&self.display);
let visual = Visual::default(&self.display, 0);
imlib2::context_set_visual(&visual);
// let (x, y, _) = self.display.translate_coordinates(window, 0, 0, root)?;
// imlib2::context_set_display(&self.display);
// let visual = Visual::default(&self.display, 0);
// imlib2::context_set_visual(&visual);
if let Region::Selection = opt.region {
let capture =
Image2::create_from_drawable(window, 0, x, y, width as i32, height as i32, true)?;
let region = self.interactive_select(&capture)?;
imlib2::context_set_image(&capture);
return imlib2::create_scaled_cropped_image(
region.x,
region.y,
region.width,
region.height,
)
.map_err(|err| err.into());
let image = self.inner.capture_full_screen()?;
let rect = self.inner.show_interactive_selection(image)?;
unimplemented!("rect: {:?}", rect);
// let capture =
// Image2::create_from_drawable(window, 0, x, y, width as i32, height as i32, true)?;
// let region = self.interactive_select(&capture)?;
// imlib2::context_set_image(&capture);
// return imlib2::create_scaled_cropped_image(
// region.x,
// region.y,
// region.width,
// region.height,
// )
// .map_err(|err| err.into());
}
Image2::create_from_drawable(window, 0, x, y, width as i32, height as i32, true)
.map_err(|err| err.into())
unimplemented!()
// Image2::create_from_drawable(window, 0, x, y, width as i32, height as i32, true)
// .map_err(|err| err.into())
}
/// Get the active window.
pub fn get_active_window(&self) -> Result<Window, ScreenshotError> {
self.display
.get_input_focus()
.map(|(window, _)| window)
.map_err(|err| err.into())
pub fn get_active_window(&self) -> Result<P::Window> {
self.inner.get_active_window()
}
/// Brings up an interactive selection GUI.
pub fn interactive_select(&self, capture: &Image2) -> Result<Rectangle, ScreenshotError> {
// let window = SelectWindow::new(&self.display);
// let root = self.display.get_default_root_window()?;
// let root_im = root.get_image();
// let mut done = 0;
// let mut button_press = false;
// while done == 0 && self.display.pending()? > 0 {
// let ev = self.display.next_event()?;
// match ev.kind() {
// EventKind::ButtonPress => {
// button_press = true;
// }
// EventKind::ButtonRelease => {
// if button_press {
// done = 1;
// }
// button_press = false;
// }
// _ => (),
// }
// }
use glutin::{
dpi::{PhysicalPosition, PhysicalSize},
os::unix::{WindowBuilderExt, WindowExt, XWindowType},
ElementState, Event, EventsLoop, GlContext, GlWindow, KeyboardInput, MouseButton,
MouseCursor, VirtualKeyCode, WindowBuilder, WindowEvent,
};
use nanovg::{Image, ImagePattern, PathOptions, StrokeOptions};
use std::{f32::consts, mem, slice};
// let attr = window.get_attributes()?;
// let width = attr.get_width();
// let height = attr.get_height();
// let root = attr.get_root();
// let (x, y, _) = self.display.translate_coordinates(window, 0, 0, root)?;
let width = capture.get_width();
let height = capture.get_height();
let mut evl = EventsLoop::new();
let mon = evl.get_primary_monitor();
// TODO: handle error
let wb = WindowBuilder::new()
.with_x11_window_type(XWindowType::Splash)
.with_decorations(false)
.with_visibility(false)
.with_always_on_top(true)
.with_dimensions(
PhysicalSize::new(width.into(), height.into()).to_logical(mon.get_hidpi_factor()),
)
.with_fullscreen(Some(mon));
let ctx = glutin::ContextBuilder::new()
.with_vsync(false)
.with_multisampling(4)
.with_double_buffer(Some(true))
.with_srgb(true);
let win = GlWindow::new(wb, ctx, &evl).expect("couldn't make window");
win.set_position((0.0, 0.0).into());
let f = win.get_hidpi_factor() as f64;
// crosshair
win.set_cursor(MouseCursor::Crosshair);
// win.set_inner_size((width, height).into());
// println!("size={:?} pos={:?} outer={:?}", win.get_inner_size(), win.get_inner_position(), win.get_outer_size());
// println!("{:?}", win.get_hidpi_factor());
let x = Display::from_handle(win.get_xlib_display().unwrap() as u64);
let len;
let raw_data;
{
let _g = x.grab();
// let img = Image2::create_from_drawable(window, 0, 0, 0, width as i32, height as i32, true)?;
imlib2::context_set_image(&capture);
len = (width * height) as usize;
// println!("{}", window.as_raw());
raw_data = unsafe { slice::from_raw_parts(imlib2::image_get_data(), len) };
unsafe {
win.make_current().expect("couldn't make window");
gl::load_with(|sym| win.get_proc_address(sym) as *const _);
}
}
mem::forget(x);
// convert ARGB to RGBA
let mut data = vec![0u32; raw_data.len()];
data.copy_from_slice(raw_data);
for i in &mut data {
// fix the colors
*i = (*i & 0xff00ff00) | ((*i & 0xff) << 16) | ((*i >> 16) & 0xff);
}
// invert image
let mut inverted = vec![0u32; raw_data.len()];
inverted.copy_from_slice(raw_data);
for i in &mut inverted {
// fix the colors
*i = (*i & 0xff000000) | !(*i & 0xffffff);
}
let ctx = nanovg::ContextBuilder::new()
.build()
.expect("couldn't init nanovg");
let image = Image::new(&ctx)
.build_from_rgba(width as usize, height as usize, data.as_slice())
.expect("couldn't create image");
let inverted_image = Image::new(&ctx)
.build_from_rgba(width as usize, height as usize, inverted.as_slice())
.expect("couldn't create image");
let mut running = true;
let mut down = false;
// drag start
let mut dx = -1.0f64;
let mut dy = -1.0f64;
// curr mouse
let mut mx = -1.0f64;
let mut my = -1.0f64;
// rect
let mut rectw = 0.0f64;
let mut recth = 0.0f64;
let mut delayed_down = false;
let mut redraw = true;
win.show();
win.set_position(PhysicalPosition::new(0.0, 0.0).to_logical(f));
while running {
if redraw {
// let size = win.get_inner_size().unwrap();
// let (width, height) = (size.width as i32, size.height as i32);
unsafe {
gl::Viewport(0, 0, width as i32, height as i32);
gl::ClearColor(0.3, 0.3, 0.32, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
}
let (width, height) = (width as f64, height as f64);
ctx.frame((width as f32, height as f32), f as f32, |frame| {
let path_opts = PathOptions::default();
frame.path(
|path| {
path.rect((0.0, 0.0), ((width * f) as f32, (height * f) as f32));
// path.fill(Color::from_rgba(200, 200, 200, 255), Default::default());
path.fill(
ImagePattern {
image: &image,
origin: (0.0, 0.0),
size: (width as f32, height as f32),
angle: 0.0 / 180.0 * consts::PI,
alpha: 1.0,
},
Default::default(),
)
},
path_opts,
);
if down && rectw.abs() > 0.0 && recth.abs() > 0.0 {
frame.path(
|path| {
path.rect(
((dx * f) as f32, (dy * f) as f32),
((rectw * f) as f32, (recth * f) as f32),
);
path.stroke(
// Color::from_rgba(0, 0, 0, 255),
ImagePattern {
image: &inverted_image,
origin: (0.0, 0.0),
size: (width as f32, height as f32),
angle: 0.0 / 180.0 * consts::PI,
alpha: 1.0,
},
StrokeOptions {
width: 1.0,
..Default::default()
},
);
},
path_opts,
);
}
});
}
evl.poll_events(|event| {
if let Event::WindowEvent { event, .. } = event {
match event {
WindowEvent::Destroyed => running = false,
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode,
state,
..
},
..
} => {
if let (Some(VirtualKeyCode::Escape), ElementState::Released) =
(virtual_keycode, state)
{
if down {
down = false;
rectw = 0.0;
recth = 0.0;
} else {
unsafe {
x11::xlib::XDestroyWindow(
self.display.as_raw(),
win.get_xlib_window().unwrap(),
)
};
running = false;
}
}
}
WindowEvent::CursorMoved { position, .. } => {
mx = position.x;
my = position.y;
if down {
if delayed_down {
dx = mx;
dy = my;
delayed_down = false;
} else {
redraw = true;
}
rectw = mx - dx;
recth = my - dy;
}
}
WindowEvent::MouseInput { button, state, .. } => {
if let MouseButton::Left = button {
down = match state {
ElementState::Pressed => {
delayed_down = true;
if mx < 0.0 || my < 0.0 {
} else {
dx = mx;
dy = my;
}
true
}
ElementState::Released => {
if down && rectw.abs() > 0.0 && recth.abs() > 0.0 {
unsafe {
x11::xlib::XDestroyWindow(
self.display.as_raw(),
win.get_xlib_window().unwrap(),
)
};
running = false;
}
false
}
}
}
}
_ => (),
}
}
});
win.swap_buffers().expect("couldn't swap buffers");
}
if rectw.abs() > 0.0 && recth.abs() > 0.0 {
let mut x = dx;
let mut y = dy;
if rectw < 0.0 {
x += rectw;
}
if recth < 0.0 {
y += recth;
}
Ok(Rectangle::new(
(x * f) as i32,
(y * f) as i32,
(rectw.abs() * f) as u32,
(recth.abs() * f) as u32,
))
} else {
Err(ScreenshotError::Error)
}
/// Get the active window.
pub fn capture_full_screen(&self) -> Result<P::Image> {
self.inner.capture_full_screen()
}
}

42
src/lib.rs Normal file
View file

@ -0,0 +1,42 @@
//! Screenshot capturing utility.
#![deny(missing_docs)]
#[macro_use]
extern crate anyhow;
#[macro_use]
extern crate structopt;
extern crate leanshot_x11 as x11;
mod options;
mod platform;
use anyhow::Result;
pub use crate::options::{Options, Region};
use crate::platform::{x11::X11, Platform};
type Rectangle = x11::Rectangle;
/// The main capture routine
pub fn capture(opt: &Options) -> Result<()> {
let gui = X11::new()?;
let image = match opt.region {
Region::ActiveWindow => {
let window = gui.get_active_window()?;
let image = gui.capture_window(window);
}
Region::Fullscreen | Region::Selection => {
let mut image = gui.capture_full_screen()?;
if let Region::Fullscreen = opt.region {
let rect = gui.show_interactive_selection(image)?;
// TODO: crop the image
}
}
};
unimplemented!("leanshot::capture")
// imlib2::context_set_image(&capture);
// capture.save_image(&opt.outfile)?;
}

View file

@ -1,36 +1,9 @@
//! Screenshot capturing utility.
#![deny(missing_docs)]
#[macro_use]
extern crate failure;
extern crate gl;
extern crate glutin;
extern crate imlib2;
extern crate nanovg;
extern crate png;
#[macro_use]
extern crate structopt;
extern crate leanshot_xlib as xlib;
extern crate time;
extern crate x11;
mod capture;
mod errors;
mod gui;
mod options;
use crate::errors::ScreenshotError;
use crate::xlib::Rectangle;
use anyhow::Result;
use leanshot::{capture, Options};
use structopt::StructOpt;
pub use crate::capture::capture;
pub use crate::options::{Options, Region};
use failure::Error;
#[allow(missing_docs)]
pub fn main() -> Result<(), Error> {
pub fn main() -> Result<()> {
let opt = Options::from_args();
capture(&opt).map(|_| ()).map_err(|err| err.into())
capture(&opt)
}

279
src/old.rs Normal file
View file

@ -0,0 +1,279 @@
/// Brings up an interactive selection GUI.
pub fn interactive_select(&self, capture: &Image2) -> Result<Rectangle> {
// let window = SelectWindow::new(&self.display);
// let root = self.display.get_default_root_window()?;
// let root_im = root.get_image();
// let mut done = 0;
// let mut button_press = false;
// while done == 0 && self.display.pending()? > 0 {
// let ev = self.display.next_event()?;
// match ev.kind() {
// EventKind::ButtonPress => {
// button_press = true;
// }
// EventKind::ButtonRelease => {
// if button_press {
// done = 1;
// }
// button_press = false;
// }
// _ => (),
// }
// }
use glutin::{
dpi::{PhysicalPosition, PhysicalSize},
os::unix::{WindowBuilderExt, WindowExt, XWindowType},
ElementState, Event, EventsLoop, GlContext, GlWindow, KeyboardInput, MouseButton,
MouseCursor, VirtualKeyCode, WindowBuilder, WindowEvent,
};
use nanovg::{Image, ImagePattern, PathOptions, StrokeOptions};
use std::{f32::consts, mem, slice};
// let attr = window.get_attributes()?;
// let width = attr.get_width();
// let height = attr.get_height();
// let root = attr.get_root();
// let (x, y, _) = self.display.translate_coordinates(window, 0, 0, root)?;
let width = capture.get_width();
let height = capture.get_height();
let mut evl = EventsLoop::new();
let mon = evl.get_primary_monitor();
// TODO: handle error
let wb = WindowBuilder::new()
.with_x11_window_type(XWindowType::Splash)
.with_decorations(false)
.with_visibility(false)
.with_always_on_top(true)
.with_dimensions(
PhysicalSize::new(width.into(), height.into()).to_logical(mon.get_hidpi_factor()),
)
.with_fullscreen(Some(mon));
let ctx = glutin::ContextBuilder::new()
.with_vsync(false)
.with_multisampling(4)
.with_double_buffer(Some(true))
.with_srgb(true);
let win = GlWindow::new(wb, ctx, &evl).expect("couldn't make window");
win.set_position((0.0, 0.0).into());
let f = win.get_hidpi_factor() as f64;
// crosshair
win.set_cursor(MouseCursor::Crosshair);
// win.set_inner_size((width, height).into());
// println!("size={:?} pos={:?} outer={:?}", win.get_inner_size(), win.get_inner_position(), win.get_outer_size());
// println!("{:?}", win.get_hidpi_factor());
let x = Display::from_handle(win.get_xlib_display().unwrap() as u64);
let len;
let raw_data;
{
let _g = x.grab();
// let img = Image2::create_from_drawable(window, 0, 0, 0, width as i32, height as i32, true)?;
imlib2::context_set_image(&capture);
len = (width * height) as usize;
// println!("{}", window.as_raw());
raw_data = unsafe { slice::from_raw_parts(imlib2::image_get_data(), len) };
unsafe {
win.make_current().expect("couldn't make window");
gl::load_with(|sym| win.get_proc_address(sym) as *const _);
}
}
mem::forget(x);
// convert ARGB to RGBA
let mut data = vec![0u32; raw_data.len()];
data.copy_from_slice(raw_data);
for i in &mut data {
// fix the colors
*i = (*i & 0xff00ff00) | ((*i & 0xff) << 16) | ((*i >> 16) & 0xff);
}
// invert image
let mut inverted = vec![0u32; raw_data.len()];
inverted.copy_from_slice(raw_data);
for i in &mut inverted {
// fix the colors
*i = (*i & 0xff000000) | !(*i & 0xffffff);
}
let ctx = nanovg::ContextBuilder::new()
.build()
.expect("couldn't init nanovg");
let image = Image::new(&ctx)
.build_from_rgba(width as usize, height as usize, data.as_slice())
.expect("couldn't create image");
let inverted_image = Image::new(&ctx)
.build_from_rgba(width as usize, height as usize, inverted.as_slice())
.expect("couldn't create image");
let mut running = true;
let mut down = false;
// drag start
let mut dx = -1.0f64;
let mut dy = -1.0f64;
// curr mouse
let mut mx = -1.0f64;
let mut my = -1.0f64;
// rect
let mut rectw = 0.0f64;
let mut recth = 0.0f64;
let mut delayed_down = false;
let mut redraw = true;
win.show();
win.set_position(PhysicalPosition::new(0.0, 0.0).to_logical(f));
while running {
if redraw {
// let size = win.get_inner_size().unwrap();
// let (width, height) = (size.width as i32, size.height as i32);
unsafe {
gl::Viewport(0, 0, width as i32, height as i32);
gl::ClearColor(0.3, 0.3, 0.32, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
}
let (width, height) = (width as f64, height as f64);
ctx.frame((width as f32, height as f32), f as f32, |frame| {
let path_opts = PathOptions::default();
frame.path(
|path| {
path.rect((0.0, 0.0), ((width * f) as f32, (height * f) as f32));
// path.fill(Color::from_rgba(200, 200, 200, 255), Default::default());
path.fill(
ImagePattern {
image: &image,
origin: (0.0, 0.0),
size: (width as f32, height as f32),
angle: 0.0 / 180.0 * consts::PI,
alpha: 1.0,
},
Default::default(),
)
},
path_opts,
);
if down && rectw.abs() > 0.0 && recth.abs() > 0.0 {
frame.path(
|path| {
path.rect(
((dx * f) as f32, (dy * f) as f32),
((rectw * f) as f32, (recth * f) as f32),
);
path.stroke(
// Color::from_rgba(0, 0, 0, 255),
ImagePattern {
image: &inverted_image,
origin: (0.0, 0.0),
size: (width as f32, height as f32),
angle: 0.0 / 180.0 * consts::PI,
alpha: 1.0,
},
StrokeOptions {
width: 1.0,
..Default::default()
},
);
},
path_opts,
);
}
});
}
evl.poll_events(|event| {
if let Event::WindowEvent { event, .. } = event {
match event {
WindowEvent::Destroyed => running = false,
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode,
state,
..
},
..
} => {
if let (Some(VirtualKeyCode::Escape), ElementState::Released) =
(virtual_keycode, state)
{
if down {
down = false;
rectw = 0.0;
recth = 0.0;
} else {
running = false;
}
}
}
WindowEvent::CursorMoved { position, .. } => {
mx = position.x;
my = position.y;
if down {
if delayed_down {
dx = mx;
dy = my;
delayed_down = false;
} else {
redraw = true;
}
rectw = mx - dx;
recth = my - dy;
}
}
WindowEvent::MouseInput { button, state, .. } => {
if let MouseButton::Left = button {
down = match state {
ElementState::Pressed => {
delayed_down = true;
if mx < 0.0 || my < 0.0 {
} else {
dx = mx;
dy = my;
}
true
}
ElementState::Released => {
if down && rectw.abs() > 0.0 && recth.abs() > 0.0 {
running = false;
}
false
}
}
}
}
_ => (),
}
}
});
win.swap_buffers().expect("couldn't swap buffers");
}
if rectw.abs() > 0.0 && recth.abs() > 0.0 {
let mut x = dx;
let mut y = dy;
if rectw < 0.0 {
x += rectw;
}
if recth < 0.0 {
y += recth;
}
Ok(Rectangle::new(
(x * f) as i32,
(y * f) as i32,
(rectw.abs() * f) as u32,
(recth.abs() * f) as u32,
))
} else {
Err(ScreenshotError::Error)
}
}

View file

@ -1,6 +1,6 @@
use std::path::PathBuf;
use crate::ScreenshotError;
use anyhow::Result;
/// A region option
#[allow(missing_docs)]
@ -27,12 +27,12 @@ pub struct Options {
}
impl Region {
pub(self) fn from_str(x: &str) -> Result<Self, ScreenshotError> {
pub(self) fn from_str(x: &str) -> Result<Self> {
match x {
"fullscreen" => Ok(Region::Fullscreen),
"window" => Ok(Region::ActiveWindow),
"select" | "selection" => Ok(Region::Selection),
_ => Err(ScreenshotError::Error),
_ => bail!("please choose a region from 'fullscreen', 'window', or 'select'"),
}
}
}

21
src/platform/mod.rs Normal file
View file

@ -0,0 +1,21 @@
#[cfg(feature = "x11")]
pub mod x11;
use anyhow::Result;
use image::RgbaImage;
use crate::Rectangle;
pub trait Platform {
type Window;
type Image: Image;
fn get_active_window(&self) -> Result<Self::Window>;
fn capture_window(&self, window: Self::Window) -> Result<Self::Image>;
fn capture_full_screen(&self) -> Result<Self::Image>;
fn show_interactive_selection(&self, image: Self::Image) -> Result<Rectangle>;
}
pub trait Image {
fn into_rgba_image(self) -> RgbaImage;
}

55
src/platform/x11.rs Normal file
View file

@ -0,0 +1,55 @@
use anyhow::Result;
use image::RgbaImage;
use x11::xlib::{Display, Window};
use crate::platform::{Image as ImageT, Platform};
use crate::Rectangle;
pub struct X11 {
inner: Display,
}
impl X11 {
pub fn new() -> Result<Self> {
let display = Display::connect(":0")?;
Ok(X11 { inner: display })
}
}
impl Platform for X11 {
type Window = Window;
type Image = Image;
fn get_active_window(&self) -> Result<Self::Window> {
let (window, _) = self.inner.get_input_focus()?;
Ok(window)
}
fn capture_window(&self, window: Self::Window) -> Result<Self::Image> {
let image = window.get_image()?;
Ok(Image(image))
}
fn capture_full_screen(&self) -> Result<Self::Image> {
let window = self.inner.get_root_window(0);
unimplemented!()
}
fn show_interactive_selection(&self, image: Self::Image) -> Result<Rectangle> {
let window = Window::create(&self.inner, None, Rectangle::new(0, 0, 500, 500))?;
window.map();
let mut running = false;
while running {}
unimplemented!()
}
}
pub struct Image(x11::xlib::Image);
impl ImageT for Image {
fn into_rgba_image(self) -> RgbaImage {
todo!()
}
}

16
x11/Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "leanshot_x11"
version = "0.2.0"
description = "x11 bindings"
license-file = "../LICENSE"
authors = ["Michael Zhang <failed.down@gmail.com>"]
[features]
default = []
xlib = ["x11/xlib"]
xrender = ["x11/xrender"]
[dependencies]
libc = "0.2"
x11 = { version = "2.18" }
thiserror = "1.0.20"

27
x11/src/errors.rs Normal file
View file

@ -0,0 +1,27 @@
pub type Result<T, E = Error> = std::result::Result<T, E>;
/// Any error that can be raised when using this library.
#[allow(missing_docs)]
#[derive(Debug, Error)]
pub enum Error {
#[error("invalid image byte order")]
InvalidByteOrder(i32),
#[error("failed to get attributes")]
GetAttributesError,
#[error("failed to create cursor")]
CreateCursorError,
#[error("failed to open display")]
DisplayOpenError,
#[error("failed to get window")]
GetWindowError,
#[error("failed to translate coordinates")]
TranslateCoordinatesError,
#[error("error")]
Error,
}

18
x11/src/lib.rs Normal file
View file

@ -0,0 +1,18 @@
//! Higher level bindings to x11.
#[macro_use]
extern crate thiserror;
pub extern crate x11;
#[cfg(feature = "xlib")]
pub mod xlib;
#[cfg(feature = "xrender")]
pub mod xrender;
mod errors;
mod rect;
pub use crate::errors::{Error, Result};
pub use crate::rect::Rectangle;

View file

@ -1,38 +1,36 @@
use std::ffi::CString;
use x11::xlib as x;
use Display;
use X11Error;
use x11::xlib;
use crate::errors::Result;
use super::display::Display;
/// A unique string or intger
pub struct Atom {
inner: x::Atom,
inner: xlib::Atom,
}
impl Atom {
/// Create a new atom using a string
pub fn new(
display: &Display,
val: impl AsRef<str>,
only_if_exists: bool,
) -> Result<Self, X11Error> {
pub fn new(display: &Display, val: impl AsRef<str>, only_if_exists: bool) -> Result<Self> {
let val = {
let v = val.as_ref();
let s = CString::new(v).unwrap();
s.as_ptr()
};
let inner =
unsafe { x::XInternAtom(display.as_raw(), val, if only_if_exists { 1 } else { 0 }) };
unsafe { xlib::XInternAtom(display.as_raw(), val, if only_if_exists { 1 } else { 0 }) };
Ok(Atom { inner })
}
/// Create a new Atom object from an existing handle
pub fn from(handle: x::Atom) -> Self {
pub fn from(handle: xlib::Atom) -> Self {
Atom { inner: handle }
}
/// Get the handle
pub fn as_raw(&self) -> x::Atom {
pub fn as_raw(&self) -> xlib::Atom {
self.inner
}
}

13
x11/src/xlib/cursor.rs Normal file
View file

@ -0,0 +1,13 @@
use x11::xlib;
/// Mouse pointer
pub struct Cursor {
pub(crate) display: *mut xlib::Display,
pub(crate) inner: xlib::Cursor,
}
impl Drop for Cursor {
fn drop(&mut self) {
unsafe { xlib::XFreeCursor(self.display, self.inner) };
}
}

View file

@ -1,21 +1,25 @@
use std::ffi::CString;
use libc;
use x11::xlib as x;
use x11::xlib;
use {Cursor, Event, Visual, Window, X11Error};
use crate::errors::{Error, Result};
use super::cursor::Cursor;
use super::event::Event;
use super::visual::Visual;
use super::window::Window;
/// A connection to an X server.
pub struct Display {
inner: *mut x::Display,
inner: *mut xlib::Display,
}
pub struct Grab(pub(crate) *mut x::Display);
pub struct Grab(pub(crate) *mut xlib::Display);
/// Something that's part of a display.
pub trait GetDisplay {
/// Get the current display
fn get_display(&self) -> *mut x::Display;
fn get_display(&self) -> *mut xlib::Display;
}
impl Display {
@ -23,11 +27,11 @@ impl Display {
///
/// 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> {
pub fn connect(display_name: impl AsRef<str>) -> Result<Display> {
let display_name = CString::new(display_name.as_ref()).unwrap();
let inner = unsafe { x::XOpenDisplay(display_name.as_ptr()) };
let inner = unsafe { xlib::XOpenDisplay(display_name.as_ptr()) };
if inner.is_null() {
return Err(X11Error::DisplayOpenError);
return Err(Error::DisplayOpenError);
}
Ok(Display { inner })
}
@ -35,21 +39,21 @@ impl Display {
/// Create a Display for an existing connection
pub fn from_handle(handle: u64) -> Self {
Display {
inner: handle as *mut x::Display,
inner: handle as *mut xlib::Display,
}
}
/// Grab
pub fn grab(&self) -> Grab {
unsafe { x::XGrabServer(self.inner) };
unsafe { xlib::XGrabServer(self.inner) };
Grab(self.inner)
}
/// Wrapper around XCreateFontCursor
pub fn create_font_cursor(&self, shape: u32) -> Result<Cursor, X11Error> {
let cursor = unsafe { x::XCreateFontCursor(self.inner, shape) as x::Cursor };
pub fn create_font_cursor(&self, shape: u32) -> Result<Cursor> {
let cursor = unsafe { xlib::XCreateFontCursor(self.inner, shape) as xlib::Cursor };
if cursor == 0 {
return Err(X11Error::CreateCursorError);
return Err(Error::CreateCursorError);
}
Ok(Cursor {
display: self.inner,
@ -58,33 +62,34 @@ impl Display {
}
/// Get the next event, blocks until an event is reached.
pub fn next_event(&self) -> Result<Event, X11Error> {
let event =
unsafe { libc::malloc(::std::mem::size_of::<x::XAnyEvent>()) as *mut x::XAnyEvent };
pub fn next_event(&self) -> Result<Event> {
let event = unsafe {
libc::malloc(::std::mem::size_of::<xlib::XAnyEvent>()) as *mut xlib::XAnyEvent
};
Event::from_raw(event)
}
/// Returns the number of events that are still pending
pub fn pending(&self) -> Result<i32, X11Error> {
Ok(unsafe { x::XPending(self.inner) })
pub fn pending(&self) -> Result<i32> {
Ok(unsafe { xlib::XPending(self.inner) })
}
/// Gets the raw X Display handle
pub fn as_raw(&self) -> *mut x::Display {
pub fn as_raw(&self) -> *mut xlib::Display {
self.inner
}
/// Gets the default visual
pub fn default_visual(&self, screen: i32) -> Visual {
let visual = unsafe { x::XDefaultVisual(self.inner, screen) };
let visual = unsafe { xlib::XDefaultVisual(self.inner, screen) };
Visual { inner: visual }
}
/// Returns the root window for the given screen.
pub fn get_root_window(&self, screen: i32) -> Result<Window, X11Error> {
let inner = unsafe { x::XRootWindow(self.inner, screen) };
pub fn get_root_window(&self, screen: i32) -> Result<Window> {
let inner = unsafe { xlib::XRootWindow(self.inner, screen) };
if inner == 0 {
return Err(X11Error::GetWindowError);
return Err(Error::GetWindowError);
}
let window = Window {
display: self.inner,
@ -94,10 +99,10 @@ impl Display {
}
/// Returns the root window for the default screen.
pub fn get_default_root_window(&self) -> Result<Window, X11Error> {
let inner = unsafe { x::XDefaultRootWindow(self.inner) };
pub fn get_default_root_window(&self) -> Result<Window> {
let inner = unsafe { xlib::XDefaultRootWindow(self.inner) };
if inner == 0 {
return Err(X11Error::GetWindowError);
return Err(Error::GetWindowError);
}
let window = Window {
display: self.inner,
@ -115,12 +120,12 @@ impl Display {
x: i32,
y: i32,
w2: Window,
) -> Result<(i32, i32, Option<Window>), X11Error> {
) -> Result<(i32, i32, Option<Window>)> {
let mut rx = 0;
let mut ry = 0;
let mut child_return: x::Window = 0;
let mut child_return: xlib::Window = 0;
let status = unsafe {
x::XTranslateCoordinates(
xlib::XTranslateCoordinates(
self.inner,
w1.inner,
w2.inner,
@ -132,7 +137,7 @@ impl Display {
)
};
if status == 0 {
return Err(X11Error::TranslateCoordinatesError);
return Err(Error::TranslateCoordinatesError);
}
let child = match child_return {
0 => None,
@ -146,14 +151,14 @@ impl Display {
/// Sync
pub fn sync(&self, discard: bool) {
unsafe { x::XSync(self.inner, if discard { 1 } else { 0 }) };
unsafe { xlib::XSync(self.inner, if discard { 1 } else { 0 }) };
}
/// 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;
/// Returns the focus window and the current focus state.
pub fn get_input_focus(&self) -> Result<(Window, i32)> {
let mut focus_return: xlib::Window = 0;
let mut revert_to_return = 0;
unsafe { x::XGetInputFocus(self.inner, &mut focus_return, &mut revert_to_return) };
unsafe { xlib::XGetInputFocus(self.inner, &mut focus_return, &mut revert_to_return) };
let window = Window {
display: self.inner,
inner: focus_return,
@ -164,12 +169,12 @@ impl Display {
impl Drop for Display {
fn drop(&mut self) {
unsafe { x::XCloseDisplay(self.inner) };
unsafe { xlib::XCloseDisplay(self.inner) };
}
}
impl Drop for Grab {
fn drop(&mut self) {
unsafe { x::XUngrabServer(self.0) };
unsafe { xlib::XUngrabServer(self.0) };
}
}

View file

@ -1,16 +1,20 @@
use x11::xlib as x;
use x11::xlib;
use {GetDisplay, Image, Rectangle, X11Error};
use crate::errors::Result;
use crate::rect::Rectangle;
use super::display::GetDisplay;
use super::image::Image;
/// Anything that's drawable
pub trait Drawable: GetDisplay {
/// Get drawable handle
fn as_drawable(&self) -> x::Drawable;
fn as_drawable(&self) -> xlib::Drawable;
/// Capture a snapshot of this drawable, clipped by rect.
fn get_image(&self, rect: Rectangle) -> Result<Image, X11Error> {
fn get_image(&self, rect: Rectangle) -> Result<Image> {
let image = unsafe {
x::XGetImage(
xlib::XGetImage(
self.get_display(),
self.as_drawable(),
rect.x as i32,
@ -18,7 +22,7 @@ pub trait Drawable: GetDisplay {
rect.width,
rect.height,
0xffffffff,
x::ZPixmap,
xlib::ZPixmap,
)
};
Ok(Image { inner: image })

View file

@ -1,11 +1,10 @@
use libc;
use x11::xlib as x;
use x11::xlib;
use X11Error;
use crate::errors::Result;
/// An x11 Event
pub struct Event {
inner: *mut x::XAnyEvent,
inner: *mut xlib::XAnyEvent,
kind: EventKind,
}
@ -27,7 +26,7 @@ impl Event {
&self.kind
}
pub(super) fn from_raw(event: *mut x::XAnyEvent) -> Result<Self, X11Error> {
pub(super) fn from_raw(event: *mut xlib::XAnyEvent) -> Result<Self> {
Ok(Event {
inner: event,
kind: EventKind::None,

View file

@ -1,10 +1,10 @@
use x11::xlib as x;
use x11::xlib;
use X11Error;
use crate::errors::{Error, Result};
/// A handle to an XImage.
pub struct Image {
pub(super) inner: *mut x::XImage,
pub(super) inner: *mut xlib::XImage,
}
/// Image byte order
@ -45,12 +45,12 @@ impl Image {
}
/// Get byte order
pub fn get_byte_order(&self) -> Result<ImageByteOrder, X11Error> {
pub fn get_byte_order(&self) -> Result<ImageByteOrder> {
let byte_order = unsafe { (*self.inner).byte_order };
match byte_order {
x::LSBFirst => Ok(ImageByteOrder::LSBFirst),
x::MSBFirst => Ok(ImageByteOrder::MSBFirst),
_ => Err(X11Error::InvalidByteOrder),
xlib::LSBFirst => Ok(ImageByteOrder::LSBFirst),
xlib::MSBFirst => Ok(ImageByteOrder::MSBFirst),
order => Err(Error::InvalidByteOrder(order)),
}
}
@ -64,7 +64,7 @@ impl Image {
impl Drop for Image {
fn drop(&mut self) {
unsafe { x::XDestroyImage(self.inner) };
unsafe { xlib::XDestroyImage(self.inner) };
}
}

23
x11/src/xlib/mod.rs Normal file
View file

@ -0,0 +1,23 @@
mod atom;
mod cursor;
mod display;
mod drawable;
mod event;
mod image;
mod visual;
mod window;
pub use self::atom::Atom;
pub use self::cursor::Cursor;
pub use self::display::{Display, GetDisplay};
pub use self::drawable::Drawable;
pub use self::event::{Event, EventKind};
pub use self::image::Image;
pub use self::visual::Visual;
pub use self::window::Window;
pub const XC_crosshair: u32 = 34;
pub const XC_ll_angle: u32 = 76;
pub const XC_lr_angle: u32 = 78;
pub const XC_ul_angle: u32 = 144;
pub const XC_ur_angle: u32 = 148;

View file

@ -1,21 +1,21 @@
use x11::xlib as x;
use x11::xlib;
use Display;
use super::display::Display;
/// A wrapper around a Visual
pub struct Visual {
pub(super) inner: *mut x::Visual,
pub(super) inner: *mut xlib::Visual,
}
impl Visual {
/// Gets the raw handle to the x11 Visual
pub fn as_raw(&self) -> *mut x::Visual {
pub fn as_raw(&self) -> *mut xlib::Visual {
self.inner
}
/// Gets the default visual
pub fn default(display: &Display, screen: i32) -> Self {
let inner = unsafe { x::XDefaultVisual(display.as_raw(), screen) };
let inner = unsafe { xlib::XDefaultVisual(display.as_raw(), screen) };
Visual { inner }
}
}

View file

@ -1,22 +1,20 @@
use std::mem;
// use imlib2::Drawable;
use libc;
use x11::xlib as x;
use x11::xlib;
use {Atom, Display, Drawable, GetDisplay, Image, Rectangle, X11Error};
use crate::errors::{Error, Result};
use crate::rect::Rectangle;
use super::atom::Atom;
use super::display::{Display, GetDisplay};
use super::drawable::Drawable;
use super::image::Image;
/// A wrapper around a window handle.
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq)]
pub struct Window {
pub(super) display: *mut x::Display,
pub(super) inner: x::Window,
}
/// Window Attributes
pub struct WindowAttributes {
pub(super) display: *mut x::Display,
pub(self) inner: *mut x::XWindowAttributes,
pub(super) display: *mut xlib::Display,
pub(super) inner: xlib::Window,
}
impl Window {
@ -25,14 +23,14 @@ impl Window {
display: &Display,
parent: Option<Window>,
location: Rectangle,
) -> Result<Window, X11Error> {
) -> Result<Window> {
let parent = match parent {
Some(parent) => parent,
None => display.get_default_root_window()?,
};
let visual = display.default_visual(0);
let window = unsafe {
x::XCreateWindow(
xlib::XCreateWindow(
display.as_raw(),
parent.as_raw(),
location.x as i32,
@ -44,7 +42,7 @@ impl Window {
0,
visual.as_raw(),
0,
0 as *mut x::XSetWindowAttributes,
0 as *mut xlib::XSetWindowAttributes,
)
};
Ok(Window {
@ -54,7 +52,7 @@ impl Window {
}
/// Create a new Window instance from an existing ID
pub fn create_from_handle(display: &Display, id: u64) -> Result<Window, X11Error> {
pub fn create_from_handle(display: &Display, id: u64) -> Result<Window> {
Ok(Window {
display: display.as_raw(),
inner: id,
@ -62,13 +60,13 @@ impl Window {
}
/// Get window attributes.
pub fn get_attributes(&self) -> Result<WindowAttributes, X11Error> {
pub fn get_attributes(&self) -> Result<WindowAttributes> {
let attr = unsafe {
libc::malloc(mem::size_of::<x::XWindowAttributes>()) as *mut x::XWindowAttributes
libc::malloc(mem::size_of::<xlib::XWindowAttributes>()) as *mut xlib::XWindowAttributes
};
let result = unsafe { x::XGetWindowAttributes(self.display, self.inner, attr) };
let result = unsafe { xlib::XGetWindowAttributes(self.display, self.inner, attr) };
match result {
0 => Err(X11Error::GetAttributesError),
0 => Err(Error::GetAttributesError),
_ => Ok(WindowAttributes {
display: self.display,
inner: attr,
@ -77,12 +75,12 @@ impl Window {
}
/// Get the raw window handle
pub fn as_raw(&self) -> x::Window {
pub fn as_raw(&self) -> xlib::Window {
self.inner
}
/// Get image
pub fn get_image(&self) -> Result<Image, X11Error> {
/// Gets the image from this window using XGetImage.
pub fn get_image(&self) -> Result<Image> {
let attr = self.get_attributes()?;
Drawable::get_image(
self,
@ -101,32 +99,49 @@ impl Window {
use std::mem::transmute;
let v = val.as_raw();
unsafe {
x::XChangeProperty(
xlib::XChangeProperty(
self.display,
self.inner,
key.as_raw(),
x::XA_ATOM,
xlib::XA_ATOM,
32,
x::PropModeReplace,
xlib::PropModeReplace,
transmute(&v),
1,
);
}
}
/// Map to display
pub fn map(&self) {
unsafe { xlib::XMapWindow(self.display, self.inner) };
}
}
impl GetDisplay for Window {
fn get_display(&self) -> *mut x::Display {
fn get_display(&self) -> *mut xlib::Display {
self.display
}
}
impl Drawable for Window {
fn as_drawable(&self) -> x::Drawable {
fn as_drawable(&self) -> xlib::Drawable {
self.inner
}
}
impl Drop for Window {
fn drop(&mut self) {
unsafe { xlib::XDestroyWindow(self.display, self.inner) };
}
}
/// Window Attributes
pub struct WindowAttributes {
pub(super) display: *mut xlib::Display,
pub(self) inner: *mut xlib::XWindowAttributes,
}
// impl AsRef<Drawable> for Window {
// fn as_ref(&self) -> &Drawable {
// &self.inner

1
x11/src/xrender/mod.rs Normal file
View file

@ -0,0 +1 @@
mod picture;

View file

@ -0,0 +1,7 @@
pub struct Picture {}
impl Picture {}
impl Drop for Picture {
fn drop(&self) {}
}

View file

@ -1,11 +0,0 @@
[package]
name = "leanshot_xlib"
version = "0.2.0"
description = "xlib bindings"
license-file = "../LICENSE"
authors = ["Michael Zhang <failed.down@gmail.com>"]
[dependencies]
failure = "0.1"
libc = "0.2"
x11 = { version = "2.18", features = ["xlib"] }

View file

@ -1,13 +0,0 @@
use x11::xlib as x;
/// Mouse pointer
pub struct Cursor {
pub(crate) display: *mut x::Display,
pub(crate) inner: x::Cursor,
}
impl Drop for Cursor {
fn drop(&mut self) {
unsafe { x::XFreeCursor(self.display, self.inner) };
}
}

View file

@ -1,5 +0,0 @@
pub const XC_crosshair: u32 = 34;
pub const XC_ll_angle: u32 = 76;
pub const XC_lr_angle: u32 = 78;
pub const XC_ul_angle: u32 = 144;
pub const XC_ur_angle: u32 = 148;

View file

@ -1,25 +0,0 @@
/// Any error that can be raised when using this library.
#[allow(missing_docs)]
#[derive(Debug, Fail)]
pub enum X11Error {
#[fail(display = "failed to create cursor")]
CreateCursorError,
#[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,
}

View file

@ -1,35 +0,0 @@
//! Safe-ish bindings to parts of x11's xlib module.
//!
//! I need this for my project.
#[macro_use]
extern crate failure;
extern crate libc;
pub extern crate x11;
mod atom;
mod cursor;
mod display;
mod drawable;
mod errors;
mod event;
mod image;
mod rect;
mod visual;
mod window;
#[allow(non_upper_case_globals)]
#[allow(missing_docs)]
mod cursorfont;
pub use atom::Atom;
pub use cursor::Cursor;
pub use cursorfont::*;
pub use display::{Display, GetDisplay, Grab};
pub use drawable::Drawable;
pub use errors::X11Error;
pub use event::{Event, EventKind};
pub use image::{Image, ImageByteOrder, PixBuffer};
pub use rect::Rectangle;
pub use visual::Visual;
pub use window::{Window, WindowAttributes};