mirror of
https://github.com/lelgenio/wl-crosshair.git
synced 2024-12-25 12:15:36 -03:00
Add support for loading any image, add some default cursors
This commit is contained in:
parent
081f6bed69
commit
d642e72c48
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
.direnv/
|
.direnv/
|
||||||
|
cursors/*~
|
||||||
|
|
944
Cargo.lock
generated
944
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -10,3 +10,4 @@ wayland-protocols-wlr = { version = "0.1.0", features = ["client"] }
|
||||||
|
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
tempfile = "3.2"
|
tempfile = "3.2"
|
||||||
|
image = "0.25.1"
|
||||||
|
|
BIN
cursors/inverse-v.png
Normal file
BIN
cursors/inverse-v.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
BIN
cursors/test-colors.png
Normal file
BIN
cursors/test-colors.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
BIN
cursors/wojak-cursor.png
Normal file
BIN
cursors/wojak-cursor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 204 KiB |
|
@ -19,6 +19,13 @@
|
||||||
version = "0.1.0";
|
version = "0.1.0";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
cargoLock.lockFile = ./Cargo.lock;
|
cargoLock.lockFile = ./Cargo.lock;
|
||||||
|
nativeBuildInputs = with pkgs; [ makeWrapper ];
|
||||||
|
postInstall = ''
|
||||||
|
mkdir -p $out/share
|
||||||
|
cp -rv ${./cursors} $out/share/cursors
|
||||||
|
wrapProgram $out/bin/* \
|
||||||
|
--set WL_CROSSHAIR_IMAGE_PATH $out/share/cursors/inverse-v.png
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
104
src/main.rs
104
src/main.rs
|
@ -1,8 +1,10 @@
|
||||||
use std::{fs::File, io::Write, os::unix::prelude::AsRawFd};
|
use std::{fs::File, io::Write, os::unix::prelude::AsRawFd};
|
||||||
|
|
||||||
|
use image::{GenericImageView, Pixel};
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
protocol::{
|
protocol::{
|
||||||
wl_buffer, wl_compositor, wl_keyboard, wl_region::WlRegion, wl_registry, wl_seat, wl_shm, wl_shm_pool, wl_surface
|
wl_buffer, wl_compositor, wl_keyboard, wl_region::WlRegion, wl_registry, wl_seat, wl_shm,
|
||||||
|
wl_shm_pool, wl_surface,
|
||||||
},
|
},
|
||||||
Connection, Dispatch, Proxy, QueueHandle,
|
Connection, Dispatch, Proxy, QueueHandle,
|
||||||
};
|
};
|
||||||
|
@ -17,7 +19,9 @@ use wayland_protocols::xdg::shell::client::xdg_wm_base;
|
||||||
struct State {
|
struct State {
|
||||||
running: bool,
|
running: bool,
|
||||||
|
|
||||||
cursor_size: u32,
|
cursor_width: u32,
|
||||||
|
cursor_height: u32,
|
||||||
|
image_path: String,
|
||||||
|
|
||||||
compositor: Option<wl_compositor::WlCompositor>,
|
compositor: Option<wl_compositor::WlCompositor>,
|
||||||
base_surface: Option<wl_surface::WlSurface>,
|
base_surface: Option<wl_surface::WlSurface>,
|
||||||
|
@ -27,6 +31,30 @@ struct State {
|
||||||
wm_base: Option<xdg_wm_base::XdgWmBase>,
|
wm_base: Option<xdg_wm_base::XdgWmBase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_cursor_image_path() -> String {
|
||||||
|
if let Some(p) = std::env::args().skip(1).next() {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(p) = std::env::var("WL_CROSSHAIR_IMAGE_PATH") {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
[
|
||||||
|
std::option_env!("WL_CROSSHAIR_IMAGE_PATH").map(String::from),
|
||||||
|
Some("cursors/inverse-v.png".to_string()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.filter(|p|
|
||||||
|
std::fs::metadata(p)
|
||||||
|
.map(|m| m.is_file())
|
||||||
|
.unwrap_or(false)
|
||||||
|
)
|
||||||
|
.next()
|
||||||
|
.expect("Could not find a crosshair image, pass it as a cli argument or set WL_CROSSHAIR_IMAGE_PATH environment variable")
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let conn = Connection::connect_to_env().unwrap();
|
let conn = Connection::connect_to_env().unwrap();
|
||||||
|
|
||||||
|
@ -38,7 +66,9 @@ fn main() {
|
||||||
|
|
||||||
let mut state = State {
|
let mut state = State {
|
||||||
running: true,
|
running: true,
|
||||||
cursor_size: 10,
|
cursor_width: 10,
|
||||||
|
cursor_height: 10,
|
||||||
|
image_path: get_cursor_image_path(),
|
||||||
compositor: None,
|
compositor: None,
|
||||||
base_surface: None,
|
base_surface: None,
|
||||||
layer_shell: None,
|
layer_shell: None,
|
||||||
|
@ -91,10 +121,11 @@ impl Dispatch<wl_registry::WlRegistry, ()> for State {
|
||||||
} else if interface == wl_shm::WlShm::interface().name {
|
} else if interface == wl_shm::WlShm::interface().name {
|
||||||
let shm = registry.bind::<wl_shm::WlShm, _, _>(name, version, qh, ());
|
let shm = registry.bind::<wl_shm::WlShm, _, _>(name, version, qh, ());
|
||||||
|
|
||||||
let (init_w, init_h) = (state.cursor_size, state.cursor_size);
|
|
||||||
|
|
||||||
let mut file = tempfile::tempfile().unwrap();
|
let mut file = tempfile::tempfile().unwrap();
|
||||||
draw(&mut file, (init_w, init_h));
|
state.draw(&mut file);
|
||||||
|
|
||||||
|
let (init_w, init_h) = (state.cursor_width, state.cursor_height);
|
||||||
|
|
||||||
let pool = shm.create_pool(file.as_raw_fd(), (init_w * init_h * 4) as i32, qh, ());
|
let pool = shm.create_pool(file.as_raw_fd(), (init_w * init_h * 4) as i32, qh, ());
|
||||||
let buffer = pool.create_buffer(
|
let buffer = pool.create_buffer(
|
||||||
0,
|
0,
|
||||||
|
@ -126,38 +157,6 @@ impl Dispatch<WlRegion, ()> for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(tmp: &mut File, (buf_x, buf_y): (u32, u32)) {
|
|
||||||
let mut buf = std::io::BufWriter::new(tmp);
|
|
||||||
for y in 0..buf_y {
|
|
||||||
for x in 0..buf_x {
|
|
||||||
let ix = x as i32;
|
|
||||||
let iy = y as i32;
|
|
||||||
|
|
||||||
let dist = if x <= (buf_x / 2) {
|
|
||||||
ix + iy - (buf_y as i32)
|
|
||||||
} else {
|
|
||||||
iy - ix
|
|
||||||
};
|
|
||||||
|
|
||||||
let a: u32 = match dist.abs() {
|
|
||||||
0 => 0xFF,
|
|
||||||
1 => 0x88,
|
|
||||||
_ => 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
let c: u32 = match dist.abs() {
|
|
||||||
0 => 0xFF,
|
|
||||||
1 => 0x88,
|
|
||||||
_ => 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
let color = (a << 24) + (c << 16) + (c << 8) + c;
|
|
||||||
buf.write_all(&color.to_ne_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.flush().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn init_layer_surface(&mut self, qh: &QueueHandle<State>) {
|
fn init_layer_surface(&mut self, qh: &QueueHandle<State>) {
|
||||||
let layer = self.layer_shell.as_ref().unwrap().get_layer_surface(
|
let layer = self.layer_shell.as_ref().unwrap().get_layer_surface(
|
||||||
|
@ -171,19 +170,44 @@ impl State {
|
||||||
// Center the window
|
// Center the window
|
||||||
layer.set_anchor(Anchor::Top | Anchor::Right | Anchor::Bottom | Anchor::Left);
|
layer.set_anchor(Anchor::Top | Anchor::Right | Anchor::Bottom | Anchor::Left);
|
||||||
layer.set_keyboard_interactivity(zwlr_layer_surface_v1::KeyboardInteractivity::None);
|
layer.set_keyboard_interactivity(zwlr_layer_surface_v1::KeyboardInteractivity::None);
|
||||||
layer.set_size(self.cursor_size, self.cursor_size);
|
layer.set_size(self.cursor_width, self.cursor_height);
|
||||||
// A negative value means we will be centered on the screen
|
// A negative value means we will be centered on the screen
|
||||||
// independently of any other xdg_layer_shell
|
// independently of any other xdg_layer_shell
|
||||||
layer.set_exclusive_zone(-1);
|
layer.set_exclusive_zone(-1);
|
||||||
// Set empty input region to allow clicking through the window.
|
// Set empty input region to allow clicking through the window.
|
||||||
if let Some(compositor) = &self.compositor {
|
if let Some(compositor) = &self.compositor {
|
||||||
let region = compositor.create_region(qh, ());
|
let region = compositor.create_region(qh, ());
|
||||||
self.base_surface.as_ref().unwrap().set_input_region(Some(®ion));
|
self.base_surface
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.set_input_region(Some(®ion));
|
||||||
}
|
}
|
||||||
self.base_surface.as_ref().unwrap().commit();
|
self.base_surface.as_ref().unwrap().commit();
|
||||||
|
|
||||||
self.layer_surface = Some(layer);
|
self.layer_surface = Some(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self, tmp: &mut File) {
|
||||||
|
let mut buf = std::io::BufWriter::new(tmp);
|
||||||
|
|
||||||
|
let i = image::open(&self.image_path).unwrap();
|
||||||
|
|
||||||
|
self.cursor_width = i.width();
|
||||||
|
self.cursor_height = i.height();
|
||||||
|
|
||||||
|
for y in 0..self.cursor_height {
|
||||||
|
for x in 0..self.cursor_width {
|
||||||
|
let px = i.get_pixel(x, y).to_rgba();
|
||||||
|
|
||||||
|
let [r, g, b, a] = px.channels().try_into().unwrap();
|
||||||
|
|
||||||
|
let color = u32::from_be_bytes([a, r, g, b]);
|
||||||
|
|
||||||
|
buf.write_all(&color.to_le_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.flush().unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ()> for State {
|
impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ()> for State {
|
||||||
|
|
Loading…
Reference in a new issue