From 9fd8dd9cec44d99bd7c4e7be5ccb40ae6e04862e Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sun, 7 Jun 2020 23:11:27 +0200 Subject: [PATCH 01/13] anvil: allow draw_windows to take optional output coordinates --- anvil/src/glium_drawer.rs | 10 +++++++++- anvil/src/udev.rs | 7 ++++++- anvil/src/window_map.rs | 4 ++-- anvil/src/winit.rs | 2 +- src/utils/rectangle.rs | 16 ++++++++++++++++ 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index 49b7301..273502c 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -26,6 +26,7 @@ use smithay::{ calloop::LoopHandle, wayland_server::protocol::{wl_buffer, wl_surface}, }, + utils::Rectangle, wayland::{ compositor::{roles::Role, SubsurfaceRole, TraversalAction}, data_device::DnDIconRole, @@ -403,12 +404,19 @@ impl GliumDrawer { &self, frame: &mut Frame, window_map: &MyWindowMap, + output_rect: Option, 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| { + window_map.with_windows_from_bottom_to_top(|toplevel_surface, initial_place, bounding_box| { + // skip windows that do not overlap with a given output + if let Some(output) = output_rect { + if !output.overlaps(bounding_box) { + return; + } + } if let Some(wl_surface) = toplevel_surface.get_surface() { // this surface is a root of a subsurface tree that needs to be drawn self.draw_surface_tree( diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index dad9b04..01007a5 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -601,7 +601,12 @@ impl DrmRenderer { 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); + drawer.draw_windows( + &mut frame, + &*self.window_map.borrow(), + None, + self.compositor_token, + ); let (x, y) = *self.pointer_location.borrow(); // draw the dnd icon if applicable { diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index fdfdc19..9eeec69 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -248,10 +248,10 @@ where pub fn with_windows_from_bottom_to_top(&self, mut f: Func) where - Func: FnMut(&Kind, (i32, i32)), + Func: FnMut(&Kind, (i32, i32), &Rectangle), { for w in self.windows.iter().rev() { - f(&w.toplevel, w.location) + f(&w.toplevel, w.location, &w.bbox) } } diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index bd2d549..0e0f221 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -103,7 +103,7 @@ pub fn run_winit( 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, &*state.window_map.borrow(), state.ctoken); + drawer.draw_windows(&mut frame, &*state.window_map.borrow(), None, state.ctoken); let (x, y) = *state.pointer_location.borrow(); // draw the dnd icon if any diff --git a/src/utils/rectangle.rs b/src/utils/rectangle.rs index 5cd8357..575c565 100644 --- a/src/utils/rectangle.rs +++ b/src/utils/rectangle.rs @@ -17,4 +17,20 @@ impl Rectangle { let (x, y) = point; (x >= self.x) && (x < self.x + self.width) && (y >= self.y) && (y < self.y + self.height) } + + /// Checks whether a given rectangle overlaps with this one + pub fn overlaps(&self, other: &Rectangle) -> bool { + // if the rectangle is not outside of the other + // they must overlap + !( + // self is left of other + self.x + self.width < other.x + // self is right of other + || self.x > other.x + other.width + // self is above of other + || self.y + self.height < other.y + // self is below of other + || self.y > other.y + other.height + ) + } } From d7f800c76da126740b9383f13b32936acd7d61ba Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Thu, 11 Jun 2020 12:15:31 +0200 Subject: [PATCH 02/13] anvil: create and render to multiple outputs independently --- anvil/src/glium_drawer.rs | 3 +- anvil/src/udev.rs | 257 +++++++++++++++++++++++++------------- 2 files changed, 174 insertions(+), 86 deletions(-) diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index 273502c..8e67afe 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -410,12 +410,13 @@ impl GliumDrawer { // 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, bounding_box| { + window_map.with_windows_from_bottom_to_top(|toplevel_surface, mut initial_place, bounding_box| { // skip windows that do not overlap with a given output if let Some(output) = output_rect { if !output.overlaps(bounding_box) { return; } + initial_place.0 -= output.x; } if let Some(wl_surface) = toplevel_surface.get_surface() { // this surface is a root of a subsurface tree that needs to be drawn diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 01007a5..78a79de 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -45,15 +45,16 @@ use smithay::{ encoder::Info as EncoderInfo, }, }, - image::{ImageBuffer, Rgba}, + image::{ImageBuffer, Rgba, Pixel}, input::Libinput, nix::{fcntl::OFlag, sys::stat::dev_t}, wayland_server::{ protocol::{wl_output, wl_surface}, - Display, + Display, Global, }, }, signaling::{Linkable, SignalToken, Signaler}, + utils::Rectangle, wayland::{ compositor::CompositorToken, output::{Mode, Output, PhysicalProperties}, @@ -137,12 +138,15 @@ pub fn run_udev( let bytes = include_bytes!("../resources/cursor2.rgba"); let udev_backend = UdevBackend::new(state.seat_name.clone(), log.clone()).map_err(|_| ())?; + let output_map = Rc::new(RefCell::new(Vec::new())); + let mut udev_handler = UdevHandlerImpl { compositor_token: state.ctoken, #[cfg(feature = "egl")] egl_buffer_reader, session: state.session.clone().unwrap(), backends: HashMap::new(), + output_map, display: display.clone(), primary_gpu, window_map: state.window_map.clone(), @@ -158,34 +162,7 @@ pub fn run_udev( /* * Initialize a fake output (we render one screen to every device in this example) */ - let (output, _output_global) = Output::new( - &mut display.borrow_mut(), - "Drm".into(), - PhysicalProperties { - width: 0, - height: 0, - subpixel: wl_output::Subpixel::Unknown, - make: "Smithay".into(), - model: "Generic DRM".into(), - }, - log.clone(), - ); - - let (w, h) = (1920, 1080); // Hardcode full-hd res - output.change_current_state( - Some(Mode { - width: w as i32, - height: h as i32, - refresh: 60_000, - }), - None, - None, - ); - output.set_preferred(Mode { - width: w as i32, - height: h as i32, - refresh: 60_000, - }); + /* * Initialize libinput backend @@ -250,6 +227,62 @@ pub fn run_udev( Ok(()) } +struct MyOutput { + device_id: dev_t, + crtc: crtc::Handle, + size: (u32, u32), + _wl: Output, + global: Option>, +} + +impl MyOutput { + fn new(display: &mut Display, device_id: dev_t, crtc: crtc::Handle, conn: ConnectorInfo, logger: ::slog::Logger) -> MyOutput { + let (output, global) = Output::new( + display, + format!("{:?}", conn.interface()), + PhysicalProperties { + width: conn.size().unwrap_or((0, 0)).0 as i32, + height: conn.size().unwrap_or((0, 0)).1 as i32, + subpixel: wl_output::Subpixel::Unknown, + make: "Smithay".into(), + model: "Generic DRM".into(), + }, + logger, + ); + + let mode = conn.modes()[0]; + let (w, h) = mode.size(); + output.change_current_state( + Some(Mode { + width: w as i32, + height: h as i32, + refresh: (mode.vrefresh() * 1000) as i32, + }), + None, + None, + ); + output.set_preferred(Mode { + width: w as i32, + height: h as i32, + refresh: (mode.vrefresh() * 1000) as i32, + }); + + MyOutput { + device_id, + crtc, + size: (w as u32, h as u32), + _wl: output, + global: Some(global), + } + } +} + +impl Drop for MyOutput { + fn drop(&mut self) { + self.global.take().unwrap().destroy(); + } +} + struct BackendData { _restart_token: SignalToken, event_source: Source>, @@ -265,6 +298,7 @@ struct UdevHandlerImpl { display: Rc>, primary_gpu: Option, window_map: Rc>, + output_map: Rc>>, pointer_location: Rc>, pointer_image: ImageBuffer, Vec>, cursor_status: Arc>, @@ -279,6 +313,8 @@ impl UdevHandlerImpl { pub fn scan_connectors( device: &mut RenderDevice, egl_buffer_reader: Rc>>, + display: &mut Display, + output_map: &mut Vec, logger: &::slog::Logger, ) -> HashMap>> { // Get a set of all modesetting resource handles (excluding planes): @@ -313,6 +349,7 @@ impl UdevHandlerImpl { egl_buffer_reader.clone(), logger.clone(), ); + output_map.push(MyOutput::new(display, device.device_id(), crtc, connector_info, logger.clone())); entry.insert(Rc::new(renderer)); break 'outer; @@ -327,6 +364,8 @@ impl UdevHandlerImpl { #[cfg(not(feature = "egl"))] pub fn scan_connectors( device: &mut RenderDevice, + display: &mut Display, + output_map: &mut Vec, logger: &::slog::Logger, ) -> HashMap>> { // Get a set of all modesetting resource handles (excluding planes): @@ -356,6 +395,7 @@ impl UdevHandlerImpl { if !backends.contains_key(&crtc) { let renderer = GliumDrawer::init(device.create_surface(crtc).unwrap(), logger.clone()); + output_map.push(MyOutput::new(display, device.device_id(), crtc, connector_info, logger.clone())); backends.insert(crtc, Rc::new(renderer)); break 'outer; @@ -369,7 +409,7 @@ impl UdevHandlerImpl { } impl UdevHandlerImpl { - fn device_added(&mut self, _device: dev_t, path: PathBuf) { + fn device_added(&mut self, device_id: dev_t, path: PathBuf) { // Try to open the device if let Some(mut device) = self .session @@ -421,12 +461,16 @@ impl UdevHandlerImpl { let backends = Rc::new(RefCell::new(UdevHandlerImpl::::scan_connectors( &mut device, self.egl_buffer_reader.clone(), + &mut *self.display.borrow_mut(), + &mut *self.output_map.borrow_mut(), &self.logger, ))); #[cfg(not(feature = "egl"))] let backends = Rc::new(RefCell::new(UdevHandlerImpl::::scan_connectors( &mut device, + &mut *self.display.borrow_mut(), + &mut *self.output_map.borrow_mut(), &self.logger, ))); @@ -434,10 +478,13 @@ impl UdevHandlerImpl { // Note: if you replicate this (very simple) structure, it is rather easy // to introduce reference cycles with Rc. Be sure about your drop order let renderer = Rc::new(DrmRenderer { + device_id, compositor_token: self.compositor_token, backends: backends.clone(), window_map: self.window_map.clone(), + output_map: self.output_map.clone(), pointer_location: self.pointer_location.clone(), + pointer_image: self.pointer_image.clone(), cursor_status: self.cursor_status.clone(), dnd_icon: self.dnd_icon.clone(), logger: self.logger.clone(), @@ -462,12 +509,6 @@ impl UdevHandlerImpl { .unwrap(); for renderer in backends.borrow_mut().values() { - // create cursor - renderer - .borrow() - .set_cursor_representation(&self.pointer_image, (2, 2)) - .unwrap(); - // render first frame schedule_initial_render(renderer.clone(), &self.loop_handle); } @@ -487,26 +528,31 @@ impl UdevHandlerImpl { //quick and dirty, just re-init all backends if let Some(ref mut backend_data) = self.backends.get_mut(&device) { let logger = &self.logger; - let pointer_image = &self.pointer_image; let egl_buffer_reader = self.egl_buffer_reader.clone(); let loop_handle = self.loop_handle.clone(); + let mut display = self.display.borrow_mut(); + let mut output_map = self.output_map.borrow_mut(); + output_map.retain(|output| output.device_id != device); self.loop_handle .with_source(&backend_data.event_source, |source| { let mut backends = backend_data.surfaces.borrow_mut(); #[cfg(feature = "egl")] let new_backends = - UdevHandlerImpl::::scan_connectors(&mut source.file, egl_buffer_reader, logger); + UdevHandlerImpl::::scan_connectors( + &mut source.file, + egl_buffer_reader, + &mut *display, + &mut *output_map, + logger); #[cfg(not(feature = "egl"))] - let new_backends = UdevHandlerImpl::::scan_connectors(&mut source.file, logger); + let new_backends = UdevHandlerImpl::::scan_connectors( + &mut source.file, + &mut *display, + &mut *output_map, + logger); *backends = new_backends; for renderer in backends.values() { - // create cursor - renderer - .borrow() - .set_cursor_representation(pointer_image, (2, 2)) - .unwrap(); - // render first frame schedule_initial_render(renderer.clone(), &loop_handle); } @@ -520,6 +566,8 @@ impl UdevHandlerImpl { // drop surfaces backend_data.surfaces.borrow_mut().clear(); debug!(self.logger, "Surfaces dropped"); + // clear outputs + self.output_map.borrow_mut().retain(|output| output.device_id != device); let device = self.loop_handle.remove(backend_data.event_source).unwrap(); @@ -568,10 +616,13 @@ impl DrmRendererSessionListener { } pub struct DrmRenderer { + device_id: dev_t, compositor_token: CompositorToken, backends: Rc>>>>, window_map: Rc>, + output_map: Rc>>, pointer_location: Rc>, + pointer_image: ImageBuffer, Vec>, cursor_status: Arc>, dnd_icon: Arc>>, logger: ::slog::Logger, @@ -590,13 +641,17 @@ impl DrmRenderer { evt_handle: Option<&LoopHandle>, ) { if let Some(drawer) = self.backends.borrow().get(&crtc) { - { - let (x, y) = *self.pointer_location.borrow(); - let _ = drawer - .borrow() - .set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32); - } - + // get output coordinates + let (x, y) = self.output_map.borrow() + .iter() + .take_while(|output| output.device_id != self.device_id || output.crtc != crtc) + .fold((0u32, 0u32), |pos, output| (pos.0 + output.size.0, pos.1)); + let (width, height) = self.output_map.borrow() + .iter() + .find(|output| output.device_id == self.device_id && output.crtc == crtc) + .map(|output| output.size) + .unwrap_or((0, 0)); // in this case the output will be removed. + // and draw in sync with our monitor let mut frame = drawer.draw(); frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); @@ -604,47 +659,78 @@ impl DrmRenderer { drawer.draw_windows( &mut frame, &*self.window_map.borrow(), - None, + Some(Rectangle { + x: x as i32, y: y as i32, + width: width as i32, + height: height as i32, + }), 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.as_ref().is_alive() { - drawer.draw_dnd_icon( - &mut frame, - surface, - (x as i32, y as i32), - self.compositor_token, - ); + + // get pointer coordinates + let (ptr_x, ptr_y) = *self.pointer_location.borrow(); + let ptr_x = ptr_x.trunc().abs() as i32 - x as i32; + let ptr_y = ptr_y.trunc().abs() as i32 - y as i32; + + // set cursor + // TODO hack, should be possible to clear the cursor + let empty = ImageBuffer::from_fn(self.pointer_image.width(), self.pointer_image.height(), |_, _| Rgba::from_channels(0, 0, 0, 0)); + if ptr_x > 0 && ptr_x < width as i32 && ptr_y > 0 && ptr_y < height as i32 { + let _ = drawer + .borrow() + .set_cursor_position(ptr_x as u32, ptr_y as u32); + + let mut software_cursor = false; + + // draw the dnd icon if applicable + { + let guard = self.dnd_icon.lock().unwrap(); + if let Some(ref surface) = *guard { + if surface.as_ref().is_alive() { + drawer.draw_dnd_icon( + &mut frame, + surface, + (ptr_x, ptr_y), + self.compositor_token, + ); + software_cursor = true; + } } } - } - // 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.as_ref().is_alive(); + // 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.as_ref().is_alive(); + } + if reset { + *guard = CursorImageStatus::Default; + } + if let CursorImageStatus::Image(ref surface) = *guard { + drawer.draw_cursor(&mut frame, surface, (ptr_x, ptr_y), self.compositor_token); + software_cursor = true; + } } - if reset { - *guard = CursorImageStatus::Default; + + // TODO: this is wasteful, only do this on change (cache previous software cursor state) + if software_cursor { + if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (2, 2)) { + error!(self.logger, "Error setting cursor: {}", err); + } + } else { + if let Err(err) = drawer.borrow().set_cursor_representation(&self.pointer_image, (2, 2)) { + error!(self.logger, "Error setting cursor: {}", err); + } } - if let CursorImageStatus::Image(ref surface) = *guard { - drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), self.compositor_token); + } else { + if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (2, 2)) { + error!(self.logger, "Error setting cursor: {}", err); } } - let result = frame.finish(); - if result.is_ok() { - // Send frame events so that client start drawing their next frame - self.window_map.borrow().send_frames(SCOUNTER.next_serial()); - } - - if let Err(err) = result { + if let Err(err) = frame.finish() { warn!(self.logger, "Error during rendering: {:?}", err); let reschedule = match err { SwapBuffersError::AlreadySwapped => false, @@ -697,6 +783,7 @@ impl DrmRenderer { } } } else { + // TODO: only send drawn windows the frames callback // Send frame events so that client start drawing their next frame self.window_map.borrow().send_frames(SCOUNTER.next_serial()); } From 33fb393383c0d8a32229133bb0c6b919939ef178 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Thu, 11 Jun 2020 19:36:57 +0200 Subject: [PATCH 03/13] anvil: clamp input to current output instead of full hd --- anvil/src/input_handler.rs | 80 +++++++++++++++++++++++++++++++++++--- anvil/src/state.rs | 10 +++-- anvil/src/udev.rs | 13 ++++--- anvil/src/winit.rs | 1 + 4 files changed, 89 insertions(+), 15 deletions(-) diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 2657982..d00eff5 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -92,10 +92,14 @@ impl AnvilState { let mut location = self.pointer_location.borrow_mut(); location.0 += x as f64; location.1 += y as f64; - // clamp to screen limits - // this event is never generated by winit so self.screen_size is relevant - location.0 = (location.0).max(0.0).min(self.screen_size.0 as f64); - location.1 = (location.1).max(0.0).min(self.screen_size.1 as f64); + + #[cfg(feature = "udev")] + { + // clamp to screen limits + // this event is never generated by winit + *location = self.clamp_coords(*location); + } + let under = self .window_map .borrow() @@ -110,8 +114,11 @@ impl AnvilState { { if self.session.is_some() { // we are started on a tty - let (ux, uy) = evt.position_transformed(self.screen_size); - (ux as f64, uy as f64) + let x = self.pointer_location.borrow().0; + let screen_size = self.current_output_size(x); + // monitor coordinates + let (ux, uy) = evt.position_transformed(screen_size); + ((ux + self.current_output_offset(x)) as f64, uy as f64) } else { // we are started in winit evt.position() @@ -128,6 +135,67 @@ impl AnvilState { self.pointer.motion((x, y), under, serial, evt.time()); } + #[cfg(feature = "udev")] + fn clamp_coords(&self, pos: (f64, f64)) -> (f64, f64) { + let output_map = self.output_map + .as_ref().unwrap(); + let outputs = output_map.borrow(); + + if outputs.len() == 0 { + return pos; + } + + let (mut x, mut y) = pos; + // max_x is the sum of the width of all outputs + let max_x = outputs + .iter() + .fold(0u32, |acc, output| acc + output.size.0); + x = x.max(0.0).min(max_x as f64); + + // max y depends on the current output + let max_y = self.current_output_size(x).1; + y = y.max(0.0).min(max_y as f64); + + (x, y) + } + + #[cfg(feature = "udev")] + fn current_output_idx(&self, x: f64) -> usize { + let output_map = self.output_map.as_ref().unwrap(); + let outputs = output_map.borrow(); + + outputs + .iter() + // map each output to their x position + .scan(0u32, |acc, output| { + let curr_x = *acc; + *acc += output.size.0; + Some(curr_x) + }) + // get an index + .enumerate() + // find the first one with a greater x + .find(|(_idx, x_pos)| *x_pos as f64 > x) + // the previous output is the one we are on + .map(|(idx, _)| idx - 1) + .unwrap_or(outputs.len() - 1) + } + #[cfg(feature = "udev")] + fn current_output_size(&self, x: f64) -> (u32, u32) { + let output_map = self.output_map.as_ref().unwrap(); + let outputs = output_map.borrow(); + outputs[self.current_output_idx(x)].size + } + #[cfg(feature = "udev")] + fn current_output_offset(&self, x: f64) -> u32 { + let output_map = self.output_map.as_ref().unwrap(); + let outputs = output_map.borrow(); + outputs + .iter() + .take(self.current_output_idx(x)) + .fold(0u32, |acc, output| acc + output.size.0) + } + fn on_pointer_button(&mut self, evt: B::PointerButtonEvent) { let serial = SCOUNTER.next_serial(); let button = match evt.button() { diff --git a/anvil/src/state.rs b/anvil/src/state.rs index 671601b..37982d5 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -27,7 +27,7 @@ use smithay::{ #[cfg(feature = "udev")] use smithay::backend::session::Session; -use crate::{buffer_utils::BufferUtils, shell::init_shell}; +use crate::{buffer_utils::BufferUtils, shell::init_shell, udev::MyOutput}; pub struct AnvilState { pub socket_name: String, @@ -43,7 +43,8 @@ pub struct AnvilState { pub keyboard: KeyboardHandle, pub pointer_location: Rc>, pub cursor_status: Arc>, - pub screen_size: (u32, u32), + #[cfg(feature = "udev")] + pub output_map: Option>>>, pub seat_name: String, #[cfg(feature = "udev")] pub session: Option, @@ -58,6 +59,8 @@ impl AnvilState { buffer_utils: BufferUtils, #[cfg(feature = "udev")] session: Option, #[cfg(not(feature = "udev"))] _session: Option<()>, + #[cfg(feature = "udev")] output_map: Option>>>, + #[cfg(not(feature = "udev"))] _output_map: Option<()>, log: slog::Logger, ) -> AnvilState { // init the wayland connection @@ -162,7 +165,8 @@ impl AnvilState { keyboard, cursor_status, pointer_location: Rc::new(RefCell::new((0.0, 0.0))), - screen_size: (1920, 1080), + #[cfg(feature = "udev")] + output_map, seat_name, #[cfg(feature = "udev")] session, diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 78a79de..4a0dff1 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -112,6 +112,8 @@ pub fn run_udev( let buffer_utils = BufferUtils::new(egl_buffer_reader.clone(), log.clone()); #[cfg(not(feature = "egl"))] let buffer_utils = BufferUtils::new(log.clone()); + + let output_map = Rc::new(RefCell::new(Vec::new())); /* * Initialize session @@ -127,6 +129,7 @@ pub fn run_udev( event_loop.handle(), buffer_utils, Some(session), + Some(output_map.clone()), log.clone(), ); @@ -138,8 +141,6 @@ pub fn run_udev( let bytes = include_bytes!("../resources/cursor2.rgba"); let udev_backend = UdevBackend::new(state.seat_name.clone(), log.clone()).map_err(|_| ())?; - let output_map = Rc::new(RefCell::new(Vec::new())); - let mut udev_handler = UdevHandlerImpl { compositor_token: state.ctoken, #[cfg(feature = "egl")] @@ -227,10 +228,10 @@ pub fn run_udev( Ok(()) } -struct MyOutput { - device_id: dev_t, - crtc: crtc::Handle, - size: (u32, u32), +pub struct MyOutput { + pub device_id: dev_t, + pub crtc: crtc::Handle, + pub size: (u32, u32), _wl: Output, global: Option>, } diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 0e0f221..be16774 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -58,6 +58,7 @@ pub fn run_winit( event_loop.handle(), buffer_utils, None, + None, log.clone(), ); From 7e75a68e5761da1ff19c20470e1a5b703c9c7f2b Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Thu, 11 Jun 2020 19:46:29 +0200 Subject: [PATCH 04/13] anvil: allow swapping monitors via shortcut --- anvil/src/input_handler.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index d00eff5..dfd5d48 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -81,6 +81,17 @@ impl AnvilState { "err" => format!("{:?}", e) ); } + }, + KeyAction::Screen(num) => { + let output_map = self.output_map + .as_ref().unwrap(); + let outputs = output_map.borrow(); + if let Some(output) = outputs.get(num) { + let x = outputs.iter().take(num).fold(0, |acc, output| acc + output.size.0) as f64 + (output.size.0 as f64 / 2.0); + let y = output.size.1 as f64 / 2.0; + *self.pointer_location.borrow_mut() = (x as f64, y as f64) + } + } _ => (), } @@ -268,6 +279,8 @@ enum KeyAction { VtSwitch(i32), /// run a command Run(String), + /// Switch the current screen + Screen(usize), /// Forward the key to the client Forward, /// Do nothing more @@ -287,6 +300,8 @@ fn process_keyboard_shortcut(modifiers: ModifiersState, keysym: Keysym) -> KeyAc } else if modifiers.logo && keysym == xkb::KEY_Return { // run terminal KeyAction::Run("weston-terminal".into()) + } else if modifiers.logo && keysym >= xkb::KEY_1 && keysym <= xkb::KEY_9 { + KeyAction::Screen((keysym - xkb::KEY_1) as usize) } else { KeyAction::Forward } From f84d6cb180df24b783cd97bff3dbb913fb9e2450 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Fri, 12 Jun 2020 21:19:57 +0200 Subject: [PATCH 05/13] anvil/udev: do not upload a cursor every frame --- anvil/src/udev.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 4a0dff1..3febe14 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -5,7 +5,7 @@ use std::{ os::unix::io::{AsRawFd, RawFd}, path::PathBuf, rc::Rc, - sync::{atomic::Ordering, Arc, Mutex}, + sync::{atomic::{Ordering, AtomicBool}, Arc, Mutex}, time::Duration, }; @@ -488,6 +488,7 @@ impl UdevHandlerImpl { pointer_image: self.pointer_image.clone(), cursor_status: self.cursor_status.clone(), dnd_icon: self.dnd_icon.clone(), + hardware_cursor: AtomicBool::new(false), logger: self.logger.clone(), }); let mut listener = DrmRendererSessionListener { @@ -626,6 +627,7 @@ pub struct DrmRenderer { pointer_image: ImageBuffer, Vec>, cursor_status: Arc>, dnd_icon: Arc>>, + hardware_cursor: AtomicBool, logger: ::slog::Logger, } @@ -681,7 +683,7 @@ impl DrmRenderer { .borrow() .set_cursor_position(ptr_x as u32, ptr_y as u32); - let mut software_cursor = false; + let mut needs_software_cursor = false; // draw the dnd icon if applicable { @@ -694,7 +696,7 @@ impl DrmRenderer { (ptr_x, ptr_y), self.compositor_token, ); - software_cursor = true; + //needs_software_cursor = true; } } } @@ -711,23 +713,24 @@ impl DrmRenderer { } if let CursorImageStatus::Image(ref surface) = *guard { drawer.draw_cursor(&mut frame, surface, (ptr_x, ptr_y), self.compositor_token); - software_cursor = true; + needs_software_cursor = true; } } - // TODO: this is wasteful, only do this on change (cache previous software cursor state) - if software_cursor { - if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (2, 2)) { + if needs_software_cursor && self.hardware_cursor.swap(false, Ordering::AcqRel) { + if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (0, 0)) { error!(self.logger, "Error setting cursor: {}", err); } - } else { + } else if !needs_software_cursor && !self.hardware_cursor.swap(true, Ordering::AcqRel) { if let Err(err) = drawer.borrow().set_cursor_representation(&self.pointer_image, (2, 2)) { error!(self.logger, "Error setting cursor: {}", err); } } } else { - if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (2, 2)) { - error!(self.logger, "Error setting cursor: {}", err); + if self.hardware_cursor.swap(false, Ordering::AcqRel) { + if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (0, 0)) { + error!(self.logger, "Error setting cursor: {}", err); + } } } From 4930e7e8b2996ff6afb4b0a54390cdf9a354762d Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Fri, 12 Jun 2020 21:20:47 +0200 Subject: [PATCH 06/13] anvil: create a texture per backend --- anvil/src/glium_drawer.rs | 15 ++++++++++----- anvil/src/shell.rs | 5 +++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index 8e67afe..9f436a8 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -1,6 +1,7 @@ use std::{ cell::{Ref, RefCell}, rc::Rc, + sync::atomic::{AtomicUsize, Ordering}, }; use glium::{ @@ -38,6 +39,8 @@ use smithay::{ use crate::shaders; use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData}; +pub static BACKEND_COUNTER: AtomicUsize = AtomicUsize::new(0); + #[derive(Copy, Clone)] struct Vertex { position: [f32; 2], @@ -52,6 +55,7 @@ mod implement_vertex { } pub struct GliumDrawer { + id: usize, display: GliumGraphicsBackend, vertex_buffer: glium::VertexBuffer, index_buffer: glium::IndexBuffer, @@ -107,6 +111,7 @@ impl> + GLGraphicsBackend + 'static> GliumDrawer let programs = opengl_programs!(&display); GliumDrawer { + id: BACKEND_COUNTER.fetch_add(1, Ordering::AcqRel), display, vertex_buffer, index_buffer, @@ -151,6 +156,7 @@ impl> + GLGraphicsBackend + 'static> GliumDrawer let programs = opengl_programs!(&display); GliumDrawer { + id: BACKEND_COUNTER.fetch_add(1, Ordering::AcqRel), display, vertex_buffer, index_buffer, @@ -321,7 +327,7 @@ impl GliumDrawer { // Pull a new buffer if available if let Some(data) = attributes.user_data.get::>() { let mut data = data.borrow_mut(); - if data.texture.is_none() { + if !data.texture.contains_key(&self.id) { if let Some(buffer) = data.current_state.buffer.take() { if let Ok(m) = self.texture_from_buffer(buffer.clone()) { // release the buffer if it was an SHM buffer @@ -335,8 +341,7 @@ impl GliumDrawer { { buffer.release(); } - - data.texture = Some(m); + data.texture.insert(self.id, m); } else { // there was an error reading the buffer, release it, we // already logged the error @@ -345,7 +350,7 @@ impl GliumDrawer { } } // Now, should we be drawn ? - if data.texture.is_some() { + if data.texture.contains_key(&self.id) { // if yes, also process the children if Role::::has(role) { x += data.current_state.sub_location.0; @@ -364,7 +369,7 @@ impl GliumDrawer { |_surface, attributes, role, &(mut x, mut y)| { if let Some(ref data) = attributes.user_data.get::>() { let data = data.borrow(); - if let Some(ref metadata) = data.texture { + if let Some(ref metadata) = data.texture.get(&self.id) { // we need to re-extract the subsurface offset, as the previous closure // only passes it to our children if Role::::has(role) { diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index d960393..a239c97 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -1,5 +1,6 @@ use std::{ cell::RefCell, + collections::HashMap, rc::Rc, sync::{Arc, Mutex}, }; @@ -654,7 +655,7 @@ pub struct CommitedState { #[derive(Default)] pub struct SurfaceData { - pub texture: Option, + pub texture: HashMap, pub geometry: Option, pub resize_state: ResizeState, /// Minimum width and height, as requested by the surface. @@ -673,7 +674,7 @@ impl SurfaceData { /// Apply a next state into the surface current state pub fn apply_state(&mut self, next_state: CommitedState) { if Self::merge_state(&mut self.current_state, next_state) { - self.texture = None; + self.texture.clear(); } } From d603a9ccfbf6fbdcac891a10012a1caf584a0c64 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sun, 21 Jun 2020 14:54:58 +0200 Subject: [PATCH 07/13] egl: Do not store and release WlBuffer for EGLImages --- src/backend/egl/display.rs | 13 ++++++------- src/backend/egl/mod.rs | 10 ++++------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs index 79233d8..d5c733a 100644 --- a/src/backend/egl/display.rs +++ b/src/backend/egl/display.rs @@ -475,7 +475,7 @@ impl EGLBufferReader { /// to render it another way. pub fn egl_buffer_contents( &self, - buffer: WlBuffer, + buffer: &WlBuffer, ) -> ::std::result::Result { let mut format: i32 = 0; let query = wrap_egl_call(|| unsafe { @@ -486,9 +486,9 @@ impl EGLBufferReader { &mut format, ) }) - .map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?; + .map_err(BufferAccessError::NotManaged)?; if query == ffi::egl::FALSE { - return Err(BufferAccessError::NotManaged(buffer, EGLError::BadParameter)); + return Err(BufferAccessError::NotManaged(EGLError::BadParameter)); } let format = match format { @@ -510,7 +510,7 @@ impl EGLBufferReader { &mut width, ) }) - .map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?; + .map_err(BufferAccessError::NotManaged)?; let mut height: i32 = 0; wrap_egl_call(|| unsafe { @@ -521,7 +521,7 @@ impl EGLBufferReader { &mut height, ) }) - .map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?; + .map_err(BufferAccessError::NotManaged)?; let mut inverted: i32 = 0; wrap_egl_call(|| unsafe { @@ -532,7 +532,7 @@ impl EGLBufferReader { &mut inverted, ) }) - .map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?; + .map_err(BufferAccessError::NotManaged)?; let mut images = Vec::with_capacity(format.num_planes()); for i in 0..format.num_planes() { @@ -562,7 +562,6 @@ impl EGLBufferReader { y_inverted: inverted != 0, format, images, - buffer, #[cfg(feature = "renderer_gl")] gl: self.gl.clone(), }) diff --git a/src/backend/egl/mod.rs b/src/backend/egl/mod.rs index 7942e98..047bd64 100644 --- a/src/backend/egl/mod.rs +++ b/src/backend/egl/mod.rs @@ -26,7 +26,7 @@ use crate::backend::graphics::{ use nix::libc::c_uint; use std::fmt; #[cfg(feature = "wayland_frontend")] -use wayland_server::{protocol::wl_buffer::WlBuffer, Display}; +use wayland_server::Display; pub mod context; pub use self::context::EGLContext; @@ -87,8 +87,8 @@ pub enum BufferAccessError { #[error("The corresponding context was lost")] ContextLost, /// This buffer is not managed by the EGL buffer - #[error("This buffer is not managed by EGL. Err: {1:}")] - NotManaged(WlBuffer, #[source] EGLError), + #[error("This buffer is not managed by EGL. Err: {0:}")] + NotManaged(#[source] EGLError), /// Failed to create `EGLImages` from the buffer #[error("Failed to create EGLImages from the buffer. Err: {0:}")] EGLImageCreationFailed(#[source] EGLError), @@ -102,7 +102,7 @@ impl fmt::Debug for BufferAccessError { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> { match *self { BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"), - BufferAccessError::NotManaged(_, _) => write!(formatter, "BufferAccessError::NotManaged"), + BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"), BufferAccessError::EGLImageCreationFailed(_) => { write!(formatter, "BufferAccessError::EGLImageCreationFailed") } @@ -265,7 +265,6 @@ pub struct EGLImages { /// Format of these images pub format: Format, images: Vec, - buffer: WlBuffer, #[cfg(feature = "renderer_gl")] gl: gl_ffi::Gles2, } @@ -339,7 +338,6 @@ impl Drop for EGLImages { ffi::egl::DestroyImageKHR(**self.display, image); } } - self.buffer.release(); } } From bf011e807170f24bc3fbb47ab031bc438f0037d7 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 27 Jun 2020 18:27:47 +0200 Subject: [PATCH 08/13] anvil: Introduce BufferTextures --- anvil/src/buffer_utils.rs | 179 ++++++++++++++++++++++++-- anvil/src/glium_drawer.rs | 255 ++++++++----------------------------- anvil/src/input_handler.rs | 1 + anvil/src/shell.rs | 5 +- anvil/src/shm_load.rs | 18 ++- anvil/src/state.rs | 12 +- anvil/src/udev.rs | 81 ++---------- anvil/src/winit.rs | 13 +- 8 files changed, 262 insertions(+), 302 deletions(-) diff --git a/anvil/src/buffer_utils.rs b/anvil/src/buffer_utils.rs index e784177..e42b650 100644 --- a/anvil/src/buffer_utils.rs +++ b/anvil/src/buffer_utils.rs @@ -1,14 +1,27 @@ -use std::{cell::RefCell, rc::Rc}; - +use std::collections::HashMap; +#[cfg(feature = "egl")] +use std::{ + cell::RefCell, + rc::Rc, +}; +use glium::texture::Texture2d; +#[cfg(feature = "egl")] +use glium::{ + texture::{MipmapsOption, UncompressedFloatFormat}, + GlObject, +}; use slog::Logger; #[cfg(feature = "egl")] -use smithay::backend::egl::display::EGLBufferReader; +use smithay::backend::egl::{display::EGLBufferReader, EGLImages, BufferAccessError as EGLBufferAccessError, Format}; use smithay::{ + backend::graphics::gl::GLGraphicsBackend, reexports::wayland_server::protocol::wl_buffer::WlBuffer, - wayland::shm::with_buffer_contents as shm_buffer_contents, + wayland::shm::{with_buffer_contents as shm_buffer_contents, BufferAccessError}, }; +use crate::glium_drawer::GliumDrawer; + /// Utilities for working with `WlBuffer`s. #[derive(Clone)] pub struct BufferUtils { @@ -41,22 +54,172 @@ impl BufferUtils { .borrow() .as_ref() .and_then(|display| display.egl_buffer_dimensions(buffer)) - .or_else(|| self.shm_buffer_dimensions(buffer)) + .or_else(|| self.shm_buffer_dimensions(buffer).ok()) } /// Returns the dimensions of an image stored in the buffer. #[cfg(not(feature = "egl"))] pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> { - self.shm_buffer_dimensions(buffer) + self.shm_buffer_dimensions(buffer).ok() } /// Returns the dimensions of an image stored in the shm buffer. - fn shm_buffer_dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> { + fn shm_buffer_dimensions(&self, buffer: &WlBuffer) -> Result<(i32, i32), BufferAccessError> { shm_buffer_contents(buffer, |_, data| (data.width, data.height)) .map_err(|err| { warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err)); err }) - .ok() + } + + #[cfg(feature = "egl")] + pub fn load_buffer(&self, buffer: WlBuffer) -> Result { + // try to retrieve the egl contents of this buffer + let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() { + display.egl_buffer_contents(&buffer) + } else { + return Err(buffer); + }; + + match images { + Ok(images) => { + // we have an EGL buffer + Ok(BufferTextures { + buffer, + textures: HashMap::new(), + fragment: crate::shaders::BUFFER_RGBA, + y_inverted: images.y_inverted, + dimensions: (images.width, images.height), + images: Some(images), // I guess we need to keep this alive ? + logger: self.log.clone(), + }) + } + Err(EGLBufferAccessError::NotManaged(_)) => { + // this is not an EGL buffer, try SHM + self.load_shm_buffer(buffer) + } + Err(err) => { + error!(self.log, "EGL error"; "err" => format!("{:?}", err)); + Err(buffer) + } + } + } + + #[cfg(not(feature = "egl"))] + pub fn load_buffer(&self, buffer: WlBuffer) -> Result { + self.load_shm_buffer(buffer) + } + + fn load_shm_buffer(&self, buffer: WlBuffer) -> Result { + let (width, height, format) = match shm_buffer_contents(&buffer, |_, data| (data.width, data.height, data.format)) { + Ok(x) => x, + Err(err) => { + warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err)); + return Err(buffer); + } + }; + let shader = match crate::shm_load::load_format(format) { + Ok(x) => x.1, + Err(format) => { + warn!(self.log, "Unable to load buffer format: {:?}", format); + return Err(buffer); + } + }; + Ok(BufferTextures { + buffer, + textures: HashMap::new(), + fragment: shader, + y_inverted: false, + dimensions: (width as u32, height as u32), + #[cfg(feature = "egl")] + images: None, + logger: self.log.clone(), + }) } } + +pub struct BufferTextures { + buffer: WlBuffer, + pub textures: HashMap, + pub fragment: usize, + pub y_inverted: bool, + pub dimensions: (u32, u32), + #[cfg(feature = "egl")] + images: Option, + logger: slog::Logger, +} + +impl BufferTextures { + #[cfg(feature = "egl")] + pub fn load_texture<'a, F: GLGraphicsBackend + 'static>(&'a mut self, drawer: &GliumDrawer) -> Result<&'a Texture2d, ()> { + if self.textures.contains_key(&drawer.id) { + return Ok(&self.textures[&drawer.id]); + } + + if let Some(images) = self.images.as_ref() { //EGL buffer + let format = match images.format { + Format::RGB => UncompressedFloatFormat::U8U8U8, + Format::RGBA => UncompressedFloatFormat::U8U8U8U8, + _ => { + warn!(self.logger, "Unsupported EGL buffer format"; "format" => format!("{:?}", images.format)); + return Err(()); + } + }; + + let opengl_texture = Texture2d::empty_with_format( + &drawer.display, + format, + MipmapsOption::NoMipmap, + images.width, + images.height, + ) + .unwrap(); + + unsafe { + images + .bind_to_texture(0, opengl_texture.get_id(), &*drawer.display.borrow()) + .expect("Failed to bind to texture"); + } + + self.textures.insert(drawer.id, opengl_texture); + Ok(&self.textures[&drawer.id]) + } else { + self.load_shm_texture(drawer) + } + } + + #[cfg(not(feature = "egl"))] + pub fn load_texture<'a, F: GLGraphicsBackend + 'static>(&'a mut self, drawer: &GliumDrawer) -> Result<&'a Texture2d, ()> { + if self.textures.contains_key(&drawer.id) { + return Ok(&self.textures[&drawer.id]); + } + + self.load_shm_texture(drawer) + } + + fn load_shm_texture<'a, F: GLGraphicsBackend + 'static>(&'a mut self, drawer: &GliumDrawer) -> Result<&'a Texture2d, ()> { + match shm_buffer_contents(&self.buffer, |slice, data| { + crate::shm_load::load_shm_buffer(data, slice) + .map(|(image, _kind)| Texture2d::new(&drawer.display, image).unwrap()) + }) { + Ok(Ok(texture)) => { + self.textures.insert(drawer.id, texture); + Ok(&self.textures[&drawer.id]) + }, + Ok(Err(format)) => { + warn!(self.logger, "Unsupported SHM buffer format"; "format" => format!("{:?}", format)); + Err(()) + } + Err(err) => { + warn!(self.logger, "Unable to load buffer contents"; "err" => format!("{:?}", err)); + Err(()) + } + } + } +} + +impl Drop for BufferTextures { + fn drop(&mut self) { + self.buffer.release() + } +} \ No newline at end of file diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index 9f436a8..b3d99a6 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -7,35 +7,30 @@ use std::{ use glium::{ self, index::PrimitiveType, - texture::{MipmapsOption, Texture2d, UncompressedFloatFormat}, - GlObject, Surface, + texture::Texture2d, + Surface, }; use slog::Logger; -#[cfg(feature = "egl")] -use smithay::backend::egl::display::EGLBufferReader; use smithay::{ - backend::{ - egl::{BufferAccessError, EGLImages, Format}, - graphics::{ - gl::GLGraphicsBackend, - glium::{Frame, GliumGraphicsBackend}, - SwapBuffersError, - }, + backend::graphics::{ + gl::GLGraphicsBackend, + glium::{Frame, GliumGraphicsBackend}, + SwapBuffersError, }, reexports::{ calloop::LoopHandle, - wayland_server::protocol::{wl_buffer, wl_surface}, + wayland_server::protocol::wl_surface, }, utils::Rectangle, wayland::{ compositor::{roles::Role, SubsurfaceRole, TraversalAction}, data_device::DnDIconRole, seat::CursorImageRole, - shm::with_buffer_contents as shm_buffer_contents, }, }; +use crate::buffer_utils::BufferUtils; use crate::shaders; use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData}; @@ -55,13 +50,12 @@ mod implement_vertex { } pub struct GliumDrawer { - id: usize, - display: GliumGraphicsBackend, + pub id: usize, + pub display: GliumGraphicsBackend, vertex_buffer: glium::VertexBuffer, index_buffer: glium::IndexBuffer, programs: [glium::Program; shaders::FRAGMENT_COUNT], - #[cfg(feature = "egl")] - egl_buffer_reader: Rc>>, + buffer_loader: BufferUtils, log: Logger, } @@ -72,10 +66,9 @@ impl GliumDrawer { } impl> + GLGraphicsBackend + 'static> GliumDrawer { - #[cfg(feature = "egl")] pub fn init( backend: T, - egl_buffer_reader: Rc>>, + buffer_loader: BufferUtils, log: Logger, ) -> GliumDrawer { let display = backend.into(); @@ -116,140 +109,13 @@ impl> + GLGraphicsBackend + 'static> GliumDrawer vertex_buffer, index_buffer, programs, - egl_buffer_reader, - log, - } - } - - #[cfg(not(feature = "egl"))] - pub fn init(backend: T, log: Logger) -> GliumDrawer { - let display = backend.into(); - - // building the vertex buffer, which contains all the vertices that we will draw - let vertex_buffer = glium::VertexBuffer::new( - &display, - &[ - Vertex { - position: [0.0, 0.0], - tex_coords: [0.0, 0.0], - }, - Vertex { - position: [0.0, 1.0], - tex_coords: [0.0, 1.0], - }, - Vertex { - position: [1.0, 1.0], - tex_coords: [1.0, 1.0], - }, - Vertex { - position: [1.0, 0.0], - tex_coords: [1.0, 0.0], - }, - ], - ) - .unwrap(); - - // building the index buffer - let index_buffer = - glium::IndexBuffer::new(&display, PrimitiveType::TriangleStrip, &[1 as u16, 2, 0, 3]).unwrap(); - - let programs = opengl_programs!(&display); - - GliumDrawer { - id: BACKEND_COUNTER.fetch_add(1, Ordering::AcqRel), - display, - vertex_buffer, - index_buffer, - programs, + buffer_loader, log, } } } impl GliumDrawer { - #[cfg(feature = "egl")] - pub fn texture_from_buffer(&self, buffer: wl_buffer::WlBuffer) -> Result { - // try to retrieve the egl contents of this buffer - let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() { - display.egl_buffer_contents(buffer) - } else { - Err(BufferAccessError::NotManaged( - buffer, - smithay::backend::egl::EGLError::BadDisplay, - )) - }; - match images { - Ok(images) => { - // we have an EGL buffer - let format = match images.format { - Format::RGB => UncompressedFloatFormat::U8U8U8, - Format::RGBA => UncompressedFloatFormat::U8U8U8U8, - _ => { - warn!(self.log, "Unsupported EGL buffer format"; "format" => format!("{:?}", images.format)); - return Err(()); - } - }; - let opengl_texture = Texture2d::empty_with_format( - &self.display, - format, - MipmapsOption::NoMipmap, - images.width, - images.height, - ) - .unwrap(); - unsafe { - images - .bind_to_texture(0, opengl_texture.get_id(), &*self.display.borrow()) - .expect("Failed to bind to texture"); - } - Ok(TextureMetadata { - texture: opengl_texture, - fragment: crate::shaders::BUFFER_RGBA, - y_inverted: images.y_inverted, - dimensions: (images.width, images.height), - images: Some(images), // I guess we need to keep this alive ? - }) - } - Err(BufferAccessError::NotManaged(buffer, _)) => { - // this is not an EGL buffer, try SHM - self.texture_from_shm_buffer(buffer) - } - Err(err) => { - error!(self.log, "EGL error"; "err" => format!("{:?}", err)); - Err(()) - } - } - } - - #[cfg(not(feature = "egl"))] - pub fn texture_from_buffer(&self, buffer: wl_buffer::WlBuffer) -> Result { - self.texture_from_shm_buffer(buffer) - } - - fn texture_from_shm_buffer(&self, buffer: wl_buffer::WlBuffer) -> Result { - match shm_buffer_contents(&buffer, |slice, data| { - crate::shm_load::load_shm_buffer(data, slice) - .map(|(image, kind)| (Texture2d::new(&self.display, image).unwrap(), kind, data)) - }) { - Ok(Ok((texture, kind, data))) => Ok(TextureMetadata { - texture, - fragment: kind, - y_inverted: false, - dimensions: (data.width as u32, data.height as u32), - #[cfg(feature = "egl")] - images: None, - }), - Ok(Err(format)) => { - warn!(self.log, "Unsupported SHM buffer format"; "format" => format!("{:?}", format)); - Err(()) - } - Err(err) => { - warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err)); - Err(()) - } - } - } - pub fn render_texture(&self, target: &mut Frame, spec: RenderTextureSpec<'_>) { let xscale = 2.0 * (spec.surface_dimensions.0 as f32) / (spec.screen_size.0 as f32); let mut yscale = -2.0 * (spec.surface_dimensions.1 as f32) / (spec.screen_size.1 as f32); @@ -302,15 +168,6 @@ pub struct RenderTextureSpec<'a> { blending: glium::Blend, } -pub struct TextureMetadata { - pub texture: Texture2d, - pub fragment: usize, - pub y_inverted: bool, - pub dimensions: (u32, u32), - #[cfg(feature = "egl")] - images: Option, -} - impl GliumDrawer { fn draw_surface_tree( &self, @@ -327,30 +184,18 @@ impl GliumDrawer { // Pull a new buffer if available if let Some(data) = attributes.user_data.get::>() { let mut data = data.borrow_mut(); - if !data.texture.contains_key(&self.id) { + if data.texture.is_none() { if let Some(buffer) = data.current_state.buffer.take() { - if let Ok(m) = self.texture_from_buffer(buffer.clone()) { - // release the buffer if it was an SHM buffer - #[cfg(feature = "egl")] - { - if m.images.is_none() { - buffer.release(); - } - } - #[cfg(not(feature = "egl"))] - { - buffer.release(); - } - data.texture.insert(self.id, m); - } else { + match self.buffer_loader.load_buffer(buffer) { + Ok(m) => data.texture = Some(m), // there was an error reading the buffer, release it, we // already logged the error - buffer.release(); - } + Err(buffer) => buffer.release(), + }; } } // Now, should we be drawn ? - if data.texture.contains_key(&self.id) { + if data.texture.is_some() { // if yes, also process the children if Role::::has(role) { x += data.current_state.sub_location.0; @@ -368,36 +213,42 @@ impl GliumDrawer { }, |_surface, attributes, role, &(mut x, mut y)| { if let Some(ref data) = attributes.user_data.get::>() { - let data = data.borrow(); - if let Some(ref metadata) = data.texture.get(&self.id) { - // we need to re-extract the subsurface offset, as the previous closure - // only passes it to our children - if Role::::has(role) { - x += data.current_state.sub_location.0; - y += data.current_state.sub_location.1; - } - self.render_texture( - frame, - RenderTextureSpec { - texture: &metadata.texture, - texture_kind: metadata.fragment, - y_inverted: metadata.y_inverted, - surface_dimensions: metadata.dimensions, - surface_location: (x, y), - screen_size: screen_dimensions, - blending: ::glium::Blend { - color: ::glium::BlendingFunction::Addition { - source: ::glium::LinearBlendingFactor::One, - destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha, + let mut data = data.borrow_mut(); + let (sub_x, sub_y) = data.current_state.sub_location; + if let Some(buffer_textures) = data.texture.as_mut() { + let texture_kind = buffer_textures.fragment; + let y_inverted = buffer_textures.y_inverted; + let surface_dimensions = buffer_textures.dimensions; + if let Ok(ref texture) = buffer_textures.load_texture(&self) { + // we need to re-extract the subsurface offset, as the previous closure + // only passes it to our children + if Role::::has(role) { + x += sub_x; + y += sub_y; + } + self.render_texture( + frame, + RenderTextureSpec { + texture: &texture, + texture_kind, + y_inverted, + surface_dimensions, + surface_location: (x, y), + screen_size: screen_dimensions, + blending: ::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() }, - alpha: ::glium::BlendingFunction::Addition { - source: ::glium::LinearBlendingFactor::One, - destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha, - }, - ..Default::default() }, - }, - ); + ); + } } } }, diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index dfd5d48..17174e1 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -82,6 +82,7 @@ impl AnvilState { ); } }, + #[cfg(feature = "udev")] KeyAction::Screen(num) => { let output_map = self.output_map .as_ref().unwrap(); diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index a239c97..fe297cf 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -1,6 +1,5 @@ use std::{ cell::RefCell, - collections::HashMap, rc::Rc, sync::{Arc, Mutex}, }; @@ -655,7 +654,7 @@ pub struct CommitedState { #[derive(Default)] pub struct SurfaceData { - pub texture: HashMap, + pub texture: Option, pub geometry: Option, pub resize_state: ResizeState, /// Minimum width and height, as requested by the surface. @@ -674,7 +673,7 @@ impl SurfaceData { /// Apply a next state into the surface current state pub fn apply_state(&mut self, next_state: CommitedState) { if Self::merge_state(&mut self.current_state, next_state) { - self.texture.clear(); + let _ = self.texture.take(); } } diff --git a/anvil/src/shm_load.rs b/anvil/src/shm_load.rs index 1a9f26e..abb41c6 100644 --- a/anvil/src/shm_load.rs +++ b/anvil/src/shm_load.rs @@ -32,13 +32,7 @@ pub fn load_shm_buffer(data: BufferData, pool: &[u8]) -> Result<(RawImage2d<'_, }; // sharders format need to be reversed to account for endianness - let (client_format, fragment) = match data.format { - Format::Argb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRA), - Format::Xrgb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRX), - Format::Rgba8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_ABGR), - Format::Rgbx8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_XBGR), - _ => return Err(data.format), - }; + let (client_format, fragment) = load_format(data.format)?; Ok(( RawImage2d { data: slice, @@ -49,3 +43,13 @@ pub fn load_shm_buffer(data: BufferData, pool: &[u8]) -> Result<(RawImage2d<'_, fragment, )) } + +pub fn load_format(format: Format) -> Result<(ClientFormat, usize), Format> { + Ok(match format { + Format::Argb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRA), + Format::Xrgb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRX), + Format::Rgba8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_ABGR), + Format::Rgbx8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_XBGR), + _ => return Err(format), + }) +} diff --git a/anvil/src/state.rs b/anvil/src/state.rs index 37982d5..edc4c62 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -8,7 +8,6 @@ use std::{ }; use smithay::{ - backend::session::auto::AutoSession, reexports::{ calloop::{ generic::{Fd, Generic}, @@ -25,9 +24,14 @@ use smithay::{ }; #[cfg(feature = "udev")] -use smithay::backend::session::Session; +use smithay::backend::session::{ + auto::AutoSession, + Session, +}; -use crate::{buffer_utils::BufferUtils, shell::init_shell, udev::MyOutput}; +use crate::{buffer_utils::BufferUtils, shell::init_shell}; +#[cfg(feature = "udev")] +use crate::udev::MyOutput; pub struct AnvilState { pub socket_name: String, @@ -129,7 +133,7 @@ impl AnvilState { "anvil".into() }; #[cfg(not(feature = "udev"))] - let seat_name = "anvil".into(); + let seat_name: String = "anvil".into(); let (mut seat, _) = Seat::new( &mut display.borrow_mut(), diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 3febe14..15ae828 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -127,7 +127,7 @@ pub fn run_udev( let mut state = AnvilState::init( display.clone(), event_loop.handle(), - buffer_utils, + buffer_utils.clone(), Some(session), Some(output_map.clone()), log.clone(), @@ -143,6 +143,7 @@ pub fn run_udev( let mut udev_handler = UdevHandlerImpl { compositor_token: state.ctoken, + buffer_utils: buffer_utils, #[cfg(feature = "egl")] egl_buffer_reader, session: state.session.clone().unwrap(), @@ -292,6 +293,7 @@ struct BackendData { struct UdevHandlerImpl { compositor_token: CompositorToken, + buffer_utils: BufferUtils, #[cfg(feature = "egl")] egl_buffer_reader: Rc>>, session: AutoSession, @@ -310,10 +312,9 @@ struct UdevHandlerImpl { } impl UdevHandlerImpl { - #[cfg(feature = "egl")] pub fn scan_connectors( device: &mut RenderDevice, - egl_buffer_reader: Rc>>, + buffer_utils: &BufferUtils, display: &mut Display, output_map: &mut Vec, logger: &::slog::Logger, @@ -347,7 +348,7 @@ impl UdevHandlerImpl { device .create_surface(crtc, connector_info.modes()[0], &[connector_info.handle()]) .unwrap(), - egl_buffer_reader.clone(), + buffer_utils.clone(), logger.clone(), ); output_map.push(MyOutput::new(display, device.device_id(), crtc, connector_info, logger.clone())); @@ -361,52 +362,6 @@ impl UdevHandlerImpl { backends } - - #[cfg(not(feature = "egl"))] - pub fn scan_connectors( - device: &mut RenderDevice, - display: &mut Display, - output_map: &mut Vec, - logger: &::slog::Logger, - ) -> HashMap>> { - // Get a set of all modesetting resource handles (excluding planes): - let res_handles = device.resource_handles().unwrap(); - - // Use first connected connector - let connector_infos: Vec = res_handles - .connectors() - .iter() - .map(|conn| device.get_connector_info(*conn).unwrap()) - .filter(|conn| conn.state() == ConnectorState::Connected) - .inspect(|conn| info!(logger, "Connected: {:?}", conn.interface())) - .collect(); - - let mut backends = HashMap::new(); - - // very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete - for connector_info in connector_infos { - let encoder_infos = connector_info - .encoders() - .iter() - .filter_map(|e| *e) - .flat_map(|encoder_handle| device.get_encoder_info(encoder_handle)) - .collect::>(); - 'outer: for encoder_info in encoder_infos { - for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) { - if !backends.contains_key(&crtc) { - let renderer = - GliumDrawer::init(device.create_surface(crtc).unwrap(), logger.clone()); - output_map.push(MyOutput::new(display, device.device_id(), crtc, connector_info, logger.clone())); - - backends.insert(crtc, Rc::new(renderer)); - break 'outer; - } - } - } - } - - backends - } } impl UdevHandlerImpl { @@ -453,23 +408,15 @@ impl UdevHandlerImpl { #[cfg(feature = "egl")] { if path.canonicalize().ok() == self.primary_gpu { + info!(self.logger, "Initializing EGL Hardware Acceleration via {:?}", path); *self.egl_buffer_reader.borrow_mut() = device.bind_wl_display(&*self.display.borrow()).ok(); } } - #[cfg(feature = "egl")] - let backends = Rc::new(RefCell::new(UdevHandlerImpl::::scan_connectors( - &mut device, - self.egl_buffer_reader.clone(), - &mut *self.display.borrow_mut(), - &mut *self.output_map.borrow_mut(), - &self.logger, - ))); - - #[cfg(not(feature = "egl"))] let backends = Rc::new(RefCell::new(UdevHandlerImpl::::scan_connectors( &mut device, + &self.buffer_utils, &mut *self.display.borrow_mut(), &mut *self.output_map.borrow_mut(), &self.logger, @@ -528,9 +475,9 @@ impl UdevHandlerImpl { fn device_changed(&mut self, device: dev_t) { //quick and dirty, just re-init all backends + let buffer_utils = &self.buffer_utils; if let Some(ref mut backend_data) = self.backends.get_mut(&device) { let logger = &self.logger; - let egl_buffer_reader = self.egl_buffer_reader.clone(); let loop_handle = self.loop_handle.clone(); let mut display = self.display.borrow_mut(); let mut output_map = self.output_map.borrow_mut(); @@ -538,21 +485,13 @@ impl UdevHandlerImpl { self.loop_handle .with_source(&backend_data.event_source, |source| { let mut backends = backend_data.surfaces.borrow_mut(); - #[cfg(feature = "egl")] - let new_backends = + *backends = UdevHandlerImpl::::scan_connectors( &mut source.file, - egl_buffer_reader, + buffer_utils, &mut *display, &mut *output_map, logger); - #[cfg(not(feature = "egl"))] - let new_backends = UdevHandlerImpl::::scan_connectors( - &mut source.file, - &mut *display, - &mut *output_map, - logger); - *backends = new_backends; for renderer in backends.values() { // render first frame diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index be16774..9432d59 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration}; use smithay::{ - backend::{egl::EGLGraphicsBackend, graphics::gl::GLGraphicsBackend, input::InputBackend, winit}, + backend::{graphics::gl::GLGraphicsBackend, input::InputBackend, winit}, reexports::{ calloop::EventLoop, wayland_server::{protocol::wl_output, Display}, @@ -12,6 +12,8 @@ use smithay::{ SERIAL_COUNTER as SCOUNTER, }, }; +#[cfg(feature = "egl")] +use smithay::backend::egl::EGLGraphicsBackend; use slog::Logger; @@ -38,17 +40,14 @@ pub fn run_winit( }, )); - let (w, h) = renderer.get_framebuffer_dimensions(); - #[cfg(feature = "egl")] - let drawer = GliumDrawer::init(renderer, egl_buffer_reader.clone(), log.clone()); - #[cfg(not(feature = "egl"))] - let drawer = GliumDrawer::init(renderer, log.clone()); - #[cfg(feature = "egl")] let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone()); #[cfg(not(feature = "egl"))] let buffer_utils = BufferUtils::new(log.clone()); + let (w, h) = renderer.get_framebuffer_dimensions(); + let drawer = GliumDrawer::init(renderer, buffer_utils.clone(), log.clone()); + /* * Initialize the globals */ From 51b5b39b75220f21fcc0df588b5aa1e9f12cea22 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 27 Jun 2020 23:26:33 +0200 Subject: [PATCH 09/13] cursor: allow cursor clearing on CursorBackend --- src/backend/drm/atomic/surface.rs | 13 +++++++++++++ src/backend/drm/common/fallback.rs | 1 + src/backend/drm/egl/surface.rs | 4 ++++ src/backend/drm/eglstream/surface.rs | 15 +++++++++++++++ src/backend/drm/gbm/surface.rs | 20 ++++++++++++++++++++ src/backend/drm/legacy/surface.rs | 14 ++++++++++++++ src/backend/graphics/cursor.rs | 3 +++ src/backend/winit.rs | 6 ++++++ 8 files changed, 76 insertions(+) diff --git a/src/backend/drm/atomic/surface.rs b/src/backend/drm/atomic/surface.rs index 82bf2da..ce0bb08 100644 --- a/src/backend/drm/atomic/surface.rs +++ b/src/backend/drm/atomic/surface.rs @@ -646,6 +646,15 @@ impl CursorBackend for AtomicDrmSurfaceInternal { Ok(()) } + + fn clear_cursor_representation(&self) -> Result<(), Self::Error> { + let mut cursor = self.cursor.lock().unwrap(); + if let Some(fb) = cursor.framebuffer.take() { + let _ = self.destroy_framebuffer(fb); + } + + self.clear_plane(self.planes.cursor) + } } impl AtomicDrmSurfaceInternal { @@ -1022,6 +1031,10 @@ impl CursorBackend for AtomicDrmSurface { ) -> Result<(), Error> { self.0.set_cursor_representation(buffer, hotspot) } + + fn clear_cursor_representation(&self) -> Result<(), Self::Error> { + self.0.clear_cursor_representation() + } } impl Surface for AtomicDrmSurface { diff --git a/src/backend/drm/common/fallback.rs b/src/backend/drm/common/fallback.rs index c1ddef7..bfc55d1 100644 --- a/src/backend/drm/common/fallback.rs +++ b/src/backend/drm/common/fallback.rs @@ -552,6 +552,7 @@ where fallback_surface_err_impl!(set_cursor_position, &Self, Result<(), EitherError>, x: u32, y: u32); fallback_surface_err_impl!(set_cursor_representation, &Self, Result<(), EitherError>, buffer: &Self::CursorFormat, hotspot: (u32, u32)); + fallback_surface_err_impl!(clear_cursor_representation, &Self, Result<(), EitherError>,); } #[cfg(feature = "renderer_gl")] diff --git a/src/backend/drm/egl/surface.rs b/src/backend/drm/egl/surface.rs index 4fabceb..2b852fb 100644 --- a/src/backend/drm/egl/surface.rs +++ b/src/backend/drm/egl/surface.rs @@ -92,6 +92,10 @@ where ) -> ::std::result::Result<(), Self::Error> { self.0.surface.set_cursor_representation(buffer, hotspot) } + + fn clear_cursor_representation(&self) -> Result<(), Self::Error> { + self.0.surface.clear_cursor_representation() + } } #[cfg(feature = "renderer_gl")] diff --git a/src/backend/drm/eglstream/surface.rs b/src/backend/drm/eglstream/surface.rs index 3d70284..b2ca40c 100644 --- a/src/backend/drm/eglstream/surface.rs +++ b/src/backend/drm/eglstream/surface.rs @@ -190,6 +190,17 @@ impl CursorBackend for EglStreamSurfaceInternal { Ok(()) } + + fn clear_cursor_representation(&self) -> Result<(), Self::Error> { + self.crtc.set_cursor(self.crtc.crtc(), Option::<&DumbBuffer>::None) + .compat() + .map_err(|source| DrmError::Access { + errmsg: "Failed to clear cursor", + dev: self.crtc.dev_path(), + source, + }) + .map_err(Error::Underlying) + } } /// egl stream surface for rendering @@ -565,6 +576,10 @@ impl CursorBackend for EglStreamSurface { ) -> Result<(), Self::Error> { self.0.set_cursor_representation(buffer, hotspot) } + + fn clear_cursor_representation(&self) -> Result<(), Self::Error> { + self.0.clear_cursor_representation() + } } #[cfg(test)] diff --git a/src/backend/drm/gbm/surface.rs b/src/backend/drm/gbm/surface.rs index c3add63..2bdf3bb 100644 --- a/src/backend/drm/gbm/surface.rs +++ b/src/backend/drm/gbm/surface.rs @@ -305,6 +305,22 @@ where *self.cursor.lock().unwrap() = (cursor, hotspot); Ok(()) } + + fn clear_cursor_representation(&self) -> Result<(), Self::Error> { + *self.cursor.lock().unwrap() = (self.dev.lock() + .unwrap() + .create_buffer_object( + 1, + 1, + GbmFormat::ARGB8888, + BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE, + ) + .map_err(Error::BufferCreationFailed)?, + (0, 0) + ); + self.crtc.clear_cursor_representation() + .map_err(Error::Underlying) + } } impl Drop for GbmSurfaceInternal { @@ -415,6 +431,10 @@ where ) -> Result<(), Self::Error> { self.0.set_cursor_representation(buffer, hotspot) } + + fn clear_cursor_representation(&self) -> Result<(), Self::Error> { + self.0.clear_cursor_representation() + } } #[cfg(test)] diff --git a/src/backend/drm/legacy/surface.rs b/src/backend/drm/legacy/surface.rs index 383e12e..d54b6a1 100644 --- a/src/backend/drm/legacy/surface.rs +++ b/src/backend/drm/legacy/surface.rs @@ -87,6 +87,16 @@ impl CursorBackend for LegacyDrmSurfaceInternal { Ok(()) } + + fn clear_cursor_representation(&self) -> Result<(), Error> { + self.set_cursor(self.crtc, Option::<&DumbBuffer>::None) + .compat() + .map_err(|source| Error::Access { + errmsg: "Failed to set cursor", + dev: self.dev_path(), + source, + }) + } } impl Surface for LegacyDrmSurfaceInternal { @@ -498,6 +508,10 @@ impl CursorBackend for LegacyDrmSurface { ) -> Result<(), Error> { self.0.set_cursor_representation(buffer, hotspot) } + + fn clear_cursor_representation(&self) -> Result<(), Self::Error> { + self.0.clear_cursor_representation() + } } impl Surface for LegacyDrmSurface { diff --git a/src/backend/graphics/cursor.rs b/src/backend/graphics/cursor.rs index f1ff46c..c0550dc 100644 --- a/src/backend/graphics/cursor.rs +++ b/src/backend/graphics/cursor.rs @@ -34,4 +34,7 @@ pub trait CursorBackend { cursor: &Self::CursorFormat, hotspot: (u32, u32), ) -> Result<(), Self::Error>; + + /// Clear the current cursor image drawn on the [`CursorBackend`]. + fn clear_cursor_representation(&self) -> Result<(), Self::Error>; } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 239effd..2d90a24 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -270,6 +270,12 @@ impl CursorBackend for WinitGraphicsBackend { // Cannot log this one, as `CursorFormat` is not `Debug` and should not be debug!(self.logger, "Changing cursor representation"); self.window.window().set_cursor_icon(*cursor); + self.window.window().set_cursor_visible(true); + Ok(()) + } + + fn clear_cursor_representation(&self) -> ::std::result::Result<(), ()> { + self.window.window().set_cursor_visible(false); Ok(()) } } From 107b18a4c1d2e0294db93a80a7ca73c372480a6f Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 27 Jun 2020 23:26:52 +0200 Subject: [PATCH 10/13] logind: do not include dbus code without dbus dependency --- src/backend/session/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 231081d..8047cf2 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -160,6 +160,9 @@ impl AsErrno for () { } pub mod auto; -mod dbus; pub mod direct; + +#[cfg(feature="backend_session_logind")] +mod dbus; +#[cfg(feature="backend_session_logind")] pub use self::dbus::*; From a1f14cb571d1f97f442adfa670f0d58851437efc Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 27 Jun 2020 23:58:31 +0200 Subject: [PATCH 11/13] reexports: reexport winit --- src/reexports.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/reexports.rs b/src/reexports.rs index d5a4706..e7ec2c8 100644 --- a/src/reexports.rs +++ b/src/reexports.rs @@ -21,3 +21,5 @@ pub use wayland_commons; pub use wayland_protocols; #[cfg(feature = "wayland_frontend")] pub use wayland_server; +#[cfg(feature = "backend_winit")] +pub use winit; \ No newline at end of file From 3fd336aba53f90484404bc3d3204b4a4ce493955 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 27 Jun 2020 23:59:10 +0200 Subject: [PATCH 12/13] anvil: GliumDrawer handle hardware cursors and track state --- anvil/src/glium_drawer.rs | 104 ++++++++++++++++++++++++++++++-------- anvil/src/udev.rs | 34 +++---------- anvil/src/winit.rs | 5 +- 3 files changed, 93 insertions(+), 50 deletions(-) diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index b3d99a6..d2bc935 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -1,7 +1,7 @@ use std::{ cell::{Ref, RefCell}, rc::Rc, - sync::atomic::{AtomicUsize, Ordering}, + sync::atomic::{AtomicUsize, AtomicBool, Ordering}, }; use glium::{ @@ -14,6 +14,7 @@ use slog::Logger; use smithay::{ backend::graphics::{ + CursorBackend, gl::GLGraphicsBackend, glium::{Frame, GliumGraphicsBackend}, SwapBuffersError, @@ -56,6 +57,7 @@ pub struct GliumDrawer { index_buffer: glium::IndexBuffer, programs: [glium::Program; shaders::FRAGMENT_COUNT], buffer_loader: BufferUtils, + pub hardware_cursor: AtomicBool, log: Logger, } @@ -110,11 +112,90 @@ impl> + GLGraphicsBackend + 'static> GliumDrawer index_buffer, programs, buffer_loader, + hardware_cursor: AtomicBool::new(false), log, } } } +impl GliumDrawer { + pub fn draw_hardware_cursor( + &self, + cursor: &::CursorFormat, + hotspot: (u32, u32), + position: (i32, i32), + ) { + let (x, y) = position; + let _ = self.display.borrow().set_cursor_position(x as u32, y as u32); + if !self.hardware_cursor.swap(true, Ordering::SeqCst) { + if let Err(_) = self.display.borrow().set_cursor_representation(cursor, hotspot) { + warn!( + self.log, + "Failed to upload hardware cursor", + ); + } + } + } + + pub fn draw_software_cursor( + &self, + frame: &mut Frame, + surface: &wl_surface::WlSurface, + (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); + self.clear_cursor() + } + + pub fn clear_cursor(&self) { + if self.hardware_cursor.swap(false, Ordering::SeqCst) { + if let Err(_) = self.display.borrow().clear_cursor_representation() { + warn!(self.log, "Failed to clear cursor"); + } + } + } +} + +// I would love to do this, but this is essentially specialization... +// And since this is just an example compositor, it seems we require now, +// that for the use of software cursors we need the hardware cursor trait (to do automatic cleanup..) +/* +impl GliumDrawer { + pub fn draw_software_cursor( + &self, + frame: &mut Frame, + surface: &wl_surface::WlSurface, + (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); + } +} +*/ + impl GliumDrawer { pub fn render_texture(&self, target: &mut Frame, spec: RenderTextureSpec<'_>) { let xscale = 2.0 * (spec.surface_dimensions.0 as f32) / (spec.screen_size.0 as f32); @@ -288,27 +369,6 @@ impl GliumDrawer { } } - pub fn draw_cursor( - &self, - frame: &mut Frame, - surface: &wl_surface::WlSurface, - (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, diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 15ae828..f2fd041 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -5,7 +5,7 @@ use std::{ os::unix::io::{AsRawFd, RawFd}, path::PathBuf, rc::Rc, - sync::{atomic::{Ordering, AtomicBool}, Arc, Mutex}, + sync::{atomic::Ordering, Arc, Mutex}, time::Duration, }; @@ -45,7 +45,7 @@ use smithay::{ encoder::Info as EncoderInfo, }, }, - image::{ImageBuffer, Rgba, Pixel}, + image::{ImageBuffer, Rgba}, input::Libinput, nix::{fcntl::OFlag, sys::stat::dev_t}, wayland_server::{ @@ -435,7 +435,6 @@ impl UdevHandlerImpl { pointer_image: self.pointer_image.clone(), cursor_status: self.cursor_status.clone(), dnd_icon: self.dnd_icon.clone(), - hardware_cursor: AtomicBool::new(false), logger: self.logger.clone(), }); let mut listener = DrmRendererSessionListener { @@ -566,7 +565,6 @@ pub struct DrmRenderer { pointer_image: ImageBuffer, Vec>, cursor_status: Arc>, dnd_icon: Arc>>, - hardware_cursor: AtomicBool, logger: ::slog::Logger, } @@ -615,15 +613,11 @@ impl DrmRenderer { let ptr_y = ptr_y.trunc().abs() as i32 - y as i32; // set cursor - // TODO hack, should be possible to clear the cursor - let empty = ImageBuffer::from_fn(self.pointer_image.width(), self.pointer_image.height(), |_, _| Rgba::from_channels(0, 0, 0, 0)); - if ptr_x > 0 && ptr_x < width as i32 && ptr_y > 0 && ptr_y < height as i32 { + if ptr_x >= 0 && ptr_x < width as i32 && ptr_y >= 0 && ptr_y < height as i32 { let _ = drawer .borrow() .set_cursor_position(ptr_x as u32, ptr_y as u32); - let mut needs_software_cursor = false; - // draw the dnd icon if applicable { let guard = self.dnd_icon.lock().unwrap(); @@ -635,7 +629,6 @@ impl DrmRenderer { (ptr_x, ptr_y), self.compositor_token, ); - //needs_software_cursor = true; } } } @@ -651,26 +644,13 @@ impl DrmRenderer { *guard = CursorImageStatus::Default; } if let CursorImageStatus::Image(ref surface) = *guard { - drawer.draw_cursor(&mut frame, surface, (ptr_x, ptr_y), self.compositor_token); - needs_software_cursor = true; - } - } - - if needs_software_cursor && self.hardware_cursor.swap(false, Ordering::AcqRel) { - if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (0, 0)) { - error!(self.logger, "Error setting cursor: {}", err); - } - } else if !needs_software_cursor && !self.hardware_cursor.swap(true, Ordering::AcqRel) { - if let Err(err) = drawer.borrow().set_cursor_representation(&self.pointer_image, (2, 2)) { - error!(self.logger, "Error setting cursor: {}", err); + drawer.draw_software_cursor(&mut frame, surface, (ptr_x, ptr_y), self.compositor_token); + } else { + drawer.draw_hardware_cursor(&self.pointer_image, (2, 2), (ptr_x, ptr_y)); } } } else { - if self.hardware_cursor.swap(false, Ordering::AcqRel) { - if let Err(err) = drawer.borrow().set_cursor_representation(&empty, (0, 0)) { - error!(self.logger, "Error setting cursor: {}", err); - } - } + drawer.clear_cursor(); } if let Err(err) = frame.finish() { diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 9432d59..104286d 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -5,6 +5,7 @@ use smithay::{ reexports::{ calloop::EventLoop, wayland_server::{protocol::wl_output, Display}, + winit::window::CursorIcon, }, wayland::{ output::{Mode, Output, PhysicalProperties}, @@ -128,7 +129,9 @@ pub fn run_winit( } // draw as relevant if let CursorImageStatus::Image(ref surface) = *guard { - drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), state.ctoken); + drawer.draw_software_cursor(&mut frame, surface, (x as i32, y as i32), state.ctoken); + } else { + drawer.draw_hardware_cursor(&CursorIcon::Default, (0, 0), (x as i32, y as i32)); } } From 9c3c3d939e803d8dd727d683e665a04fb45439b8 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sun, 28 Jun 2020 00:07:48 +0200 Subject: [PATCH 13/13] cargo fmt & lint --- anvil/src/buffer_utils.rs | 59 +++++++++++-------- anvil/src/glium_drawer.rs | 86 ++++++++++++---------------- anvil/src/input_handler.rs | 25 ++++---- anvil/src/state.rs | 7 +-- anvil/src/udev.rs | 77 +++++++++++++++---------- anvil/src/winit.rs | 4 +- src/backend/drm/atomic/surface.rs | 2 +- src/backend/drm/eglstream/surface.rs | 3 +- src/backend/drm/gbm/surface.rs | 9 +-- src/backend/drm/legacy/surface.rs | 2 +- src/backend/session/mod.rs | 4 +- src/reexports.rs | 2 +- 12 files changed, 148 insertions(+), 132 deletions(-) diff --git a/anvil/src/buffer_utils.rs b/anvil/src/buffer_utils.rs index e42b650..8509c11 100644 --- a/anvil/src/buffer_utils.rs +++ b/anvil/src/buffer_utils.rs @@ -1,9 +1,3 @@ -use std::collections::HashMap; -#[cfg(feature = "egl")] -use std::{ - cell::RefCell, - rc::Rc, -}; use glium::texture::Texture2d; #[cfg(feature = "egl")] use glium::{ @@ -11,9 +5,14 @@ use glium::{ GlObject, }; use slog::Logger; +use std::collections::HashMap; +#[cfg(feature = "egl")] +use std::{cell::RefCell, rc::Rc}; #[cfg(feature = "egl")] -use smithay::backend::egl::{display::EGLBufferReader, EGLImages, BufferAccessError as EGLBufferAccessError, Format}; +use smithay::backend::egl::{ + display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLImages, Format, +}; use smithay::{ backend::graphics::gl::GLGraphicsBackend, reexports::wayland_server::protocol::wl_buffer::WlBuffer, @@ -65,11 +64,10 @@ impl BufferUtils { /// Returns the dimensions of an image stored in the shm buffer. fn shm_buffer_dimensions(&self, buffer: &WlBuffer) -> Result<(i32, i32), BufferAccessError> { - shm_buffer_contents(buffer, |_, data| (data.width, data.height)) - .map_err(|err| { - warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err)); - err - }) + shm_buffer_contents(buffer, |_, data| (data.width, data.height)).map_err(|err| { + warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err)); + err + }) } #[cfg(feature = "egl")] @@ -111,13 +109,14 @@ impl BufferUtils { } fn load_shm_buffer(&self, buffer: WlBuffer) -> Result { - let (width, height, format) = match shm_buffer_contents(&buffer, |_, data| (data.width, data.height, data.format)) { - Ok(x) => x, - Err(err) => { - warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err)); - return Err(buffer); - } - }; + let (width, height, format) = + match shm_buffer_contents(&buffer, |_, data| (data.width, data.height, data.format)) { + Ok(x) => x, + Err(err) => { + warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err)); + return Err(buffer); + } + }; let shader = match crate::shm_load::load_format(format) { Ok(x) => x.1, Err(format) => { @@ -151,12 +150,16 @@ pub struct BufferTextures { impl BufferTextures { #[cfg(feature = "egl")] - pub fn load_texture<'a, F: GLGraphicsBackend + 'static>(&'a mut self, drawer: &GliumDrawer) -> Result<&'a Texture2d, ()> { + pub fn load_texture<'a, F: GLGraphicsBackend + 'static>( + &'a mut self, + drawer: &GliumDrawer, + ) -> Result<&'a Texture2d, ()> { if self.textures.contains_key(&drawer.id) { return Ok(&self.textures[&drawer.id]); } - if let Some(images) = self.images.as_ref() { //EGL buffer + if let Some(images) = self.images.as_ref() { + //EGL buffer let format = match images.format { Format::RGB => UncompressedFloatFormat::U8U8U8, Format::RGBA => UncompressedFloatFormat::U8U8U8U8, @@ -189,7 +192,10 @@ impl BufferTextures { } #[cfg(not(feature = "egl"))] - pub fn load_texture<'a, F: GLGraphicsBackend + 'static>(&'a mut self, drawer: &GliumDrawer) -> Result<&'a Texture2d, ()> { + pub fn load_texture<'a, F: GLGraphicsBackend + 'static>( + &'a mut self, + drawer: &GliumDrawer, + ) -> Result<&'a Texture2d, ()> { if self.textures.contains_key(&drawer.id) { return Ok(&self.textures[&drawer.id]); } @@ -197,7 +203,10 @@ impl BufferTextures { self.load_shm_texture(drawer) } - fn load_shm_texture<'a, F: GLGraphicsBackend + 'static>(&'a mut self, drawer: &GliumDrawer) -> Result<&'a Texture2d, ()> { + fn load_shm_texture<'a, F: GLGraphicsBackend + 'static>( + &'a mut self, + drawer: &GliumDrawer, + ) -> Result<&'a Texture2d, ()> { match shm_buffer_contents(&self.buffer, |slice, data| { crate::shm_load::load_shm_buffer(data, slice) .map(|(image, _kind)| Texture2d::new(&drawer.display, image).unwrap()) @@ -205,7 +214,7 @@ impl BufferTextures { Ok(Ok(texture)) => { self.textures.insert(drawer.id, texture); Ok(&self.textures[&drawer.id]) - }, + } Ok(Err(format)) => { warn!(self.logger, "Unsupported SHM buffer format"; "format" => format!("{:?}", format)); Err(()) @@ -222,4 +231,4 @@ impl Drop for BufferTextures { fn drop(&mut self) { self.buffer.release() } -} \ No newline at end of file +} diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index d2bc935..bcf2a76 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -1,28 +1,19 @@ use std::{ cell::{Ref, RefCell}, rc::Rc, - sync::atomic::{AtomicUsize, AtomicBool, Ordering}, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, }; -use glium::{ - self, - index::PrimitiveType, - texture::Texture2d, - Surface, -}; +use glium::{self, index::PrimitiveType, texture::Texture2d, Surface}; use slog::Logger; use smithay::{ backend::graphics::{ - CursorBackend, gl::GLGraphicsBackend, glium::{Frame, GliumGraphicsBackend}, - SwapBuffersError, - }, - reexports::{ - calloop::LoopHandle, - wayland_server::protocol::wl_surface, + CursorBackend, SwapBuffersError, }, + reexports::{calloop::LoopHandle, wayland_server::protocol::wl_surface}, utils::Rectangle, wayland::{ compositor::{roles::Role, SubsurfaceRole, TraversalAction}, @@ -68,11 +59,7 @@ impl GliumDrawer { } impl> + GLGraphicsBackend + 'static> GliumDrawer { - pub fn init( - backend: T, - buffer_loader: BufferUtils, - log: Logger, - ) -> GliumDrawer { + pub fn init(backend: T, buffer_loader: BufferUtils, log: Logger) -> GliumDrawer { let display = backend.into(); // building the vertex buffer, which contains all the vertices that we will draw @@ -127,13 +114,14 @@ impl GliumDrawer { ) { let (x, y) = position; let _ = self.display.borrow().set_cursor_position(x as u32, y as u32); - if !self.hardware_cursor.swap(true, Ordering::SeqCst) { - if let Err(_) = self.display.borrow().set_cursor_representation(cursor, hotspot) { - warn!( - self.log, - "Failed to upload hardware cursor", - ); - } + if !self.hardware_cursor.swap(true, Ordering::SeqCst) + && self + .display + .borrow() + .set_cursor_representation(cursor, hotspot) + .is_err() + { + warn!(self.log, "Failed to upload hardware cursor",); } } @@ -160,15 +148,15 @@ impl GliumDrawer { } pub fn clear_cursor(&self) { - if self.hardware_cursor.swap(false, Ordering::SeqCst) { - if let Err(_) = self.display.borrow().clear_cursor_representation() { - warn!(self.log, "Failed to clear cursor"); - } + if self.hardware_cursor.swap(false, Ordering::SeqCst) + && self.display.borrow().clear_cursor_representation().is_err() + { + warn!(self.log, "Failed to clear cursor"); } } } -// I would love to do this, but this is essentially specialization... +// I would love to do this (check on !CursorBackend), but this is essentially specialization... // And since this is just an example compositor, it seems we require now, // that for the use of software cursors we need the hardware cursor trait (to do automatic cleanup..) /* @@ -347,25 +335,27 @@ impl GliumDrawer { // 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, mut initial_place, bounding_box| { - // skip windows that do not overlap with a given output - if let Some(output) = output_rect { - if !output.overlaps(bounding_box) { - return; + window_map.with_windows_from_bottom_to_top( + |toplevel_surface, mut initial_place, bounding_box| { + // skip windows that do not overlap with a given output + if let Some(output) = output_rect { + if !output.overlaps(bounding_box) { + return; + } + initial_place.0 -= output.x; } - initial_place.0 -= output.x; - } - if let Some(wl_surface) = toplevel_surface.get_surface() { - // this surface is a root of a subsurface tree that needs to be drawn - self.draw_surface_tree( - frame, - &wl_surface, - initial_place, - compositor_token, - screen_dimensions, - ); - } - }); + if let Some(wl_surface) = toplevel_surface.get_surface() { + // this surface is a root of a subsurface tree that needs to be drawn + self.draw_surface_tree( + frame, + &wl_surface, + initial_place, + compositor_token, + screen_dimensions, + ); + } + }, + ); } } diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 17174e1..9d288e6 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -81,18 +81,20 @@ impl AnvilState { "err" => format!("{:?}", e) ); } - }, + } #[cfg(feature = "udev")] KeyAction::Screen(num) => { - let output_map = self.output_map - .as_ref().unwrap(); + let output_map = self.output_map.as_ref().unwrap(); let outputs = output_map.borrow(); if let Some(output) = outputs.get(num) { - let x = outputs.iter().take(num).fold(0, |acc, output| acc + output.size.0) as f64 + (output.size.0 as f64 / 2.0); + let x = outputs + .iter() + .take(num) + .fold(0, |acc, output| acc + output.size.0) as f64 + + (output.size.0 as f64 / 2.0); let y = output.size.1 as f64 / 2.0; *self.pointer_location.borrow_mut() = (x as f64, y as f64) } - } _ => (), } @@ -104,7 +106,7 @@ impl AnvilState { let mut location = self.pointer_location.borrow_mut(); location.0 += x as f64; location.1 += y as f64; - + #[cfg(feature = "udev")] { // clamp to screen limits @@ -149,19 +151,16 @@ impl AnvilState { #[cfg(feature = "udev")] fn clamp_coords(&self, pos: (f64, f64)) -> (f64, f64) { - let output_map = self.output_map - .as_ref().unwrap(); + let output_map = self.output_map.as_ref().unwrap(); let outputs = output_map.borrow(); - + if outputs.len() == 0 { return pos; } let (mut x, mut y) = pos; // max_x is the sum of the width of all outputs - let max_x = outputs - .iter() - .fold(0u32, |acc, output| acc + output.size.0); + let max_x = outputs.iter().fold(0u32, |acc, output| acc + output.size.0); x = x.max(0.0).min(max_x as f64); // max y depends on the current output @@ -170,7 +169,7 @@ impl AnvilState { (x, y) } - + #[cfg(feature = "udev")] fn current_output_idx(&self, x: f64) -> usize { let output_map = self.output_map.as_ref().unwrap(); diff --git a/anvil/src/state.rs b/anvil/src/state.rs index edc4c62..48bc6ce 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -24,14 +24,11 @@ use smithay::{ }; #[cfg(feature = "udev")] -use smithay::backend::session::{ - auto::AutoSession, - Session, -}; +use smithay::backend::session::{auto::AutoSession, Session}; -use crate::{buffer_utils::BufferUtils, shell::init_shell}; #[cfg(feature = "udev")] use crate::udev::MyOutput; +use crate::{buffer_utils::BufferUtils, shell::init_shell}; pub struct AnvilState { pub socket_name: String, diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index f2fd041..0f33b28 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -112,7 +112,7 @@ pub fn run_udev( let buffer_utils = BufferUtils::new(egl_buffer_reader.clone(), log.clone()); #[cfg(not(feature = "egl"))] let buffer_utils = BufferUtils::new(log.clone()); - + let output_map = Rc::new(RefCell::new(Vec::new())); /* @@ -143,7 +143,7 @@ pub fn run_udev( let mut udev_handler = UdevHandlerImpl { compositor_token: state.ctoken, - buffer_utils: buffer_utils, + buffer_utils, #[cfg(feature = "egl")] egl_buffer_reader, session: state.session.clone().unwrap(), @@ -164,7 +164,6 @@ pub fn run_udev( /* * Initialize a fake output (we render one screen to every device in this example) */ - /* * Initialize libinput backend @@ -238,7 +237,13 @@ pub struct MyOutput { } impl MyOutput { - fn new(display: &mut Display, device_id: dev_t, crtc: crtc::Handle, conn: ConnectorInfo, logger: ::slog::Logger) -> MyOutput { + fn new( + display: &mut Display, + device_id: dev_t, + crtc: crtc::Handle, + conn: ConnectorInfo, + logger: ::slog::Logger, + ) -> MyOutput { let (output, global) = Output::new( display, format!("{:?}", conn.interface()), @@ -351,7 +356,13 @@ impl UdevHandlerImpl { buffer_utils.clone(), logger.clone(), ); - output_map.push(MyOutput::new(display, device.device_id(), crtc, connector_info, logger.clone())); + output_map.push(MyOutput::new( + display, + device.device_id(), + crtc, + connector_info, + logger.clone(), + )); entry.insert(Rc::new(renderer)); break 'outer; @@ -408,7 +419,10 @@ impl UdevHandlerImpl { #[cfg(feature = "egl")] { if path.canonicalize().ok() == self.primary_gpu { - info!(self.logger, "Initializing EGL Hardware Acceleration via {:?}", path); + info!( + self.logger, + "Initializing EGL Hardware Acceleration via {:?}", path + ); *self.egl_buffer_reader.borrow_mut() = device.bind_wl_display(&*self.display.borrow()).ok(); } @@ -484,13 +498,13 @@ impl UdevHandlerImpl { self.loop_handle .with_source(&backend_data.event_source, |source| { let mut backends = backend_data.surfaces.borrow_mut(); - *backends = - UdevHandlerImpl::::scan_connectors( - &mut source.file, - buffer_utils, - &mut *display, - &mut *output_map, - logger); + *backends = UdevHandlerImpl::::scan_connectors( + &mut source.file, + buffer_utils, + &mut *display, + &mut *output_map, + logger, + ); for renderer in backends.values() { // render first frame @@ -507,7 +521,9 @@ impl UdevHandlerImpl { backend_data.surfaces.borrow_mut().clear(); debug!(self.logger, "Surfaces dropped"); // clear outputs - self.output_map.borrow_mut().retain(|output| output.device_id != device); + self.output_map + .borrow_mut() + .retain(|output| output.device_id != device); let device = self.loop_handle.remove(backend_data.event_source).unwrap(); @@ -582,16 +598,20 @@ impl DrmRenderer { ) { if let Some(drawer) = self.backends.borrow().get(&crtc) { // get output coordinates - let (x, y) = self.output_map.borrow() + let (x, y) = self + .output_map + .borrow() .iter() .take_while(|output| output.device_id != self.device_id || output.crtc != crtc) .fold((0u32, 0u32), |pos, output| (pos.0 + output.size.0, pos.1)); - let (width, height) = self.output_map.borrow() + let (width, height) = self + .output_map + .borrow() .iter() .find(|output| output.device_id == self.device_id && output.crtc == crtc) .map(|output| output.size) .unwrap_or((0, 0)); // in this case the output will be removed. - + // and draw in sync with our monitor let mut frame = drawer.draw(); frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); @@ -600,7 +620,8 @@ impl DrmRenderer { &mut frame, &*self.window_map.borrow(), Some(Rectangle { - x: x as i32, y: y as i32, + x: x as i32, + y: y as i32, width: width as i32, height: height as i32, }), @@ -614,21 +635,14 @@ impl DrmRenderer { // set cursor if ptr_x >= 0 && ptr_x < width as i32 && ptr_y >= 0 && ptr_y < height as i32 { - let _ = drawer - .borrow() - .set_cursor_position(ptr_x as u32, ptr_y as u32); - + let _ = drawer.borrow().set_cursor_position(ptr_x as u32, ptr_y as u32); + // draw the dnd icon if applicable { let guard = self.dnd_icon.lock().unwrap(); if let Some(ref surface) = *guard { if surface.as_ref().is_alive() { - drawer.draw_dnd_icon( - &mut frame, - surface, - (ptr_x, ptr_y), - self.compositor_token, - ); + drawer.draw_dnd_icon(&mut frame, surface, (ptr_x, ptr_y), self.compositor_token); } } } @@ -644,7 +658,12 @@ impl DrmRenderer { *guard = CursorImageStatus::Default; } if let CursorImageStatus::Image(ref surface) = *guard { - drawer.draw_software_cursor(&mut frame, surface, (ptr_x, ptr_y), self.compositor_token); + drawer.draw_software_cursor( + &mut frame, + surface, + (ptr_x, ptr_y), + self.compositor_token, + ); } else { drawer.draw_hardware_cursor(&self.pointer_image, (2, 2), (ptr_x, ptr_y)); } diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 104286d..7c876b0 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -1,5 +1,7 @@ use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration}; +#[cfg(feature = "egl")] +use smithay::backend::egl::EGLGraphicsBackend; use smithay::{ backend::{graphics::gl::GLGraphicsBackend, input::InputBackend, winit}, reexports::{ @@ -13,8 +15,6 @@ use smithay::{ SERIAL_COUNTER as SCOUNTER, }, }; -#[cfg(feature = "egl")] -use smithay::backend::egl::EGLGraphicsBackend; use slog::Logger; diff --git a/src/backend/drm/atomic/surface.rs b/src/backend/drm/atomic/surface.rs index ce0bb08..96c08ef 100644 --- a/src/backend/drm/atomic/surface.rs +++ b/src/backend/drm/atomic/surface.rs @@ -1031,7 +1031,7 @@ impl CursorBackend for AtomicDrmSurface { ) -> Result<(), Error> { self.0.set_cursor_representation(buffer, hotspot) } - + fn clear_cursor_representation(&self) -> Result<(), Self::Error> { self.0.clear_cursor_representation() } diff --git a/src/backend/drm/eglstream/surface.rs b/src/backend/drm/eglstream/surface.rs index b2ca40c..0d26d88 100644 --- a/src/backend/drm/eglstream/surface.rs +++ b/src/backend/drm/eglstream/surface.rs @@ -192,7 +192,8 @@ impl CursorBackend for EglStreamSurfaceInternal { } fn clear_cursor_representation(&self) -> Result<(), Self::Error> { - self.crtc.set_cursor(self.crtc.crtc(), Option::<&DumbBuffer>::None) + self.crtc + .set_cursor(self.crtc.crtc(), Option::<&DumbBuffer>::None) .compat() .map_err(|source| DrmError::Access { errmsg: "Failed to clear cursor", diff --git a/src/backend/drm/gbm/surface.rs b/src/backend/drm/gbm/surface.rs index 2bdf3bb..56e500d 100644 --- a/src/backend/drm/gbm/surface.rs +++ b/src/backend/drm/gbm/surface.rs @@ -307,7 +307,9 @@ where } fn clear_cursor_representation(&self) -> Result<(), Self::Error> { - *self.cursor.lock().unwrap() = (self.dev.lock() + *self.cursor.lock().unwrap() = ( + self.dev + .lock() .unwrap() .create_buffer_object( 1, @@ -316,10 +318,9 @@ where BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE, ) .map_err(Error::BufferCreationFailed)?, - (0, 0) + (0, 0), ); - self.crtc.clear_cursor_representation() - .map_err(Error::Underlying) + self.crtc.clear_cursor_representation().map_err(Error::Underlying) } } diff --git a/src/backend/drm/legacy/surface.rs b/src/backend/drm/legacy/surface.rs index d54b6a1..04cbe49 100644 --- a/src/backend/drm/legacy/surface.rs +++ b/src/backend/drm/legacy/surface.rs @@ -90,7 +90,7 @@ impl CursorBackend for LegacyDrmSurfaceInternal { fn clear_cursor_representation(&self) -> Result<(), Error> { self.set_cursor(self.crtc, Option::<&DumbBuffer>::None) - .compat() + .compat() .map_err(|source| Error::Access { errmsg: "Failed to set cursor", dev: self.dev_path(), diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 8047cf2..5c9206d 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -162,7 +162,7 @@ impl AsErrno for () { pub mod auto; pub mod direct; -#[cfg(feature="backend_session_logind")] +#[cfg(feature = "backend_session_logind")] mod dbus; -#[cfg(feature="backend_session_logind")] +#[cfg(feature = "backend_session_logind")] pub use self::dbus::*; diff --git a/src/reexports.rs b/src/reexports.rs index e7ec2c8..6c9aaee 100644 --- a/src/reexports.rs +++ b/src/reexports.rs @@ -22,4 +22,4 @@ pub use wayland_protocols; #[cfg(feature = "wayland_frontend")] pub use wayland_server; #[cfg(feature = "backend_winit")] -pub use winit; \ No newline at end of file +pub use winit;