anvil: draw custom cursors and dnd icons

This commit is contained in:
Victor Berger 2018-12-10 09:18:00 +01:00 committed by Victor Berger
parent 280decf863
commit d1d608ab2b
4 changed files with 243 additions and 73 deletions

View File

@ -20,9 +20,14 @@ use smithay::{
}, },
wayland::{ wayland::{
compositor::{roles::Role, SubsurfaceRole, TraversalAction}, compositor::{roles::Role, SubsurfaceRole, TraversalAction},
data_device::DnDIconRole,
seat::CursorImageRole,
shm::with_buffer_contents as shm_buffer_contents, shm::with_buffer_contents as shm_buffer_contents,
}, },
wayland_server::{protocol::wl_buffer, Resource}, wayland_server::{
protocol::{wl_buffer, wl_surface},
Resource,
},
}; };
use shaders; use shaders;
@ -284,71 +289,120 @@ pub struct TextureMetadata {
} }
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> { impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
pub fn draw_windows(&self, window_map: &MyWindowMap, compositor_token: MyCompositorToken, log: &Logger) { fn draw_surface_tree(
let mut frame = self.draw(); &self,
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); frame: &mut Frame,
root: &Resource<wl_surface::WlSurface>,
location: (i32, i32),
compositor_token: MyCompositorToken,
screen_dimensions: (u32, u32),
) {
compositor_token
.with_surface_tree_upward(root, location, |_surface, attributes, role, &(mut x, mut y)| {
// there is actually something to draw !
if attributes.user_data.texture.is_none() {
if let Some(buffer) = attributes.user_data.buffer.take() {
if let Ok(m) = self.texture_from_buffer(buffer.clone()) {
attributes.user_data.texture = Some(m);
}
// notify the client that we have finished reading the
// buffer
buffer.send(wl_buffer::Event::Release);
}
}
if let Some(ref metadata) = attributes.user_data.texture {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.location.0;
y += subdata.location.1;
}
self.render_texture(
frame,
&metadata.texture,
metadata.fragment,
metadata.y_inverted,
metadata.dimensions,
(x, y),
screen_dimensions,
::glium::Blend {
color: ::glium::BlendingFunction::Addition {
source: ::glium::LinearBlendingFactor::One,
destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha,
},
alpha: ::glium::BlendingFunction::Addition {
source: ::glium::LinearBlendingFactor::One,
destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha,
},
..Default::default()
},
);
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
})
.unwrap();
}
pub fn draw_windows(
&self,
frame: &mut Frame,
window_map: &MyWindowMap,
compositor_token: MyCompositorToken,
) {
// redraw the frame, in a simple but inneficient way // redraw the frame, in a simple but inneficient way
{ {
let screen_dimensions = self.borrow().get_framebuffer_dimensions(); 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| {
if let Some(wl_surface) = toplevel_surface.get_surface() { if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn // this surface is a root of a subsurface tree that needs to be drawn
compositor_token self.draw_surface_tree(
.with_surface_tree_upward( frame,
wl_surface, &wl_surface,
initial_place, initial_place,
|_surface, attributes, role, &(mut x, mut y)| { compositor_token,
// there is actually something to draw ! screen_dimensions,
if attributes.user_data.texture.is_none() { );
if let Some(buffer) = attributes.user_data.buffer.take() {
if let Ok(m) = self.texture_from_buffer(buffer.clone()) {
attributes.user_data.texture = Some(m);
}
// notify the client that we have finished reading the
// buffer
buffer.send(wl_buffer::Event::Release);
}
}
if let Some(ref metadata) = attributes.user_data.texture {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.location.0;
y += subdata.location.1;
}
self.render_texture(
&mut frame,
&metadata.texture,
metadata.fragment,
metadata.y_inverted,
metadata.dimensions,
(x, y),
screen_dimensions,
::glium::Blend {
color: ::glium::BlendingFunction::Addition {
source: ::glium::LinearBlendingFactor::One,
destination:
::glium::LinearBlendingFactor::OneMinusSourceAlpha,
},
alpha: ::glium::BlendingFunction::Addition {
source: ::glium::LinearBlendingFactor::One,
destination:
::glium::LinearBlendingFactor::OneMinusSourceAlpha,
},
..Default::default()
},
);
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
},
)
.unwrap();
} }
}); });
} }
if let Err(err) = frame.finish() { }
error!(log, "Error during rendering: {:?}", err);
pub fn draw_cursor(
&self,
frame: &mut Frame,
surface: &Resource<wl_surface::WlSurface>,
(x, y): (i32, i32),
token: MyCompositorToken,
) {
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
Ok(h) => h,
Err(_) => {
warn!(
self.log,
"Trying to display as a cursor a surface that does not have the CursorImage role."
);
(0, 0)
}
};
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
self.draw_surface_tree(frame, surface, (x - dx, y - dy), token, screen_dimensions);
}
pub fn draw_dnd_icon(
&self,
frame: &mut Frame,
surface: &Resource<wl_surface::WlSurface>,
(x, y): (i32, i32),
token: MyCompositorToken,
) {
if !token.has_role::<DnDIconRole>(surface) {
warn!(
self.log,
"Trying to display as a dnd icon a surface that does not have the DndIcon role."
);
} }
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
self.draw_surface_tree(frame, surface, (x, y), token, screen_dimensions);
} }
} }

