From d1d608ab2b3d9ab71460075fe045d4660d0afedd Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 10 Dec 2018 09:18:00 +0100 Subject: [PATCH] anvil: draw custom cursors and dnd icons --- anvil/src/glium_drawer.rs | 168 ++++++++++++++++++++++++------------ anvil/src/udev.rs | 74 ++++++++++++++-- anvil/src/winit.rs | 72 ++++++++++++++-- src/wayland/seat/pointer.rs | 2 +- 4 files changed, 243 insertions(+), 73 deletions(-) diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index 440ed5a..bb42aa3 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -20,9 +20,14 @@ use smithay::{ }, wayland::{ compositor::{roles::Role, SubsurfaceRole, TraversalAction}, + data_device::DnDIconRole, + seat::CursorImageRole, shm::with_buffer_contents as shm_buffer_contents, }, - wayland_server::{protocol::wl_buffer, Resource}, + wayland_server::{ + protocol::{wl_buffer, wl_surface}, + Resource, + }, }; use shaders; @@ -284,71 +289,120 @@ pub struct TextureMetadata { } impl GliumDrawer { - pub fn draw_windows(&self, window_map: &MyWindowMap, compositor_token: MyCompositorToken, log: &Logger) { - let mut frame = self.draw(); - frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); + fn draw_surface_tree( + &self, + frame: &mut Frame, + root: &Resource, + location: (i32, i32), + compositor_token: MyCompositorToken, + screen_dimensions: (u32, u32), + ) { + compositor_token + .with_surface_tree_upward(root, location, |_surface, attributes, role, &(mut x, mut y)| { + // there is actually something to draw ! + if attributes.user_data.texture.is_none() { + if let Some(buffer) = attributes.user_data.buffer.take() { + if let Ok(m) = self.texture_from_buffer(buffer.clone()) { + attributes.user_data.texture = Some(m); + } + // notify the client that we have finished reading the + // buffer + buffer.send(wl_buffer::Event::Release); + } + } + if let Some(ref metadata) = attributes.user_data.texture { + if let Ok(subdata) = Role::::data(role) { + x += subdata.location.0; + y += subdata.location.1; + } + self.render_texture( + frame, + &metadata.texture, + metadata.fragment, + metadata.y_inverted, + metadata.dimensions, + (x, y), + screen_dimensions, + ::glium::Blend { + color: ::glium::BlendingFunction::Addition { + source: ::glium::LinearBlendingFactor::One, + destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha, + }, + alpha: ::glium::BlendingFunction::Addition { + source: ::glium::LinearBlendingFactor::One, + destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha, + }, + ..Default::default() + }, + ); + TraversalAction::DoChildren((x, y)) + } else { + // we are not display, so our children are neither + TraversalAction::SkipChildren + } + }) + .unwrap(); + } + + pub fn draw_windows( + &self, + frame: &mut Frame, + window_map: &MyWindowMap, + compositor_token: MyCompositorToken, + ) { // redraw the frame, in a simple but inneficient way { let screen_dimensions = self.borrow().get_framebuffer_dimensions(); window_map.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| { if let Some(wl_surface) = toplevel_surface.get_surface() { // this surface is a root of a subsurface tree that needs to be drawn - compositor_token - .with_surface_tree_upward( - wl_surface, - initial_place, - |_surface, attributes, role, &(mut x, mut y)| { - // there is actually something to draw ! - if attributes.user_data.texture.is_none() { - if let Some(buffer) = attributes.user_data.buffer.take() { - if let Ok(m) = self.texture_from_buffer(buffer.clone()) { - attributes.user_data.texture = Some(m); - } - // notify the client that we have finished reading the - // buffer - buffer.send(wl_buffer::Event::Release); - } - } - if let Some(ref metadata) = attributes.user_data.texture { - if let Ok(subdata) = Role::::data(role) { - x += subdata.location.0; - y += subdata.location.1; - } - self.render_texture( - &mut frame, - &metadata.texture, - metadata.fragment, - metadata.y_inverted, - metadata.dimensions, - (x, y), - screen_dimensions, - ::glium::Blend { - color: ::glium::BlendingFunction::Addition { - source: ::glium::LinearBlendingFactor::One, - destination: - ::glium::LinearBlendingFactor::OneMinusSourceAlpha, - }, - alpha: ::glium::BlendingFunction::Addition { - source: ::glium::LinearBlendingFactor::One, - destination: - ::glium::LinearBlendingFactor::OneMinusSourceAlpha, - }, - ..Default::default() - }, - ); - TraversalAction::DoChildren((x, y)) - } else { - // we are not display, so our children are neither - TraversalAction::SkipChildren - } - }, - ) - .unwrap(); + self.draw_surface_tree( + frame, + &wl_surface, + initial_place, + compositor_token, + screen_dimensions, + ); } }); } - if let Err(err) = frame.finish() { - error!(log, "Error during rendering: {:?}", err); + } + + pub fn draw_cursor( + &self, + frame: &mut Frame, + surface: &Resource, + (x, y): (i32, i32), + token: MyCompositorToken, + ) { + let (dx, dy) = match token.with_role_data::(surface, |data| data.hotspot) { + Ok(h) => h, + Err(_) => { + warn!( + self.log, + "Trying to display as a cursor a surface that does not have the CursorImage role." + ); + (0, 0) + } + }; + let screen_dimensions = self.borrow().get_framebuffer_dimensions(); + self.draw_surface_tree(frame, surface, (x - dx, y - dy), token, screen_dimensions); + } + + pub fn draw_dnd_icon( + &self, + frame: &mut Frame, + surface: &Resource, + (x, y): (i32, i32), + token: MyCompositorToken, + ) { + if !token.has_role::(surface) { + warn!( + self.log, + "Trying to display as a dnd icon a surface that does not have the DndIcon role." + ); } + let screen_dimensions = self.borrow().get_framebuffer_dimensions(); + self.draw_surface_tree(frame, surface, (x, y), token, screen_dimensions); } } diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index ca605ab..5b85214 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -7,7 +7,7 @@ use std::{ rc::Rc, sync::{ atomic::{AtomicBool, Ordering}, - Arc, + Arc, Mutex, }, }; @@ -43,9 +43,9 @@ use smithay::{ input::Libinput, wayland::{ compositor::CompositorToken, - data_device::{default_action_chooser, init_data_device, set_data_device_focus}, + data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent}, output::{Mode, Output, PhysicalProperties}, - seat::{Seat, XkbConfig}, + seat::{CursorImageStatus, Seat, XkbConfig}, shm::init_shm_global, }, wayland_server::{ @@ -53,8 +53,8 @@ use smithay::{ generic::{EventedFd, Generic}, EventLoop, LoopHandle, Source, }, - protocol::wl_output, - Display, + protocol::{wl_output, wl_surface}, + Display, Resource, }, }; @@ -101,6 +101,8 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger let running = Arc::new(AtomicBool::new(true)); let pointer_location = Rc::new(RefCell::new((0.0, 0.0))); + let cursor_status = Arc::new(Mutex::new(CursorImageStatus::Default)); + let dnd_icon = Arc::new(Mutex::new(None)); /* * Initialize the udev backend @@ -124,6 +126,8 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger window_map: window_map.clone(), pointer_location: pointer_location.clone(), pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(), + cursor_status: cursor_status.clone(), + dnd_icon: dnd_icon.clone(), loop_handle: event_loop.handle(), notifier: udev_notifier, logger: log.clone(), @@ -136,9 +140,18 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger /* * Initialize wayland clipboard */ + init_data_device( &mut display.borrow_mut(), - |_| {}, + move |event| match event { + DataDeviceEvent::DnDStarted { icon, .. } => { + *dnd_icon.lock().unwrap() = icon; + } + DataDeviceEvent::DnDDropped => { + *dnd_icon.lock().unwrap() = None; + } + _ => {} + }, default_action_chooser, compositor_token.clone(), log.clone(), @@ -154,7 +167,9 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger log.clone(), ); - let pointer = w_seat.add_pointer(compositor_token.clone(), |_| {}); + let pointer = w_seat.add_pointer(compositor_token.clone(), move |new_status| { + *cursor_status.lock().unwrap() = new_status; + }); let keyboard = w_seat .add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| { set_data_device_focus(seat, focus.and_then(|s| s.client())) @@ -271,6 +286,8 @@ struct UdevHandlerImpl { window_map: Rc>, pointer_location: Rc>, pointer_image: ImageBuffer, Vec>, + cursor_status: Arc>, + dnd_icon: Arc>>>, loop_handle: LoopHandle, notifier: S, logger: ::slog::Logger, @@ -410,6 +427,8 @@ impl UdevHandler for UdevHandlerImpl backends: backends.clone(), window_map: self.window_map.clone(), pointer_location: self.pointer_location.clone(), + cursor_status: self.cursor_status.clone(), + dnd_icon: self.dnd_icon.clone(), logger: self.logger.clone(), }); @@ -504,6 +523,8 @@ pub struct DrmHandlerImpl { backends: Rc>>>, window_map: Rc>, pointer_location: Rc>, + cursor_status: Arc>, + dnd_icon: Arc>>>, logger: ::slog::Logger, } @@ -520,7 +541,44 @@ impl DeviceHandler for DrmHandlerImpl { } // and draw in sync with our monitor - drawer.draw_windows(&*self.window_map.borrow(), self.compositor_token, &self.logger); + let mut frame = drawer.draw(); + frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); + // draw the surfaces + drawer.draw_windows(&mut frame, &*self.window_map.borrow(), self.compositor_token); + let (x, y) = *self.pointer_location.borrow(); + // draw the dnd icon if applicable + { + let guard = self.dnd_icon.lock().unwrap(); + if let Some(ref surface) = *guard { + if surface.is_alive() { + drawer.draw_dnd_icon( + &mut frame, + surface, + (x as i32, y as i32), + self.compositor_token, + ); + } + } + } + // draw the cursor as relevant + { + let mut guard = self.cursor_status.lock().unwrap(); + // reset the cursor if the surface is no longer alive + let mut reset = false; + if let CursorImageStatus::Image(ref surface) = *guard { + reset = !surface.is_alive(); + } + if reset { + *guard = CursorImageStatus::Default; + } + if let CursorImageStatus::Image(ref surface) = *guard { + drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), self.compositor_token); + } + } + + if let Err(err) = frame.finish() { + error!(self.logger, "Error during rendering: {:?}", err); + } } } diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index f071db8..a78640a 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -1,15 +1,15 @@ use std::{ cell::RefCell, rc::Rc, - sync::{atomic::AtomicBool, Arc}, + sync::{atomic::AtomicBool, Arc, Mutex}, }; use smithay::{ backend::{egl::EGLGraphicsBackend, graphics::gl::GLGraphicsBackend, input::InputBackend, winit}, wayland::{ - data_device::{default_action_chooser, init_data_device, set_data_device_focus}, + data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent}, output::{Mode, Output, PhysicalProperties}, - seat::{Seat, XkbConfig}, + seat::{CursorImageStatus, Seat, XkbConfig}, shm::init_shm_global, }, wayland_server::{calloop::EventLoop, protocol::wl_output, Display}, @@ -50,9 +50,20 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log let (compositor_token, _, _, window_map) = init_shell(display, log.clone()); + let dnd_icon = Arc::new(Mutex::new(None)); + + let dnd_icon2 = dnd_icon.clone(); init_data_device( display, - |_| {}, + move |event| match event { + DataDeviceEvent::DnDStarted { icon, .. } => { + *dnd_icon2.lock().unwrap() = icon; + } + DataDeviceEvent::DnDDropped => { + *dnd_icon2.lock().unwrap() = None; + } + _ => {} + }, default_action_chooser, compositor_token.clone(), log.clone(), @@ -60,7 +71,13 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log let (mut seat, _) = Seat::new(display, "winit".into(), compositor_token.clone(), log.clone()); - let pointer = seat.add_pointer(compositor_token.clone(), |_| {}); + let cursor_status = Arc::new(Mutex::new(CursorImageStatus::Default)); + + let cursor_status2 = cursor_status.clone(); + let pointer = seat.add_pointer(compositor_token.clone(), move |new_status| { + // TODO: hide winit system cursor when relevant + *cursor_status2.lock().unwrap() = new_status + }); let keyboard = seat .add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| { @@ -96,6 +113,8 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log refresh: 60_000, }); + let pointer_location = Rc::new(RefCell::new((0.0, 0.0))); + input.set_handler(AnvilInputHandler::new( log.clone(), pointer, @@ -103,7 +122,7 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log window_map.clone(), (0, 0), running.clone(), - Rc::new(RefCell::new((0.0, 0.0))), + pointer_location.clone(), )); info!(log, "Initialization completed, starting the main loop."); @@ -111,7 +130,46 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log loop { input.dispatch_new_events().unwrap(); - drawer.draw_windows(&*window_map.borrow(), compositor_token, &log); + // drawing logic + { + use glium::Surface; + let mut frame = drawer.draw(); + frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); + + // draw the windows + drawer.draw_windows(&mut frame, &*window_map.borrow(), compositor_token); + + let (x, y) = *pointer_location.borrow(); + // draw the dnd icon if any + { + let guard = dnd_icon.lock().unwrap(); + if let Some(ref surface) = *guard { + if surface.is_alive() { + drawer.draw_dnd_icon(&mut frame, surface, (x as i32, y as i32), compositor_token); + } + } + } + // draw the cursor as relevant + { + let mut guard = cursor_status.lock().unwrap(); + // reset the cursor if the surface is no longer alive + let mut reset = false; + if let CursorImageStatus::Image(ref surface) = *guard { + reset = !surface.is_alive(); + } + if reset { + *guard = CursorImageStatus::Default; + } + // draw as relevant + if let CursorImageStatus::Image(ref surface) = *guard { + drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), compositor_token); + } + } + + if let Err(err) = frame.finish() { + error!(log, "Error during rendering: {:?}", err); + } + } event_loop .dispatch(Some(::std::time::Duration::from_millis(16)), &mut ()) diff --git a/src/wayland/seat/pointer.rs b/src/wayland/seat/pointer.rs index e72d318..7a57600 100644 --- a/src/wayland/seat/pointer.rs +++ b/src/wayland/seat/pointer.rs @@ -17,7 +17,7 @@ pub struct CursorImageRole { } /// Possible status of a cursor as requested by clients -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum CursorImageStatus { /// The cursor should be hidden Hidden,