commit 083c0ea5c4942ef5f850a5ee62bde0fbcffa95cf Author: bad Date: Tue May 17 12:36:03 2022 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0d907d9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,425 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "activate-linux-wayland" +version = "0.1.0" +dependencies = [ + "png", + "smithay-client-toolkit", + "tempfile", + "wayland-client", + "wayland-protocols", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "calloop" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82" +dependencies = [ + "log", + "nix", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "deflate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +dependencies = [ + "adler32", +] + +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +dependencies = [ + "adler", +] + +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "png" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide", +] + +[[package]] +name = "proc-macro2" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "smithay-client-toolkit" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3" +dependencies = [ + "bitflags", + "calloop", + "dlib", + "lazy_static", + "log", + "memmap2", + "nix", + "pkg-config", + "wayland-client", + "wayland-cursor", + "wayland-protocols", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + +[[package]] +name = "wayland-client" +version = "0.29.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91223460e73257f697d9e23d401279123d36039a3f7a449e983f123292d4458f" +dependencies = [ + "bitflags", + "downcast-rs", + "libc", + "nix", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.29.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" +dependencies = [ + "nix", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd" +dependencies = [ + "nix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741" +dependencies = [ + "bitflags", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a1ed3143f7a143187156a2ab52742e89dac33245ba505c17224df48939f9e0" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.29.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3634ab4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "activate-linux-wayland" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +png = "0.17.5" +smithay-client-toolkit = "0.15.4" +tempfile = "3.3.0" +wayland-client = "0.29.4" +wayland-protocols = { version = "0.29.4", features = ["client", "staging_protocols", "unstable_protocols"] } diff --git a/activate_linux.png b/activate_linux.png new file mode 100644 index 0000000..010a8c9 Binary files /dev/null and b/activate_linux.png differ diff --git a/activate_linux.svg b/activate_linux.svg new file mode 100644 index 0000000..96f8673 --- /dev/null +++ b/activate_linux.svg @@ -0,0 +1,57 @@ + + + + + + + + + + Activate Linux + + diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..8bf6180 --- /dev/null +++ b/shell.nix @@ -0,0 +1,12 @@ +{ pkgs ? import { } }: +with pkgs; +mkShell { + shellHook = ''export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${pkgs.lib.makeLibraryPath [ + wayland + ]}"''; + buildInputs = [ + pkgconfig + wayland + ]; +} + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..38c7862 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,222 @@ +use smithay_client_toolkit::{ + default_environment, + environment::SimpleGlobal, + new_default_environment, + output::{with_output_info, OutputInfo}, + reexports::{ + calloop, + client::protocol::{wl_output, wl_shm, wl_surface}, + client::{Attached, Main}, + protocols::wlr::unstable::layer_shell::v1::client::{ + zwlr_layer_shell_v1, zwlr_layer_surface_v1, + }, + }, + shm::AutoMemPool, + WaylandSource, +}; +use wayland_client::protocol::{wl_region::WlRegion, wl_compositor::WlCompositor}; +use wayland_protocols::wlr::unstable::layer_shell::v1::client::zwlr_layer_surface_v1::KeyboardInteractivity; + +use std::{cell::{Cell, RefCell}, io::Write}; +use std::rc::Rc; + +static ACTIVATE_LINUX_PNG: &[u8] = include_bytes!("../activate_linux.png"); + +fn load_activate_png() -> (png::OutputInfo, Vec) { + let decoder = png::Decoder::new(ACTIVATE_LINUX_PNG); + let mut reader = decoder.read_info().unwrap(); + // Allocate the output buffer. + let mut buf = vec![0; reader.output_buffer_size()]; + // Read the next frame. An APNG might contain multiple frames. + let info = reader.next_frame(&mut buf).unwrap(); + // Grab the bytes of the image. + let bytes = &buf[..info.buffer_size()]; + buf.truncate(info.buffer_size()); + + (info, buf) +} + +default_environment!(Env, + fields = [ + layer_shell: SimpleGlobal, + ], + singles = [ + zwlr_layer_shell_v1::ZwlrLayerShellV1 => layer_shell + ], +); + +#[derive(PartialEq, Copy, Clone)] +enum RenderEvent { + Configure { width: u32, height: u32 }, + Closed, +} + +struct Surface { + surface: wl_surface::WlSurface, + layer_surface: Main, + next_render_event: Rc>>, + pool: AutoMemPool, + dimensions: (u32, u32), +} + +impl Surface { + fn new( + output: &wl_output::WlOutput, + surface: wl_surface::WlSurface, + layer_shell: &Attached, + pool: AutoMemPool, + compositor: &Attached, + ) -> Self { + let (info, png) = load_activate_png(); + let layer_surface = layer_shell.get_layer_surface( + &surface, + Some(output), + zwlr_layer_shell_v1::Layer::Overlay, + "example".to_owned(), + ); + layer_surface.set_keyboard_interactivity(KeyboardInteractivity::None); + + layer_surface.set_size(info.width, info.height); + // Anchor to the top left corner of the output + layer_surface + .set_anchor(zwlr_layer_surface_v1::Anchor::Bottom | zwlr_layer_surface_v1::Anchor::Right); + + let next_render_event = Rc::new(Cell::new(None::)); + let next_render_event_handle = Rc::clone(&next_render_event); + layer_surface.quick_assign(move |layer_surface, event, _| { + match (event, next_render_event_handle.get()) { + (zwlr_layer_surface_v1::Event::Closed, _) => { + next_render_event_handle.set(Some(RenderEvent::Closed)); + } + (zwlr_layer_surface_v1::Event::Configure { serial, width, height }, next) + if next != Some(RenderEvent::Closed) => + { + layer_surface.ack_configure(serial); + next_render_event_handle.set(Some(RenderEvent::Configure { width, height })); + } + (_, _) => {} + } + }); + + let region = compositor.create_region(); + surface.set_input_region(Some(®ion)); + // Commit so that the server will send a configure event + surface.commit(); + + Self { surface, layer_surface, next_render_event, pool, dimensions: (0, 0) } + } + + /// Handles any events that have occurred since the last call, redrawing if needed. + /// Returns true if the surface should be dropped. + fn handle_events(&mut self) -> bool { + match self.next_render_event.take() { + Some(RenderEvent::Closed) => true, + Some(RenderEvent::Configure { width, height }) => { + if self.dimensions != (width, height) { + self.dimensions = (width, height); + self.draw(); + } + false + } + None => false, + } + } + + fn draw(&mut self) { + let (info,png) = load_activate_png(); + + let width = self.dimensions.0 as i32; + let height = self.dimensions.1 as i32; + + // Note: unwrap() is only used here in the interest of simplicity of the example. + // A "real" application should handle the case where both pools are still in use by the + // compositor. + let (mut canvas, buffer) = + self.pool.buffer(info.width as i32, info.height as i32, info.line_size as i32, wl_shm::Format::Argb8888).unwrap(); + + canvas.write_all(&png).unwrap(); + for dst_pixel in canvas.chunks_exact_mut(4) { + let pixel = 0xff00ff00u32.to_ne_bytes(); + dst_pixel[0] = pixel[0]; + dst_pixel[1] = pixel[1]; + dst_pixel[2] = pixel[2]; + dst_pixel[3] = pixel[3]; + } + + // Attach the buffer to the surface and mark the entire surface as damaged + self.surface.attach(Some(&buffer), 0, 0); + self.surface.damage_buffer(0, 0, width as i32, height as i32); + + // Finally, commit the surface + self.surface.commit(); + } +} + +impl Drop for Surface { + fn drop(&mut self) { + self.layer_surface.destroy(); + self.surface.destroy(); + } +} + +fn main() { + let (env, display, queue) = + new_default_environment!(Env, fields = [layer_shell: SimpleGlobal::new(),]) + .expect("Initial roundtrip failed!"); + + let surfaces = Rc::new(RefCell::new(Vec::new())); + + let layer_shell = env.require_global::(); + let compositor = env.require_global::(); + + let env_handle = env.clone(); + let surfaces_handle = Rc::clone(&surfaces); + let output_handler = move |output: wl_output::WlOutput, info: &OutputInfo| { + if info.obsolete { + // an output has been removed, release it + surfaces_handle.borrow_mut().retain(|(i, _)| *i != info.id); + output.release(); + } else { + // an output has been created, construct a surface for it + let surface = env_handle.create_surface().detach(); + let pool = env_handle.create_auto_pool().expect("Failed to create a memory pool!"); + (*surfaces_handle.borrow_mut()) + .push((info.id, Surface::new(&output, surface, &layer_shell.clone(), pool, &compositor))); + } + }; + + // Process currently existing outputs + for output in env.get_all_outputs() { + if let Some(info) = with_output_info(&output, Clone::clone) { + output_handler(output, &info); + } + } + + // Setup a listener for changes + // The listener will live for as long as we keep this handle alive + let _listner_handle = + env.listen_for_outputs(move |output, info, _| output_handler(output, info)); + + let mut event_loop = calloop::EventLoop::<()>::try_new().unwrap(); + + WaylandSource::new(queue).quick_insert(event_loop.handle()).unwrap(); + + loop { + // This is ugly, let's hope that some version of drain_filter() gets stabilized soon + // https://github.com/rust-lang/rust/issues/43244 + { + let mut surfaces = surfaces.borrow_mut(); + let mut i = 0; + while i != surfaces.len() { + if surfaces[i].1.handle_events() { + surfaces.remove(i); + } else { + i += 1; + } + } + } + + display.flush().unwrap(); + event_loop.dispatch(None, &mut ()).unwrap(); + } +}