forked from michael/leanshot
push some restructure
This commit is contained in:
parent
15220bed4b
commit
77393ca68c
42 changed files with 870 additions and 1803 deletions
9
.editorconfig
Normal file
9
.editorconfig
Normal 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
|
44
.travis.yml
44
.travis.yml
|
@ -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
1102
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
23
Cargo.toml
23
Cargo.toml
|
@ -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"] }
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -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:
|
||||
|
||||
|
|
|
@ -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" }
|
|
@ -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"
|
|
@ -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");
|
||||
}
|
|
@ -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"));
|
|
@ -1 +0,0 @@
|
|||
#include "Imlib2.h"
|
|
@ -1,7 +0,0 @@
|
|||
/// Enumerated error type.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum Error {
|
||||
#[fail(display = "error")]
|
||||
Error,
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 })
|
||||
}
|
|
@ -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(())
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
367
src/gui.rs
367
src/gui.rs
|
@ -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
42
src/lib.rs
Normal 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)?;
|
||||
}
|
35
src/main.rs
35
src/main.rs
|
@ -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
279
src/old.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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
21
src/platform/mod.rs
Normal 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
55
src/platform/x11.rs
Normal 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
16
x11/Cargo.toml
Normal 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
27
x11/src/errors.rs
Normal 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
18
x11/src/lib.rs
Normal 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;
|
|
@ -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
13
x11/src/xlib/cursor.rs
Normal 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) };
|
||||
}
|
||||
}
|
|
@ -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) };
|
||||
}
|
||||
}
|
|
@ -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 })
|
|
@ -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,
|
|
@ -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
23
x11/src/xlib/mod.rs
Normal 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;
|
|
@ -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 }
|
||||
}
|
||||
}
|
|
@ -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
1
x11/src/xrender/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
mod picture;
|
7
x11/src/xrender/picture.rs
Normal file
7
x11/src/xrender/picture.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
pub struct Picture {}
|
||||
|
||||
impl Picture {}
|
||||
|
||||
impl Drop for Picture {
|
||||
fn drop(&self) {}
|
||||
}
|
|
@ -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"] }
|
|
@ -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) };
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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,
|
||||
}
|
|
@ -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};
|
Loading…
Reference in a new issue