View File

@ -7,7 +7,7 @@ use std::{
rc::Rc, rc::Rc,
sync::{ sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Arc, Mutex,
}, },
}; };
@ -43,9 +43,9 @@ use smithay::{
input::Libinput, input::Libinput,
wayland::{ wayland::{
compositor::CompositorToken, compositor::CompositorToken,
data_device::{default_action_chooser, init_data_device, set_data_device_focus}, data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
output::{Mode, Output, PhysicalProperties}, output::{Mode, Output, PhysicalProperties},
seat::{Seat, XkbConfig}, seat::{CursorImageStatus, Seat, XkbConfig},
shm::init_shm_global, shm::init_shm_global,
}, },
wayland_server::{ wayland_server::{
@ -53,8 +53,8 @@ use smithay::{
generic::{EventedFd, Generic}, generic::{EventedFd, Generic},
EventLoop, LoopHandle, Source, EventLoop, LoopHandle, Source,
}, },
protocol::wl_output, protocol::{wl_output, wl_surface},
Display, Display, Resource,
}, },
}; };
@ -101,6 +101,8 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
let running = Arc::new(AtomicBool::new(true)); let running = Arc::new(AtomicBool::new(true));
let pointer_location = Rc::new(RefCell::new((0.0, 0.0))); let pointer_location = Rc::new(RefCell::new((0.0, 0.0)));
let cursor_status = Arc::new(Mutex::new(CursorImageStatus::Default));
let dnd_icon = Arc::new(Mutex::new(None));
/* /*
* Initialize the udev backend * Initialize the udev backend
@ -124,6 +126,8 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
window_map: window_map.clone(), window_map: window_map.clone(),
pointer_location: pointer_location.clone(), pointer_location: pointer_location.clone(),
pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(), pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(),
cursor_status: cursor_status.clone(),
dnd_icon: dnd_icon.clone(),
loop_handle: event_loop.handle(), loop_handle: event_loop.handle(),
notifier: udev_notifier, notifier: udev_notifier,
logger: log.clone(), logger: log.clone(),
@ -136,9 +140,18 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
/* /*
* Initialize wayland clipboard * Initialize wayland clipboard
*/ */
init_data_device( init_data_device(
&mut display.borrow_mut(), &mut display.borrow_mut(),
|_| {}, move |event| match event {
DataDeviceEvent::DnDStarted { icon, .. } => {
*dnd_icon.lock().unwrap() = icon;
}
DataDeviceEvent::DnDDropped => {
*dnd_icon.lock().unwrap() = None;
}
_ => {}
},
default_action_chooser, default_action_chooser,
compositor_token.clone(), compositor_token.clone(),
log.clone(), log.clone(),
@ -154,7 +167,9 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
log.clone(), log.clone(),
); );
let pointer = w_seat.add_pointer(compositor_token.clone(), |_| {}); let pointer = w_seat.add_pointer(compositor_token.clone(), move |new_status| {
*cursor_status.lock().unwrap() = new_status;
});
let keyboard = w_seat let keyboard = w_seat
.add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| { .add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| {
set_data_device_focus(seat, focus.and_then(|s| s.client())) set_data_device_focus(seat, focus.and_then(|s| s.client()))
@ -271,6 +286,8 @@ struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> {
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Rc<RefCell<(f64, f64)>>, pointer_location: Rc<RefCell<(f64, f64)>>,
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>, pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
cursor_status: Arc<Mutex<CursorImageStatus>>,
dnd_icon: Arc<Mutex<Option<Resource<wl_surface::WlSurface>>>>,
loop_handle: LoopHandle<Data>, loop_handle: LoopHandle<Data>,
notifier: S, notifier: S,
logger: ::slog::Logger, logger: ::slog::Logger,
@ -410,6 +427,8 @@ impl<S: SessionNotifier, Data: 'static> UdevHandler for UdevHandlerImpl<S, Data>
backends: backends.clone(), backends: backends.clone(),
window_map: self.window_map.clone(), window_map: self.window_map.clone(),
pointer_location: self.pointer_location.clone(), pointer_location: self.pointer_location.clone(),
cursor_status: self.cursor_status.clone(),
dnd_icon: self.dnd_icon.clone(),
logger: self.logger.clone(), logger: self.logger.clone(),
}); });
@ -504,6 +523,8 @@ pub struct DrmHandlerImpl {
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<RenderSurface>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<RenderSurface>>>>,
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Rc<RefCell<(f64, f64)>>, pointer_location: Rc<RefCell<(f64, f64)>>,
cursor_status: Arc<Mutex<CursorImageStatus>>,
dnd_icon: Arc<Mutex<Option<Resource<wl_surface::WlSurface>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -520,7 +541,44 @@ impl DeviceHandler for DrmHandlerImpl {
} }
// and draw in sync with our monitor // and draw in sync with our monitor
drawer.draw_windows(&*self.window_map.borrow(), self.compositor_token, &self.logger); let mut frame = drawer.draw();
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
// draw the surfaces
drawer.draw_windows(&mut frame, &*self.window_map.borrow(), self.compositor_token);
let (x, y) = *self.pointer_location.borrow();
// draw the dnd icon if applicable
{
let guard = self.dnd_icon.lock().unwrap();
if let Some(ref surface) = *guard {
if surface.is_alive() {
drawer.draw_dnd_icon(
&mut frame,
surface,
(x as i32, y as i32),
self.compositor_token,
);
}
}
}
// draw the cursor as relevant
{
let mut guard = self.cursor_status.lock().unwrap();
// reset the cursor if the surface is no longer alive
let mut reset = false;
if let CursorImageStatus::Image(ref surface) = *guard {
reset = !surface.is_alive();
}
if reset {
*guard = CursorImageStatus::Default;
}
if let CursorImageStatus::Image(ref surface) = *guard {
drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), self.compositor_token);
}
}
if let Err(err) = frame.finish() {
error!(self.logger, "Error during rendering: {:?}", err);
}
} }
} }

