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::{
compositor::{roles::Role, SubsurfaceRole, TraversalAction},
data_device::DnDIconRole,
seat::CursorImageRole,
shm::with_buffer_contents as shm_buffer_contents,
},
wayland_server::{protocol::wl_buffer, Resource},
wayland_server::{
protocol::{wl_buffer, wl_surface},
Resource,
},
};
use shaders;
@ -284,20 +289,16 @@ pub struct TextureMetadata {
}
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
pub fn draw_windows(&self, window_map: &MyWindowMap, compositor_token: MyCompositorToken, log: &Logger) {
let mut frame = self.draw();
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
// redraw the frame, in a simple but inneficient way
{
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
window_map.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn
fn draw_surface_tree(
&self,
frame: &mut Frame,
root: &Resource<wl_surface::WlSurface>,
location: (i32, i32),
compositor_token: MyCompositorToken,
screen_dimensions: (u32, u32),
) {
compositor_token
.with_surface_tree_upward(
wl_surface,
initial_place,
|_surface, attributes, role, &(mut x, mut y)| {
.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() {
@ -315,7 +316,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
y += subdata.location.1;
}
self.render_texture(
&mut frame,
frame,
&metadata.texture,
metadata.fragment,
metadata.y_inverted,
@ -325,13 +326,11 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
::glium::Blend {
color: ::glium::BlendingFunction::Addition {
source: ::glium::LinearBlendingFactor::One,
destination:
::glium::LinearBlendingFactor::OneMinusSourceAlpha,
destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha,
},
alpha: ::glium::BlendingFunction::Addition {
source: ::glium::LinearBlendingFactor::One,
destination:
::glium::LinearBlendingFactor::OneMinusSourceAlpha,
destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha,
},
..Default::default()
},
@ -341,14 +340,69 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
},
)
})
.unwrap();
}
pub fn draw_windows(
&self,
frame: &mut Frame,
window_map: &MyWindowMap,
compositor_token: MyCompositorToken,
) {
// redraw the frame, in a simple but inneficient way
{
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
window_map.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn
self.draw_surface_tree(
frame,
&wl_surface,
initial_place,
compositor_token,
screen_dimensions,
);
}
});
}
if let Err(err) = frame.finish() {
error!(log, "Error during rendering: {:?}", err);
}
pub fn draw_cursor(
&self,
frame: &mut Frame,
surface: &Resource<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,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
Arc, Mutex,
},
};
@ -43,9 +43,9 @@ use smithay::{
input::Libinput,
wayland::{
compositor::CompositorToken,
data_device::{default_action_chooser, init_data_device, set_data_device_focus},
data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
output::{Mode, Output, PhysicalProperties},
seat::{Seat, XkbConfig},
seat::{CursorImageStatus, Seat, XkbConfig},
shm::init_shm_global,
},
wayland_server::{
@ -53,8 +53,8 @@ use smithay::{
generic::{EventedFd, Generic},
EventLoop, LoopHandle, Source,
},
protocol::wl_output,
Display,
protocol::{wl_output, wl_surface},
Display, Resource,
},
};
@ -101,6 +101,8 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
let running = Arc::new(AtomicBool::new(true));
let pointer_location = Rc::new(RefCell::new((0.0, 0.0)));
let cursor_status = Arc::new(Mutex::new(CursorImageStatus::Default));
let dnd_icon = Arc::new(Mutex::new(None));
/*
* Initialize the udev backend
@ -124,6 +126,8 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
window_map: window_map.clone(),
pointer_location: pointer_location.clone(),
pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(),
cursor_status: cursor_status.clone(),
dnd_icon: dnd_icon.clone(),
loop_handle: event_loop.handle(),
notifier: udev_notifier,
logger: log.clone(),
@ -136,9 +140,18 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
/*
* Initialize wayland clipboard
*/
init_data_device(
&mut display.borrow_mut(),
|_| {},
move |event| match event {
DataDeviceEvent::DnDStarted { icon, .. } => {
*dnd_icon.lock().unwrap() = icon;
}
DataDeviceEvent::DnDDropped => {
*dnd_icon.lock().unwrap() = None;
}
_ => {}
},
default_action_chooser,
compositor_token.clone(),
log.clone(),
@ -154,7 +167,9 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
log.clone(),
);
let pointer = w_seat.add_pointer(compositor_token.clone(), |_| {});
let pointer = w_seat.add_pointer(compositor_token.clone(), move |new_status| {
*cursor_status.lock().unwrap() = new_status;
});
let keyboard = w_seat
.add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| {
set_data_device_focus(seat, focus.and_then(|s| s.client()))
@ -271,6 +286,8 @@ struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> {
window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Rc<RefCell<(f64, f64)>>,
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>,
notifier: S,
logger: ::slog::Logger,
@ -410,6 +427,8 @@ impl<S: SessionNotifier, Data: 'static> UdevHandler for UdevHandlerImpl<S, Data>
backends: backends.clone(),
window_map: self.window_map.clone(),
pointer_location: self.pointer_location.clone(),
cursor_status: self.cursor_status.clone(),
dnd_icon: self.dnd_icon.clone(),
logger: self.logger.clone(),
});
@ -504,6 +523,8 @@ pub struct DrmHandlerImpl {
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<RenderSurface>>>>,
window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Rc<RefCell<(f64, f64)>>,
cursor_status: Arc<Mutex<CursorImageStatus>>,
dnd_icon: Arc<Mutex<Option<Resource<wl_surface::WlSurface>>>>,
logger: ::slog::Logger,
}
@ -520,7 +541,44 @@ impl DeviceHandler for DrmHandlerImpl {
}
// and draw in sync with our monitor
drawer.draw_windows(&*self.window_map.borrow(), self.compositor_token, &self.logger);
let mut frame = drawer.draw();
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
// draw the surfaces
drawer.draw_windows(&mut frame, &*self.window_map.borrow(), self.compositor_token);
let (x, y) = *self.pointer_location.borrow();
// draw the dnd icon if applicable
{
let guard = self.dnd_icon.lock().unwrap();
if let Some(ref surface) = *guard {
if surface.is_alive() {
drawer.draw_dnd_icon(
&mut frame,
surface,
(x as i32, y as i32),
self.compositor_token,
);
}
}
}
// draw the cursor as relevant
{
let mut guard = self.cursor_status.lock().unwrap();
// reset the cursor if the surface is no longer alive
let mut reset = false;
if let CursorImageStatus::Image(ref surface) = *guard {
reset = !surface.is_alive();
}
if reset {
*guard = CursorImageStatus::Default;
}
if let CursorImageStatus::Image(ref surface) = *guard {
drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), self.compositor_token);
}
}
if let Err(err) = frame.finish() {
error!(self.logger, "Error during rendering: {:?}", err);
}
}
}

View File

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

View File

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