activate-linux-wayland/src/main.rs

225 lines
7.8 KiB
Rust

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<u8>) {
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<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
],
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<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1>,
next_render_event: Rc<Cell<Option<RenderEvent>>>,
pool: AutoMemPool,
dimensions: (u32, u32),
}
impl Surface {
fn new(
output: &wl_output::WlOutput,
surface: wl_surface::WlSurface,
layer_shell: &Attached<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
pool: AutoMemPool,
compositor: &Attached<WlCompositor>,
) -> 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_margin(0,40,40,0);
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::<RenderEvent>));
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(&region));
// 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::<zwlr_layer_shell_v1::ZwlrLayerShellV1>();
let compositor = env.require_global::<WlCompositor>();
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();
}
}