View File

@ -1,15 +1,15 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
rc::Rc, rc::Rc,
sync::{atomic::AtomicBool, Arc}, sync::{atomic::AtomicBool, Arc, Mutex},
}; };
use smithay::{ use smithay::{
backend::{egl::EGLGraphicsBackend, graphics::gl::GLGraphicsBackend, input::InputBackend, winit}, backend::{egl::EGLGraphicsBackend, graphics::gl::GLGraphicsBackend, input::InputBackend, winit},
wayland::{ wayland::{
data_device::{default_action_chooser, init_data_device, set_data_device_focus}, data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
output::{Mode, Output, PhysicalProperties}, output::{Mode, Output, PhysicalProperties},
seat::{Seat, XkbConfig}, seat::{CursorImageStatus, Seat, XkbConfig},
shm::init_shm_global, shm::init_shm_global,
}, },
wayland_server::{calloop::EventLoop, protocol::wl_output, Display}, wayland_server::{calloop::EventLoop, protocol::wl_output, Display},
@ -50,9 +50,20 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log
let (compositor_token, _, _, window_map) = init_shell(display, log.clone()); let (compositor_token, _, _, window_map) = init_shell(display, log.clone());
let dnd_icon = Arc::new(Mutex::new(None));
let dnd_icon2 = dnd_icon.clone();
init_data_device( init_data_device(
display, display,
|_| {}, move |event| match event {
DataDeviceEvent::DnDStarted { icon, .. } => {
*dnd_icon2.lock().unwrap() = icon;
}
DataDeviceEvent::DnDDropped => {
*dnd_icon2.lock().unwrap() = None;
}
_ => {}
},
default_action_chooser, default_action_chooser,
compositor_token.clone(), compositor_token.clone(),
log.clone(), log.clone(),
@ -60,7 +71,13 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log
let (mut seat, _) = Seat::new(display, "winit".into(), compositor_token.clone(), log.clone()); let (mut seat, _) = Seat::new(display, "winit".into(), compositor_token.clone(), log.clone());
let pointer = seat.add_pointer(compositor_token.clone(), |_| {}); let cursor_status = Arc::new(Mutex::new(CursorImageStatus::Default));
let cursor_status2 = cursor_status.clone();
let pointer = seat.add_pointer(compositor_token.clone(), move |new_status| {
// TODO: hide winit system cursor when relevant
*cursor_status2.lock().unwrap() = new_status
});
let keyboard = seat let keyboard = seat
.add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| { .add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| {
@ -96,6 +113,8 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log
refresh: 60_000, refresh: 60_000,
}); });
let pointer_location = Rc::new(RefCell::new((0.0, 0.0)));
input.set_handler(AnvilInputHandler::new( input.set_handler(AnvilInputHandler::new(
log.clone(), log.clone(),
pointer, pointer,
@ -103,7 +122,7 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log
window_map.clone(), window_map.clone(),
(0, 0), (0, 0),
running.clone(), running.clone(),
Rc::new(RefCell::new((0.0, 0.0))), pointer_location.clone(),
)); ));
info!(log, "Initialization completed, starting the main loop."); info!(log, "Initialization completed, starting the main loop.");
@ -111,7 +130,46 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log
loop { loop {
input.dispatch_new_events().unwrap(); input.dispatch_new_events().unwrap();
drawer.draw_windows(&*window_map.borrow(), compositor_token, &log); // drawing logic
{
use glium::Surface;
let mut frame = drawer.draw();
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
// draw the windows
drawer.draw_windows(&mut frame, &*window_map.borrow(), compositor_token);
let (x, y) = *pointer_location.borrow();
// draw the dnd icon if any
{
let guard = dnd_icon.lock().unwrap();
if let Some(ref surface) = *guard {
if surface.is_alive() {
drawer.draw_dnd_icon(&mut frame, surface, (x as i32, y as i32), compositor_token);
}
}
}
// draw the cursor as relevant
{
let mut guard = cursor_status.lock().unwrap();
// reset the cursor if the surface is no longer alive
let mut reset = false;
if let CursorImageStatus::Image(ref surface) = *guard {
reset = !surface.is_alive();
}
if reset {
*guard = CursorImageStatus::Default;
}
// draw as relevant
if let CursorImageStatus::Image(ref surface) = *guard {
drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), compositor_token);
}
}
if let Err(err) = frame.finish() {
error!(log, "Error during rendering: {:?}", err);
}
}
event_loop event_loop
.dispatch(Some(::std::time::Duration::from_millis(16)), &mut ()) .dispatch(Some(::std::time::Duration::from_millis(16)), &mut ())

View File

@ -17,7 +17,7 @@ pub struct CursorImageRole {
} }
/// Possible status of a cursor as requested by clients /// Possible status of a cursor as requested by clients
#[derive(Clone)] #[derive(Clone, PartialEq)]
pub enum CursorImageStatus { pub enum CursorImageStatus {
/// The cursor should be hidden /// The cursor should be hidden
Hidden, Hidden,