Merge pull request #88 from Smithay/wayland_rs_20

Upgrade to wayland-rs 0.20
This commit is contained in:
Victor Berger 2018-04-24 14:45:17 +02:00 committed by GitHub
commit 60fc47917d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 4950 additions and 3745 deletions

View File

@ -1,10 +1 @@
error_on_line_overflow = false
fn_args_density = "Compressed"
fn_args_layout = "Visual"
fn_arg_intent = "Tabbed"
reorder_imports = true
reorder_imported_names = true
report_todo = "Never"
report_fixme = "Never"
use_try_shorthand = true
max_width = 110

View File

@ -8,6 +8,10 @@ rust:
sudo: required
branches:
only:
- master
dist: trusty
# We cannot cache .vagga, because we actually do not have read permissions

View File

@ -7,15 +7,15 @@ description = "Smithay is a library for writing wayland compositors."
repository = "https://github.com/Smithay/smithay"
[dependencies]
wayland-server = "0.14.0"
wayland-sys = "0.14.0"
nix = "0.9.0"
wayland-server = "0.20.1"
wayland-sys = "0.20.1"
nix = "0.10.0"
xkbcommon = "0.2.1"
tempfile = "2.1.5"
slog = { version = "2.1.1" }
slog-stdlog = "3.0.2"
libloading = "0.4.0"
wayland-client = { version = "0.12.5", optional = true }
wayland-client = { version = "0.20.1", optional = true }
winit = { version = "0.10.0", optional = true }
drm = { version = "^0.3.1", optional = true }
gbm = { version = "^0.4.0", optional = true, default-features = false, features = ["drm-support"] }
@ -24,7 +24,7 @@ input = { version = "0.4.0", optional = true }
udev = { version = "0.2.0", optional = true }
dbus = { version = "0.6.1", optional = true }
systemd = { version = "^0.2.0", optional = true }
wayland-protocols = { version = "0.14.0", features = ["unstable_protocols", "server"] }
wayland-protocols = { version = "0.20.1", features = ["unstable_protocols", "server"] }
image = "0.17.0"
error-chain = "0.11.0"
lazy_static = "1.0.0"
@ -38,7 +38,7 @@ slog-async = "2.2"
rand = "0.3"
[features]
default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium", "backend_session_logind"]
default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium"]
backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
backend_drm = ["drm", "gbm"]
backend_libinput = ["input"]
@ -47,3 +47,4 @@ backend_session_udev = ["udev", "backend_session"]
backend_session_logind = ["dbus", "systemd", "backend_session"]
backend_udev = ["udev", "backend_drm", "backend_session_udev"]
renderer_glium = ["glium"]

View File

@ -34,7 +34,6 @@ use std::os::unix::io::AsRawFd;
use std::os::unix::io::RawFd;
use std::rc::Rc;
use std::time::Duration;
use wayland_server::EventLoopHandle;
#[derive(Debug)]
pub struct Card(File);
@ -56,7 +55,7 @@ fn main() {
);
// Initialize the wayland server
let (mut display, mut event_loop) = wayland_server::create_display();
let (mut display, mut event_loop) = wayland_server::Display::new();
/*
* Initialize the drm backend
@ -124,10 +123,10 @@ fn main() {
* Initialize the globals
*/
init_shm_global(&mut event_loop, vec![], log.clone());
init_shm_global(&mut display, event_loop.token(), vec![], log.clone());
let (compositor_token, _shell_state_token, window_map) =
init_shell(&mut event_loop, log.clone(), egl_display.clone());
let (compositor_token, _, _, window_map) =
init_shell(&mut display, event_loop.token(), log.clone(), egl_display);
/*
* Add a listening socket:
@ -139,7 +138,7 @@ fn main() {
* Register the DrmDevice on the EventLoop
*/
let _source = drm_device_bind(
&mut event_loop,
&event_loop.token(),
device,
DrmHandlerImpl {
compositor_token,
@ -159,7 +158,7 @@ fn main() {
}
pub struct DrmHandlerImpl {
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
compositor_token: CompositorToken<SurfaceData, Roles>,
window_map: Rc<RefCell<MyWindowMap>>,
drawer: GliumDrawer<DrmBackend<Card>>,
logger: ::slog::Logger,
@ -167,8 +166,11 @@ pub struct DrmHandlerImpl {
impl DrmHandler<Card> for DrmHandlerImpl {
fn ready(
&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<Card>, _crtc: crtc::Handle,
_frame: u32, _duration: Duration,
&mut self,
_device: &mut DrmDevice<Card>,
_crtc: crtc::Handle,
_frame: u32,
_duration: Duration,
) {
let mut frame = self.drawer.draw();
frame.clear_color(0.8, 0.8, 0.9, 1.0);
@ -215,8 +217,8 @@ impl DrmHandler<Card> for DrmHandlerImpl {
if let Some(ref texture) = attributes.user_data.texture {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
x += subdata.location.0;
y += subdata.location.1;
}
info!(self.logger, "Render window");
self.drawer.render_texture(
@ -248,7 +250,7 @@ impl DrmHandler<Card> for DrmHandlerImpl {
frame.finish().unwrap();
}
fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<Card>, error: DrmError) {
fn error(&mut self, _device: &mut DrmDevice<Card>, error: DrmError) {
panic!("{:?}", error);
}
}

View File

@ -135,8 +135,13 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
}
pub fn render_texture(
&self, target: &mut glium::Frame, texture: &Texture2d, y_inverted: bool,
surface_dimensions: (u32, u32), surface_location: (i32, i32), screen_size: (u32, u32),
&self,
target: &mut glium::Frame,
texture: &Texture2d,
y_inverted: bool,
surface_dimensions: (u32, u32),
surface_location: (i32, i32),
screen_size: (u32, u32),
blending: glium::Blend,
) {
let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32);

View File

@ -1,18 +1,21 @@
use super::WindowMap;
use super::{SurfaceKind, WindowMap};
use glium::texture::Texture2d;
use rand;
use smithay::backend::graphics::egl::wayland::{BufferAccessError, Format};
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages};
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes,
SurfaceUserImplementation};
use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole,
ShellSurfaceUserImplementation, ToplevelConfigure};
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent};
use smithay::wayland::shell::xdg::{xdg_shell_init, PopupConfigure, ShellState as XdgShellState,
ToplevelConfigure, XdgRequest, XdgSurfaceRole};
use smithay::wayland::shell::legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState,
ShellSurfaceKind, ShellSurfaceRole};
use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents;
use std::cell::RefCell;
use std::rc::Rc;
use wayland_server::{EventLoop, StateToken};
use std::sync::{Arc, Mutex};
use wayland_server::{Display, LoopToken, Resource};
use wayland_server::protocol::{wl_buffer, wl_callback, wl_shell_surface, wl_surface};
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
define_roles!(Roles => [ XdgSurface, XdgSurfaceRole ] [ ShellSurface, ShellSurfaceRole<()>] );
#[derive(Default)]
pub struct SurfaceData {
@ -25,110 +28,62 @@ pub enum Buffer {
Shm { data: Vec<u8>, size: (u32, u32) },
}
pub fn surface_implementation(
) -> SurfaceUserImplementation<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>> {
SurfaceUserImplementation {
commit: |_, display, surface, token| {
// we retrieve the contents of the associated buffer and copy it
token.with_surface_data(surface, |attributes| {
match attributes.buffer.take() {
Some(Some((buffer, (_x, _y)))) => {
// we ignore hotspot coordinates in this simple example
match if let Some(display) = display.borrow().as_ref() {
display.egl_buffer_contents(buffer)
} else {
Err(BufferAccessError::NotManaged(buffer))
} {
Ok(images) => {
match images.format {
Format::RGB => {}
Format::RGBA => {}
_ => {
// we don't handle the more complex formats here.
attributes.user_data.buffer = None;
attributes.user_data.texture = None;
return;
}
};
fn surface_commit(
surface: &Resource<wl_surface::WlSurface>,
token: CompositorToken<SurfaceData, Roles>,
display: &RefCell<Option<EGLDisplay>>,
) {
// we retrieve the contents of the associated buffer and copy it
token.with_surface_data(surface, |attributes| {
match attributes.buffer.take() {
Some(Some((buffer, (_x, _y)))) => {
// we ignore hotspot coordinates in this simple example
match if let Some(display) = display.borrow().as_ref() {
display.egl_buffer_contents(buffer)
} else {
Err(BufferAccessError::NotManaged(buffer))
} {
Ok(images) => {
match images.format {
Format::RGB => {}
Format::RGBA => {}
_ => {
// we don't handle the more complex formats here.
attributes.user_data.buffer = None;
attributes.user_data.texture = None;
attributes.user_data.buffer = Some(Buffer::Egl { images });
return;
}
Err(BufferAccessError::NotManaged(buffer)) => {
shm_buffer_contents(&buffer, |slice, data| {
let offset = data.offset as usize;
let stride = data.stride as usize;
let width = data.width as usize;
let height = data.height as usize;
let mut new_vec = Vec::with_capacity(width * height * 4);
for i in 0..height {
new_vec
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
}
attributes.user_data.texture = None;
attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) });
}).expect("Got EGL buffer with no set EGLDisplay. You need to unbind your EGLContexts before dropping them!");
buffer.release();
}
Err(err) => panic!("EGL error: {}", err),
}
}
Some(None) => {
// erase the contents
attributes.user_data.buffer = None;
};
attributes.user_data.texture = None;
attributes.user_data.buffer = Some(Buffer::Egl { images });
}
None => {}
Err(BufferAccessError::NotManaged(buffer)) => {
shm_buffer_contents(&buffer, |slice, data| {
let offset = data.offset as usize;
let stride = data.stride as usize;
let width = data.width as usize;
let height = data.height as usize;
let mut new_vec = Vec::with_capacity(width * height * 4);
for i in 0..height {
new_vec
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
}
attributes.user_data.texture = None;
attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) });
}).expect("Got EGL buffer with no set EGLDisplay. You need to unbind your EGLContexts before dropping them!");
buffer.send(wl_buffer::Event::Release);
}
Err(err) => panic!("EGL error: {}", err),
}
});
},
frame: |_, _, _, callback, _| {
callback.done(0);
},
}
}
pub struct ShellIData<F> {
pub token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
pub window_map: Rc<RefCell<super::WindowMap<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, (), F>>>,
}
pub fn shell_implementation<F>(
) -> ShellSurfaceUserImplementation<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ShellIData<F>, ()>
where
F: Fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
{
ShellSurfaceUserImplementation {
new_client: |_, _, _| {},
client_pong: |_, _, _| {},
new_toplevel: |_, idata, toplevel| {
// place the window at a random location in the [0;300]x[0;300] square
use rand::distributions::{IndependentSample, Range};
let range = Range::new(0, 300);
let mut rng = rand::thread_rng();
let x = range.ind_sample(&mut rng);
let y = range.ind_sample(&mut rng);
idata.window_map.borrow_mut().insert(toplevel, (x, y));
ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
}
},
new_popup: |_, _, _| PopupConfigure {
size: (10, 10),
position: (10, 10),
serial: 42,
},
move_: |_, _, _, _, _| {},
resize: |_, _, _, _, _, _| {},
grab: |_, _, _, _, _| {},
change_display_state: |_, _, _, _, _, _, _| ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
},
show_window_menu: |_, _, _, _, _, _, _| {},
}
Some(None) => {
// erase the contents
attributes.user_data.buffer = None;
attributes.user_data.texture = None;
}
None => {}
}
});
}
fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
@ -143,38 +98,104 @@ fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
.map(|(x, y)| (x as i32, y as i32))
}
pub type MyWindowMap = WindowMap<
SurfaceData,
Roles,
Rc<RefCell<Option<EGLDisplay>>>,
(),
fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
>;
pub type MyWindowMap =
WindowMap<SurfaceData, Roles, (), (), fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>>;
pub fn init_shell(
evl: &mut EventLoop, log: ::slog::Logger, data: Rc<RefCell<Option<EGLDisplay>>>
display: &mut Display,
looptoken: LoopToken,
log: ::slog::Logger,
egl_display: Rc<RefCell<Option<EGLDisplay>>>,
) -> (
CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
StateToken<ShellState<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ()>>,
CompositorToken<SurfaceData, Roles>,
Arc<Mutex<XdgShellState<SurfaceData, Roles, ()>>>,
Arc<Mutex<WlShellState<SurfaceData, Roles, ()>>>,
Rc<RefCell<MyWindowMap>>,
) {
let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), data, log.clone());
let window_map = Rc::new(RefCell::new(WindowMap::<_, _, _, (), _>::new(
compositor_token,
get_size as _,
)));
let (shell_state_token, _, _) = shell_init(
evl,
compositor_token,
shell_implementation(),
ShellIData {
token: compositor_token,
window_map: window_map.clone(),
// Create the compositor
let c_egl_display = egl_display.clone();
let (compositor_token, _, _) = compositor_init(
display,
looptoken.clone(),
move |request, (surface, ctoken)| match request {
SurfaceEvent::Commit => surface_commit(&surface, ctoken, &*c_egl_display),
SurfaceEvent::Frame { callback } => callback
.implement(|e, _| match e {}, None::<fn(_, _)>)
.send(wl_callback::Event::Done { callback_data: 0 }),
},
log.clone(),
);
(compositor_token, shell_state_token, window_map)
// Init a window map, to track the location of our windows
let window_map = Rc::new(RefCell::new(WindowMap::<_, _, (), (), _>::new(
compositor_token,
get_size as _,
)));
// init the xdg_shell
let xdg_window_map = window_map.clone();
let (xdg_shell_state, _, _) = xdg_shell_init(
display,
looptoken.clone(),
compositor_token.clone(),
move |shell_event, ()| match shell_event {
XdgRequest::NewToplevel { surface } => {
// place the window at a random location in the [0;300]x[0;300] square
use rand::distributions::{IndependentSample, Range};
let range = Range::new(0, 300);
let mut rng = rand::thread_rng();
let x = range.ind_sample(&mut rng);
let y = range.ind_sample(&mut rng);
surface.send_configure(ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
});
xdg_window_map
.borrow_mut()
.insert(SurfaceKind::Xdg(surface), (x, y));
}
XdgRequest::NewPopup { surface } => surface.send_configure(PopupConfigure {
size: (10, 10),
position: (10, 10),
serial: 42,
}),
_ => (),
},
log.clone(),
);
// init the wl_shell
let shell_window_map = window_map.clone();
let (wl_shell_state, _) = wl_shell_init(
display,
looptoken,
compositor_token.clone(),
move |req: ShellRequest<_, _, ()>, ()| match req {
ShellRequest::SetKind {
surface,
kind: ShellSurfaceKind::Toplevel,
} => {
// place the window at a random location in the [0;300]x[0;300] square
use rand::distributions::{IndependentSample, Range};
let range = Range::new(0, 300);
let mut rng = rand::thread_rng();
let x = range.ind_sample(&mut rng);
let y = range.ind_sample(&mut rng);
surface.send_configure((0, 0), wl_shell_surface::Resize::None);
shell_window_map
.borrow_mut()
.insert(SurfaceKind::Wl(surface), (x, y));
}
_ => (),
},
log.clone(),
);
(
compositor_token,
xdg_shell_state,
wl_shell_state,
window_map,
)
}

View File

@ -4,4 +4,4 @@ mod window_map;
pub use self::glium::GliumDrawer;
pub use self::implementations::*;
pub use self::window_map::WindowMap;
pub use self::window_map::{Kind as SurfaceKind, WindowMap};

View File

@ -1,27 +1,57 @@
use smithay::utils::Rectangle;
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, SurfaceAttributes, TraversalAction};
use smithay::wayland::compositor::roles::Role;
use smithay::wayland::shell::{ShellSurfaceRole, ToplevelSurface};
use smithay::wayland::shell::xdg::{ToplevelSurface, XdgSurfaceRole};
use smithay::wayland::shell::legacy::{ShellSurface, ShellSurfaceRole};
use wayland_server::Resource;
use wayland_server::protocol::wl_surface;
struct Window<U, R, CID, SD> {
location: (i32, i32),
surface: Rectangle,
toplevel: ToplevelSurface<U, R, CID, SD>,
pub enum Kind<U, R, SD, D> {
Xdg(ToplevelSurface<U, R, SD>),
Wl(ShellSurface<U, R, D>),
}
impl<U, R, CID, SD> Window<U, R, CID, SD>
impl<U, R, SD, D> Kind<U, R, SD, D>
where
U: 'static,
R: Role<SubsurfaceRole> + Role<ShellSurfaceRole> + 'static,
CID: 'static,
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + 'static,
SD: 'static,
D: 'static,
{
pub fn alive(&self) -> bool {
match *self {
Kind::Xdg(ref t) => t.alive(),
Kind::Wl(ref t) => t.alive(),
}
}
pub fn get_surface(&self) -> Option<&Resource<wl_surface::WlSurface>> {
match *self {
Kind::Xdg(ref t) => t.get_surface(),
Kind::Wl(ref t) => t.get_surface(),
}
}
}
struct Window<U, R, SD, D> {
location: (i32, i32),
surface: Rectangle,
toplevel: Kind<U, R, SD, D>,
}
impl<U, R, SD, D> Window<U, R, SD, D>
where
U: 'static,
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + 'static,
SD: 'static,
D: 'static,
{
// Find the topmost surface under this point if any and the location of this point in the surface
fn matching<F>(
&self, point: (f64, f64), ctoken: CompositorToken<U, R, CID>, get_size: F
) -> Option<(wl_surface::WlSurface, (f64, f64))>
&self,
point: (f64, f64),
ctoken: CompositorToken<U, R>,
get_size: F,
) -> Option<(Resource<wl_surface::WlSurface>, (f64, f64))>
where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
{
@ -37,8 +67,8 @@ where
|wl_surface, attributes, role, &(mut x, mut y)| {
if let Some((w, h)) = get_size(attributes) {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
x += subdata.location.0;
y += subdata.location.1;
}
let my_rect = Rectangle {
x,
@ -47,9 +77,10 @@ where
height: h,
};
if my_rect.contains((point.0 as i32, point.1 as i32)) {
found = wl_surface
.clone()
.map(|s| (s, (point.0 - my_rect.x as f64, point.1 - my_rect.y as f64)));
found = Some((
wl_surface.clone(),
(point.0 - my_rect.x as f64, point.1 - my_rect.y as f64),
));
TraversalAction::Break
} else {
TraversalAction::DoChildren((x, y))
@ -63,7 +94,7 @@ where
found
}
fn self_update<F>(&mut self, ctoken: CompositorToken<U, R, CID>, get_size: F)
fn self_update<F>(&mut self, ctoken: CompositorToken<U, R>, get_size: F)
where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
{
@ -76,8 +107,8 @@ where
|_, attributes, role, &(mut x, mut y)| {
if let Some((w, h)) = get_size(attributes) {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
x += subdata.location.0;
y += subdata.location.1;
}
// update the bounding box
if x < min_x {
@ -108,21 +139,21 @@ where
}
}
pub struct WindowMap<U, R, CID, SD, F> {
ctoken: CompositorToken<U, R, CID>,
windows: Vec<Window<U, R, CID, SD>>,
pub struct WindowMap<U, R, SD, D, F> {
ctoken: CompositorToken<U, R>,
windows: Vec<Window<U, R, SD, D>>,
get_size: F,
}
impl<U, R, CID, SD, F> WindowMap<U, R, CID, SD, F>
impl<U, R, SD, D, F> WindowMap<U, R, SD, D, F>
where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
U: 'static,
R: Role<SubsurfaceRole> + Role<ShellSurfaceRole> + 'static,
CID: 'static,
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + 'static,
SD: 'static,
D: 'static,
{
pub fn new(ctoken: CompositorToken<U, R, CID>, get_size: F) -> WindowMap<U, R, CID, SD, F> {
pub fn new(ctoken: CompositorToken<U, R>, get_size: F) -> WindowMap<U, R, D, SD, F> {
WindowMap {
ctoken: ctoken,
windows: Vec::new(),
@ -130,7 +161,7 @@ where
}
}
pub fn insert(&mut self, toplevel: ToplevelSurface<U, R, CID, SD>, location: (i32, i32)) {
pub fn insert(&mut self, toplevel: Kind<U, R, SD, D>, location: (i32, i32)) {
let mut window = Window {
location: location,
surface: Rectangle {
@ -145,7 +176,10 @@ where
self.windows.insert(0, window);
}
pub fn get_surface_under(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> {
pub fn get_surface_under(
&self,
point: (f64, f64),
) -> Option<(Resource<wl_surface::WlSurface>, (f64, f64))> {
for w in &self.windows {
if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) {
return Some(surface);
@ -155,8 +189,9 @@ where
}
pub fn get_surface_and_bring_to_top(
&mut self, point: (f64, f64)
) -> Option<(wl_surface::WlSurface, (f64, f64))> {
&mut self,
point: (f64, f64),
) -> Option<(Resource<wl_surface::WlSurface>, (f64, f64))> {
let mut found = None;
for (i, w) in self.windows.iter().enumerate() {
if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) {
@ -175,7 +210,7 @@ where
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
where
Func: FnMut(&ToplevelSurface<U, R, CID, SD>, (i32, i32)),
Func: FnMut(&Kind<U, R, SD, D>, (i32, i32)),
{
for w in self.windows.iter().rev() {
f(&w.toplevel, w.location)

View File

@ -53,9 +53,9 @@ use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use wayland_server::{Display, EventLoopHandle};
use wayland_server::Display;
use wayland_server::commons::downcast_impl;
use wayland_server::protocol::{wl_output, wl_pointer};
use wayland_server::sources::EventSource;
use xkbcommon::xkb::keysyms as xkb;
struct LibinputInputHandler {
@ -78,18 +78,16 @@ impl LibinputInputHandler {
}
impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
fn on_seat_created(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) {
fn on_seat_created(&mut self, _: &input::Seat) {
/* we just create a single static one */
}
fn on_seat_destroyed(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) {
fn on_seat_destroyed(&mut self, _: &input::Seat) {
/* we just create a single static one */
}
fn on_seat_changed(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) {
fn on_seat_changed(&mut self, _: &input::Seat) {
/* we just create a single static one */
}
fn on_keyboard_key(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent
) {
fn on_keyboard_key(&mut self, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent) {
let keycode = evt.key();
let state = evt.state();
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
@ -100,8 +98,9 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
let running = &self.running;
let mut session = &mut self.session;
let log = &self.log;
let time = Event::time(&evt);
self.keyboard
.input(keycode, state, serial, move |modifiers, keysym| {
.input(keycode, state, serial, time, move |modifiers, keysym| {
debug!(log, "keysym"; "state" => format!("{:?}", state), "mods" => format!("{:?}", modifiers), "keysym" => xkbcommon::xkb::keysym_get_name(keysym));
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace
&& state == KeyState::Pressed
@ -132,9 +131,7 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
}
});
}
fn on_pointer_move(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerMotionEvent
) {
fn on_pointer_move(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionEvent) {
let (x, y) = (evt.dx(), evt.dy());
let serial = self.next_serial();
let mut location = self.pointer_location.borrow_mut();
@ -149,10 +146,7 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
evt.time(),
);
}
fn on_pointer_move_absolute(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat,
evt: event::pointer::PointerMotionAbsoluteEvent,
) {
fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) {
let (x, y) = (
evt.absolute_x_transformed(self.screen_size.0),
evt.absolute_y_transformed(self.screen_size.1),
@ -166,9 +160,7 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
evt.time(),
);
}
fn on_pointer_button(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerButtonEvent
) {
fn on_pointer_button(&mut self, _: &input::Seat, evt: event::pointer::PointerButtonEvent) {
let serial = self.next_serial();
let button = evt.button();
let state = match evt.state() {
@ -185,9 +177,7 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
};
self.pointer.button(button, state, serial, evt.time());
}
fn on_pointer_axis(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerAxisEvent
) {
fn on_pointer_axis(&mut self, _: &input::Seat, evt: event::pointer::PointerAxisEvent) {
let source = match evt.source() {
input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous,
input::AxisSource::Finger => wayland_server::protocol::wl_pointer::AxisSource::Finger,
@ -244,30 +234,22 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
event.done();
}
}
fn on_touch_down(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchDownEvent
) {
fn on_touch_down(&mut self, _: &input::Seat, _: event::touch::TouchDownEvent) {
/* not done in this example */
}
fn on_touch_motion(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchMotionEvent
) {
fn on_touch_motion(&mut self, _: &input::Seat, _: event::touch::TouchMotionEvent) {
/* not done in this example */
}
fn on_touch_up(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchUpEvent) {
fn on_touch_up(&mut self, _: &input::Seat, _: event::touch::TouchUpEvent) {
/* not done in this example */
}
fn on_touch_cancel(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchCancelEvent
) {
fn on_touch_cancel(&mut self, _: &input::Seat, _: event::touch::TouchCancelEvent) {
/* not done in this example */
}
fn on_touch_frame(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchFrameEvent
) {
fn on_touch_frame(&mut self, _: &input::Seat, _: event::touch::TouchFrameEvent) {
/* not done in this example */
}
fn on_input_config_changed(&mut self, _evlh: &mut EventLoopHandle, _: &mut [LibinputDevice]) {
fn on_input_config_changed(&mut self, _: &mut [LibinputDevice]) {
/* not done in this example */
}
}
@ -284,7 +266,7 @@ fn main() {
);
// Initialize the wayland server
let (mut display, mut event_loop) = wayland_server::create_display();
let (mut display, mut event_loop) = wayland_server::Display::new();
/*
* Add a listening socket
@ -292,15 +274,24 @@ fn main() {
let name = display.add_socket_auto().unwrap().into_string().unwrap();
println!("Listening on socket: {}", name);
env::set_var("WAYLAND_DISPLAY", name);
let display = Rc::new(display);
let display = Rc::new(RefCell::new(display));
/*
* Initialize the compositor
*/
init_shm_global(&mut event_loop, vec![], log.clone());
init_shm_global(
&mut display.borrow_mut(),
event_loop.token(),
vec![],
log.clone(),
);
let (compositor_token, _shell_state_token, window_map) =
init_shell(&mut event_loop, log.clone(), active_egl_context.clone());
let (compositor_token, _, _, window_map) = init_shell(
&mut display.borrow_mut(),
event_loop.token(),
log.clone(),
active_egl_context.clone(),
);
/*
* Initialize session
@ -321,7 +312,7 @@ fn main() {
let bytes = include_bytes!("resources/cursor2.rgba");
let mut udev_backend = UdevBackend::new(
&mut event_loop,
event_loop.token(),
&context,
session.clone(),
UdevHandlerImpl {
@ -340,17 +331,21 @@ fn main() {
let udev_session_id = notifier.register(&mut udev_backend);
let (seat_token, _) = Seat::new(&mut event_loop, session.seat().into(), log.clone());
let (mut w_seat, _) = Seat::new(
&mut display.borrow_mut(),
event_loop.token(),
session.seat().into(),
log.clone(),
);
let pointer = event_loop.state().get_mut(&seat_token).add_pointer();
let keyboard = event_loop
.state()
.get_mut(&seat_token)
let pointer = w_seat.add_pointer();
let keyboard = w_seat
.add_keyboard("", "", "", None, 1000, 500)
.expect("Failed to initialize the keyboard");
let (output_token, _output_global) = Output::new(
&mut event_loop,
let (output, _output_global) = Output::new(
&mut display.borrow_mut(),
event_loop.token(),
"Drm".into(),
PhysicalProperties {
width: 0,
@ -363,26 +358,20 @@ fn main() {
);
let (w, h) = (1920, 1080); // Hardcode full-hd res
event_loop
.state()
.get_mut(&output_token)
.change_current_state(
Some(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
}),
None,
None,
);
event_loop
.state()
.get_mut(&output_token)
.set_preferred(Mode {
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
@ -392,28 +381,25 @@ fn main() {
let libinput_session_id = notifier.register(&mut libinput_context);
libinput_context.udev_assign_seat(&seat).unwrap();
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
libinput_backend.set_handler(
&mut event_loop,
LibinputInputHandler {
log: log.clone(),
pointer,
keyboard,
window_map: window_map.clone(),
pointer_location,
screen_size: (w, h),
serial: 0,
session: session,
running: running.clone(),
},
);
let libinput_event_source = libinput_bind(libinput_backend, &mut event_loop)
libinput_backend.set_handler(LibinputInputHandler {
log: log.clone(),
pointer,
keyboard,
window_map: window_map.clone(),
pointer_location,
screen_size: (w, h),
serial: 0,
session: session,
running: running.clone(),
});
let libinput_event_source = libinput_bind(libinput_backend, event_loop.token())
.map_err(|(err, _)| err)
.unwrap();
let session_event_source = auto_session_bind(notifier, &mut event_loop)
let session_event_source = auto_session_bind(notifier, &event_loop.token())
.map_err(|(err, _)| err)
.unwrap();
let udev_event_source = udev_backend_bind(&mut event_loop, udev_backend)
let udev_event_source = udev_backend_bind(&event_loop.token(), udev_backend)
.map_err(|(err, _)| err)
.unwrap();
@ -421,7 +407,7 @@ fn main() {
if let Err(_) = event_loop.dispatch(Some(16)) {
running.store(false, Ordering::SeqCst);
} else {
display.flush_clients();
display.borrow_mut().flush_clients();
window_map.borrow_mut().refresh();
}
}
@ -435,14 +421,19 @@ fn main() {
libinput_event_source.remove();
// destroy the udev backend freeing the drm devices
udev_event_source.remove().close(&mut event_loop)
//
// udev_event_source.remove() returns a Box<Implementation<..>>, downcast_impl
// allows us to cast it back to its original type, storing it back into its original
// variable to simplify type inference.
udev_backend = *(downcast_impl(udev_event_source.remove()).unwrap_or_else(|_| unreachable!()));
udev_backend.close();
}
struct UdevHandlerImpl {
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
compositor_token: CompositorToken<SurfaceData, Roles>,
active_egl_context: Rc<RefCell<Option<EGLDisplay>>>,
backends: HashMap<u64, Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>>,
display: Rc<Display>,
display: Rc<RefCell<Display>>,
primary_gpu: Option<PathBuf>,
window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Rc<RefCell<(f64, f64)>>,
@ -452,7 +443,8 @@ struct UdevHandlerImpl {
impl UdevHandlerImpl {
pub fn scan_connectors(
&self, device: &mut DrmDevice<SessionFdDrmDevice>
&self,
device: &mut DrmDevice<SessionFdDrmDevice>,
) -> HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>> {
// Get a set of all modesetting resource handles (excluding planes):
let res_handles = device.resource_handles().unwrap();
@ -511,12 +503,10 @@ impl UdevHandlerImpl {
}
impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
fn device_added(
&mut self, _evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>
) -> Option<DrmHandlerImpl> {
fn device_added(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) -> Option<DrmHandlerImpl> {
// init hardware acceleration on the primary gpu.
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
*self.active_egl_context.borrow_mut() = device.bind_wl_display(&*self.display).ok();
*self.active_egl_context.borrow_mut() = device.bind_wl_display(&*self.display.borrow()).ok();
}
let backends = Rc::new(RefCell::new(self.scan_connectors(device)));
@ -531,13 +521,13 @@ impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
})
}
fn device_changed(&mut self, _evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>) {
fn device_changed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
//quick and dirt, just re-init all backends
let backends = self.backends.get(&device.device_id()).unwrap();
*backends.borrow_mut() = self.scan_connectors(device);
}
fn device_removed(&mut self, _evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>) {
fn device_removed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
// drop the backends on this side
self.backends.remove(&device.device_id());
@ -547,13 +537,13 @@ impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
}
}
fn error(&mut self, _evlh: &mut EventLoopHandle, error: IoError) {
fn error(&mut self, error: IoError) {
error!(self.logger, "{:?}", error);
}
}
pub struct DrmHandlerImpl {
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
compositor_token: CompositorToken<SurfaceData, Roles>,
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>,
window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Rc<RefCell<(f64, f64)>>,
@ -562,8 +552,11 @@ pub struct DrmHandlerImpl {
impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
fn ready(
&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<SessionFdDrmDevice>,
crtc: crtc::Handle, _frame: u32, _duration: Duration,
&mut self,
_device: &mut DrmDevice<SessionFdDrmDevice>,
crtc: crtc::Handle,
_frame: u32,
_duration: Duration,
) {
if let Some(drawer) = self.backends.borrow().get(&crtc) {
{
@ -616,8 +609,8 @@ impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
if let Some(ref texture) = attributes.user_data.texture {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
x += subdata.location.0;
y += subdata.location.1;
}
info!(self.logger, "Render window");
drawer.render_texture(
@ -655,9 +648,7 @@ impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
}
}
fn error(
&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<SessionFdDrmDevice>, error: DrmError
) {
fn error(&mut self, _device: &mut DrmDevice<SessionFdDrmDevice>, error: DrmError) {
error!(self.logger, "{:?}", error);
}
}

View File

@ -26,7 +26,7 @@ use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat};
use smithay::wayland::shm::init_shm_global;
use std::cell::RefCell;
use std::rc::Rc;
use wayland_server::EventLoopHandle;
use wayland_server::Display;
use wayland_server::protocol::{wl_output, wl_pointer};
struct WinitInputHandler {
@ -46,30 +46,27 @@ impl WinitInputHandler {
}
impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
fn on_seat_created(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) {
fn on_seat_created(&mut self, _: &input::Seat) {
/* never happens with winit */
}
fn on_seat_destroyed(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) {
fn on_seat_destroyed(&mut self, _: &input::Seat) {
/* never happens with winit */
}
fn on_seat_changed(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) {
fn on_seat_changed(&mut self, _: &input::Seat) {
/* never happens with winit */
}
fn on_keyboard_key(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitKeyboardInputEvent
) {
fn on_keyboard_key(&mut self, _: &input::Seat, evt: winit::WinitKeyboardInputEvent) {
let keycode = evt.key_code();
let state = evt.state();
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
let serial = self.next_serial();
self.keyboard.input(keycode, state, serial, |_, _| true);
self.keyboard
.input(keycode, state, serial, evt.time(), |_, _| true);
}
fn on_pointer_move(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: input::UnusedEvent) {
fn on_pointer_move(&mut self, _: &input::Seat, _: input::UnusedEvent) {
/* never happens with winit */
}
fn on_pointer_move_absolute(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseMovedEvent
) {
fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: winit::WinitMouseMovedEvent) {
// on winit, mouse events are already in pixel coordinates
let (x, y) = evt.position();
self.pointer_location = (x, y);
@ -81,9 +78,7 @@ impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
evt.time(),
);
}
fn on_pointer_button(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseInputEvent
) {
fn on_pointer_button(&mut self, _: &input::Seat, evt: winit::WinitMouseInputEvent) {
let serial = self.next_serial();
let button = match evt.button() {
input::MouseButton::Left => 0x110,
@ -105,9 +100,7 @@ impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
};
self.pointer.button(button, state, serial, evt.time());
}
fn on_pointer_axis(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseWheelEvent
) {
fn on_pointer_axis(&mut self, _: &input::Seat, evt: winit::WinitMouseWheelEvent) {
let source = match evt.source() {
input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous,
input::AxisSource::Wheel => wayland_server::protocol::wl_pointer::AxisSource::Wheel,
@ -152,28 +145,22 @@ impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
event.done();
}
}
fn on_touch_down(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchStartedEvent
) {
fn on_touch_down(&mut self, _: &input::Seat, _: winit::WinitTouchStartedEvent) {
/* not done in this example */
}
fn on_touch_motion(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchMovedEvent
) {
fn on_touch_motion(&mut self, _: &input::Seat, _: winit::WinitTouchMovedEvent) {
/* not done in this example */
}
fn on_touch_up(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchEndedEvent) {
fn on_touch_up(&mut self, _: &input::Seat, _: winit::WinitTouchEndedEvent) {
/* not done in this example */
}
fn on_touch_cancel(
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchCancelledEvent
) {
fn on_touch_cancel(&mut self, _: &input::Seat, _: winit::WinitTouchCancelledEvent) {
/* not done in this example */
}
fn on_touch_frame(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: input::UnusedEvent) {
fn on_touch_frame(&mut self, _: &input::Seat, _: input::UnusedEvent) {
/* never happens with winit */
}
fn on_input_config_changed(&mut self, _evlh: &mut EventLoopHandle, _: &mut ()) {
fn on_input_config_changed(&mut self, _: &mut ()) {
/* never happens with winit */
}
}
@ -188,7 +175,7 @@ fn main() {
// Initialize a simple backend for testing
let (renderer, mut input) = winit::init(log.clone()).unwrap();
let (mut display, mut event_loop) = wayland_server::create_display();
let (mut display, mut event_loop) = wayland_server::Display::new();
let egl_display = Rc::new(RefCell::new(
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
@ -206,22 +193,25 @@ fn main() {
* Initialize the globals
*/
init_shm_global(&mut event_loop, vec![], log.clone());
init_shm_global(&mut display, event_loop.token(), vec![], log.clone());
let (compositor_token, _shell_state_token, window_map) =
init_shell(&mut event_loop, log.clone(), egl_display);
let (compositor_token, _, _, window_map) =
init_shell(&mut display, event_loop.token(), log.clone(), egl_display);
let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone());
let (mut seat, _) = Seat::new(
&mut display,
event_loop.token(),
"winit".into(),
log.clone(),
);
let pointer = event_loop.state().get_mut(&seat_token).add_pointer();
let keyboard = event_loop
.state()
.get_mut(&seat_token)
.add_keyboard("", "fr", "oss", None, 1000, 500)
let pointer = seat.add_pointer();
let keyboard = seat.add_keyboard("", "fr", "oss", None, 1000, 500)
.expect("Failed to initialize the keyboard");
let (output_token, _output_global) = Output::new(
&mut event_loop,
let (output, _) = Output::new(
&mut display,
event_loop.token(),
"Winit".into(),
PhysicalProperties {
width: 0,
@ -233,38 +223,29 @@ fn main() {
log.clone(),
);
event_loop
.state()
.get_mut(&output_token)
.change_current_state(
Some(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
}),
None,
None,
);
event_loop
.state()
.get_mut(&output_token)
.set_preferred(Mode {
output.change_current_state(
Some(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
});
input.set_handler(
&mut event_loop,
WinitInputHandler {
log: log.clone(),
pointer,
keyboard,
window_map: window_map.clone(),
pointer_location: (0.0, 0.0),
serial: 0,
},
}),
None,
None,
);
output.set_preferred(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
});
input.set_handler(WinitInputHandler {
log: log.clone(),
pointer,
keyboard,
window_map: window_map.clone(),
pointer_location: (0.0, 0.0),
serial: 0,
});
/*
* Add a listening socket:
@ -273,7 +254,7 @@ fn main() {
println!("Listening on socket: {}", name);
loop {
input.dispatch_new_events(&mut event_loop).unwrap();
input.dispatch_new_events().unwrap();
let mut frame = drawer.draw();
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
@ -320,8 +301,8 @@ fn main() {
if let Some(ref texture) = attributes.user_data.texture {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
x += subdata.location.0;
y += subdata.location.1;
}
drawer.render_texture(
&mut frame,

View File

@ -37,8 +37,11 @@ pub(crate) struct DrmBackendInternal<A: Device + 'static> {
impl<A: Device + 'static> DrmBackend<A> {
pub(crate) fn new(
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>, crtc: crtc::Handle, mode: Mode,
connectors: Vec<connector::Handle>, log: ::slog::Logger,
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
crtc: crtc::Handle,
mode: Mode,
connectors: Vec<connector::Handle>,
log: ::slog::Logger,
) -> Result<Self> {
// logger already initialized by the DrmDevice
info!(log, "Initializing DrmBackend");
@ -351,7 +354,8 @@ impl<A: Device + 'static> DrmBackendInternal<A> {
}
pub(crate) fn page_flip(
&self, fb: Option<&framebuffer::Info>
&self,
fb: Option<&framebuffer::Info>,
) -> ::std::result::Result<(), SwapBuffersError> {
trace!(self.logger, "Queueing Page flip");
@ -421,7 +425,9 @@ impl<A: Device + 'static> GraphicsBackend for DrmBackend<A> {
}
fn set_cursor_representation(
&self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)
&self,
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
hotspot: (u32, u32),
) -> Result<()> {
let (w, h) = buffer.dimensions();
debug!(self.backend.logger, "Importing cursor");

View File

@ -66,8 +66,6 @@
//! impl ControlDevice for Card {}
//!
//! # fn main() {
//! let (_display, mut event_loop) = wayland_server::create_display();
//!
//! // Open the drm device
//! let mut options = OpenOptions::new();
//! options.read(true);
@ -139,7 +137,6 @@
//! # use std::time::Duration;
//! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
//! use smithay::backend::graphics::egl::EGLGraphicsBackend;
//! use wayland_server::EventLoopHandle;
//! #
//! # #[derive(Debug)]
//! # pub struct Card(File);
@ -153,7 +150,7 @@
//! #
//! # fn main() {
//! #
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! # let (_display, mut event_loop) = wayland_server::Display::new();
//! #
//! # let mut options = OpenOptions::new();
//! # options.read(true);
@ -181,7 +178,6 @@
//! impl DrmHandler<Card> for MyDrmHandler {
//! fn ready(
//! &mut self,
//! _evlh: &mut EventLoopHandle,
//! _device: &mut DrmDevice<Card>,
//! _crtc: CrtcHandle,
//! _frame: u32,
@ -192,7 +188,6 @@
//! }
//! fn error(
//! &mut self,
//! _evlh: &mut EventLoopHandle,
//! device: &mut DrmDevice<Card>,
//! error: DrmError)
//! {
@ -203,7 +198,11 @@
//! // render something (like clear_color)
//! backend.swap_buffers().unwrap();
//!
//! let _source = drm_device_bind(&mut event_loop, device, MyDrmHandler(backend)).map_err(|(err, _)| err).unwrap();
//! let (_source, _device_rc) = drm_device_bind(
//! &event_loop.token(),
//! device,
//! MyDrmHandler(backend)
//! ).map_err(|(err, _)| err).unwrap();
//!
//! event_loop.run().unwrap();
//! # }
@ -233,8 +232,9 @@ use std::rc::{Rc, Weak};
use std::sync::{Arc, Once, ONCE_INIT};
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use wayland_server::{Display, EventLoopHandle};
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
use wayland_server::{Display, LoopToken};
use wayland_server::commons::Implementation;
use wayland_server::sources::{FdEvent, FdInterest, Source};
mod backend;
pub mod error;
@ -378,7 +378,10 @@ impl<A: ControlDevice + 'static> DrmDevice<A> {
/// Errors if initialization fails or the mode is not available on all given
/// connectors.
pub fn create_backend<I>(
&mut self, crtc: crtc::Handle, mode: Mode, connectors: I
&mut self,
crtc: crtc::Handle,
mode: Mode,
connectors: I,
) -> Result<DrmBackend<A>>
where
I: Into<Vec<connector::Handle>>,
@ -532,44 +535,66 @@ impl<A: ControlDevice + 'static> Drop for DrmDevice<A> {
pub trait DrmHandler<A: ControlDevice + 'static> {
/// The `DrmBackend` of crtc has finished swapping buffers and new frame can now
/// (and should be immediately) be rendered.
fn ready(
&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<A>, crtc: crtc::Handle, frame: u32,
duration: Duration,
);
fn ready(&mut self, device: &mut DrmDevice<A>, crtc: crtc::Handle, frame: u32, duration: Duration);
/// The `DrmDevice` has thrown an error.
///
/// The related backends are most likely *not* usable anymore and
/// the whole stack has to be recreated..
fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<A>, error: DrmError);
fn error(&mut self, device: &mut DrmDevice<A>, error: DrmError);
}
/// Bind a `DrmDevice` to an `EventLoop`,
///
/// This will cause it to recieve events and feed them into an `DrmHandler`
pub fn drm_device_bind<A, H>(
evlh: &mut EventLoopHandle, device: DrmDevice<A>, handler: H
) -> ::std::result::Result<FdEventSource<(DrmDevice<A>, H)>, (IoError, (DrmDevice<A>, H))>
token: &LoopToken,
device: DrmDevice<A>,
handler: H,
) -> ::std::result::Result<(Source<FdEvent>, Rc<RefCell<DrmDevice<A>>>), (IoError, (DrmDevice<A>, H))>
where
A: ControlDevice + 'static,
H: DrmHandler<A> + 'static,
{
let fd = device.as_raw_fd();
evlh.add_fd_event_source(
let device = Rc::new(RefCell::new(device));
match token.add_fd_event_source(
fd,
fd_event_source_implementation(),
(device, handler),
FdInterest::READ,
)
DrmFdImpl {
device: device.clone(),
handler,
},
) {
Ok(source) => Ok((source, device)),
Err((
ioerror,
DrmFdImpl {
device: device2,
handler,
},
)) => {
// make the Rc unique again
::std::mem::drop(device2);
let device = Rc::try_unwrap(device).unwrap_or_else(|_| unreachable!());
Err((ioerror, (device.into_inner(), handler)))
}
}
}
fn fd_event_source_implementation<A, H>() -> FdEventSourceImpl<(DrmDevice<A>, H)>
struct DrmFdImpl<A: ControlDevice + 'static, H> {
device: Rc<RefCell<DrmDevice<A>>>,
handler: H,
}
impl<A, H> Implementation<(), FdEvent> for DrmFdImpl<A, H>
where
A: ControlDevice + 'static,
H: DrmHandler<A> + 'static,
{
FdEventSourceImpl {
ready: |evlh, &mut (ref mut device, ref mut handler), _, _| {
match crtc::receive_events(device) {
fn receive(&mut self, event: FdEvent, (): ()) {
let mut device = self.device.borrow_mut();
match event {
FdEvent::Ready { .. } => match crtc::receive_events(&mut *device) {
Ok(events) => for event in events {
if let crtc::Event::PageFlip(event) = event {
if device.active.load(Ordering::SeqCst) {
@ -584,20 +609,21 @@ where
backend.unlock_buffer();
trace!(device.logger, "Handling event for backend {:?}", event.crtc);
// and then call the user to render the next frame
handler.ready(evlh, device, event.crtc, event.frame, event.duration);
self.handler
.ready(&mut device, event.crtc, event.frame, event.duration);
} else {
device.backends.borrow_mut().remove(&event.crtc);
}
}
}
},
Err(err) => handler.error(evlh, device, err),
};
},
error: |evlh, &mut (ref mut device, ref mut handler), _, error| {
warn!(device.logger, "DrmDevice errored: {}", error);
handler.error(evlh, device, error.into());
},
Err(err) => self.handler.error(&mut device, err),
},
FdEvent::Error { error, .. } => {
warn!(device.logger, "DrmDevice errored: {}", error);
self.handler.error(&mut device, error.into());
}
}
}
}
@ -629,7 +655,7 @@ impl<A: ControlDevice + 'static> AsSessionObserver<DrmDeviceObserver<A>> for Drm
#[cfg(feature = "backend_session")]
impl<A: ControlDevice + 'static> SessionObserver for DrmDeviceObserver<A> {
fn pause(&mut self, _evlh: &mut EventLoopHandle, devnum: Option<(u32, u32)>) {
fn pause(&mut self, devnum: Option<(u32, u32)>) {
if let Some((major, minor)) = devnum {
if major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) {
return;
@ -665,7 +691,7 @@ impl<A: ControlDevice + 'static> SessionObserver for DrmDeviceObserver<A> {
}
}
fn activate(&mut self, _evlh: &mut EventLoopHandle, devnum: Option<(u32, u32, Option<RawFd>)>) {
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
if let Some((major, minor, fd)) = devnum {
if major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) {
return;

View File

@ -50,7 +50,10 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> DerefMut for EGLContext<B,
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
/// Create a new `EGLContext` from a given `NativeDisplay`
pub fn new<L>(
native: N, attributes: GlAttributes, reqs: PixelFormatRequirements, logger: L
native: N,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
logger: L,
) -> Result<EGLContext<B, N>>
where
L: Into<Option<::slog::Logger>>,
@ -82,7 +85,9 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
}
unsafe fn new_internal(
ptr: ffi::NativeDisplayType, mut attributes: GlAttributes, reqs: PixelFormatRequirements,
ptr: ffi::NativeDisplayType,
mut attributes: GlAttributes,
reqs: PixelFormatRequirements,
log: ::slog::Logger,
) -> Result<
(

View File

@ -42,7 +42,8 @@ pub mod egl {
#[allow(non_snake_case, unused_variables, dead_code)]
#[inline]
pub unsafe fn BindWaylandDisplayWL(
dpy: types::EGLDisplay, display: *mut __gl_imports::raw::c_void
dpy: types::EGLDisplay,
display: *mut __gl_imports::raw::c_void,
) -> types::EGLBoolean {
__gl_imports::mem::transmute::<
_,
@ -53,7 +54,8 @@ pub mod egl {
#[allow(non_snake_case, unused_variables, dead_code)]
#[inline]
pub unsafe fn UnbindWaylandDisplayWL(
dpy: types::EGLDisplay, display: *mut __gl_imports::raw::c_void
dpy: types::EGLDisplay,
display: *mut __gl_imports::raw::c_void,
) -> types::EGLBoolean {
__gl_imports::mem::transmute::<
_,
@ -64,7 +66,9 @@ pub mod egl {
#[allow(non_snake_case, unused_variables, dead_code)]
#[inline]
pub unsafe fn QueryWaylandBufferWL(
dpy: types::EGLDisplay, buffer: *mut __gl_imports::raw::c_void, attribute: types::EGLint,
dpy: types::EGLDisplay,
buffer: *mut __gl_imports::raw::c_void,
attribute: types::EGLint,
value: *mut types::EGLint,
) -> types::EGLBoolean {
__gl_imports::mem::transmute::<

View File

@ -31,7 +31,9 @@ pub trait Backend {
/// The returned `EGLDisplay` needs to be a valid ptr for egl,
/// but there is no way to test that.
unsafe fn get_display<F: Fn(&str) -> bool>(
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger
display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay;
}
@ -43,7 +45,9 @@ impl Backend for Wayland {
type Surface = wegl::WlEglSurface;
unsafe fn get_display<F>(
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger
display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay
where
F: Fn(&str) -> bool,
@ -87,7 +91,9 @@ impl Backend for X11 {
type Surface = XlibWindow;
unsafe fn get_display<F>(
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger
display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay
where
F: Fn(&str) -> bool,
@ -114,7 +120,9 @@ impl<T: 'static> Backend for Gbm<T> {
type Surface = GbmSurface<T>;
unsafe fn get_display<F>(
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger
display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay
where
F: Fn(&str) -> bool,

View File

@ -30,7 +30,8 @@ impl<N: native::NativeSurface> DerefMut for EGLSurface<N> {
impl<N: native::NativeSurface> EGLSurface<N> {
pub(crate) fn new<B: native::Backend<Surface = N>, D: native::NativeDisplay<B>>(
context: &EGLContext<B, D>, native: N
context: &EGLContext<B, D>,
native: N,
) -> Result<EGLSurface<N>> {
let surface = unsafe {
ffi::egl::CreateWindowSurface(

View File

@ -17,7 +17,7 @@ use nix::libc::c_uint;
use std::fmt;
use std::rc::{Rc, Weak};
use wayland_server::{Display, Resource};
use wayland_server::protocol::wl_buffer::WlBuffer;
use wayland_server::protocol::wl_buffer::{self, WlBuffer};
use wayland_sys::server::wl_display;
/// Error that can occur when accessing an EGL buffer
@ -25,7 +25,7 @@ pub enum BufferAccessError {
/// The corresponding Context is not alive anymore
ContextLost,
/// This buffer is not managed by the EGL buffer
NotManaged(WlBuffer),
NotManaged(Resource<WlBuffer>),
/// Failed to create EGLImages from the buffer
EGLImageCreationFailed,
/// The required EGL extension is not supported by the underlying EGL implementation
@ -175,7 +175,7 @@ pub struct EGLImages {
/// Format of these images
pub format: Format,
images: Vec<EGLImage>,
buffer: WlBuffer,
buffer: Resource<WlBuffer>,
}
impl EGLImages {
@ -192,7 +192,9 @@ impl EGLImages {
///
/// The given `tex_id` needs to be a valid GL texture otherwise undefined behavior might occur.
pub unsafe fn bind_to_texture(
&self, plane: usize, tex_id: c_uint
&self,
plane: usize,
tex_id: c_uint,
) -> ::std::result::Result<(), TextureCreationError> {
if self.display.upgrade().is_some() {
let mut old_tex_id: i32 = 0;
@ -225,7 +227,7 @@ impl Drop for EGLImages {
}
}
}
self.buffer.release();
self.buffer.send(wl_buffer::Event::Release);
}
}
@ -255,7 +257,8 @@ pub struct EGLDisplay(Weak<ffi::egl::types::EGLDisplay>, *mut wl_display);
impl EGLDisplay {
fn new<B: native::Backend, N: native::NativeDisplay<B>>(
context: &EGLContext<B, N>, display: *mut wl_display
context: &EGLContext<B, N>,
display: *mut wl_display,
) -> EGLDisplay {
EGLDisplay(Rc::downgrade(&context.display), display)
}
@ -266,14 +269,15 @@ impl EGLDisplay {
/// a `BufferAccessError::NotManaged(WlBuffer)` is returned with the original buffer
/// to render it another way.
pub fn egl_buffer_contents(
&self, buffer: WlBuffer
&self,
buffer: Resource<WlBuffer>,
) -> ::std::result::Result<EGLImages, BufferAccessError> {
if let Some(display) = self.0.upgrade() {
let mut format: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.ptr() as *mut _,
buffer.c_ptr() as *mut _,
ffi::egl::EGL_TEXTURE_FORMAT,
&mut format as *mut _,
) == 0
@ -294,7 +298,7 @@ impl EGLDisplay {
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.ptr() as *mut _,
buffer.c_ptr() as *mut _,
ffi::egl::WIDTH as i32,
&mut width as *mut _,
) == 0
@ -306,7 +310,7 @@ impl EGLDisplay {
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.ptr() as *mut _,
buffer.c_ptr() as *mut _,
ffi::egl::HEIGHT as i32,
&mut height as *mut _,
) == 0
@ -318,7 +322,7 @@ impl EGLDisplay {
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.ptr() as *mut _,
buffer.c_ptr() as *mut _,
ffi::egl::WAYLAND_Y_INVERTED_WL,
&mut inverted as *mut _,
) != 0
@ -339,7 +343,7 @@ impl EGLDisplay {
*display,
ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL,
buffer.ptr() as *mut _,
buffer.c_ptr() as *mut _,
out.as_ptr(),
)
};
@ -394,10 +398,10 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLWaylandExtensions for E
if !self.egl_to_texture_support {
bail!(ErrorKind::EglExtensionNotSupported(&["GL_OES_EGL_image"]));
}
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) };
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.c_ptr() as *mut _) };
if res == 0 {
bail!(ErrorKind::OtherEGLDisplayAlreadyBound);
}
Ok(EGLDisplay::new(self, unsafe { display.ptr() }))
Ok(EGLDisplay::new(self, display.c_ptr()))
}
}

View File

@ -30,7 +30,9 @@ pub trait GraphicsBackend {
/// from raw image buffers over a fixed list of possible cursor types to simply the
/// void type () to represent no possible customization of the cursor itself.
fn set_cursor_representation(
&self, cursor: &Self::CursorFormat, hotspot: (u32, u32)
&self,
cursor: &Self::CursorFormat,
hotspot: (u32, u32),
) -> Result<(), Self::Error>;
}

View File

@ -2,7 +2,6 @@
use std::error::Error;
use std::string::ToString;
use wayland_server::EventLoopHandle;
/// A seat describes a group of input devices and at least one
/// graphics device belonging together.
@ -521,31 +520,31 @@ pub trait InputBackend: Sized {
type TouchFrameEvent: TouchFrameEvent;
/// Sets a new handler for this `InputBackend`
fn set_handler<H: InputHandler<Self> + 'static>(&mut self, evlh: &mut EventLoopHandle, handler: H);
fn set_handler<H: InputHandler<Self> + 'static>(&mut self, handler: H);
/// Get a reference to the currently set handler, if any
fn get_handler(&mut self) -> Option<&mut InputHandler<Self>>;
/// Clears the currently handler, if one is set
fn clear_handler(&mut self, evlh: &mut EventLoopHandle);
fn clear_handler(&mut self);
/// Get current `InputConfig`
fn input_config(&mut self) -> &mut Self::InputConfig;
/// Processes new events of the underlying backend and drives the `InputHandler`.
fn dispatch_new_events(&mut self, evlh: &mut EventLoopHandle) -> Result<(), Self::EventError>;
fn dispatch_new_events(&mut self) -> Result<(), Self::EventError>;
}
/// Implement to receive input events from any `InputBackend`.
pub trait InputHandler<B: InputBackend> {
/// Called when a new `Seat` has been created
fn on_seat_created(&mut self, evlh: &mut EventLoopHandle, seat: &Seat);
fn on_seat_created(&mut self, seat: &Seat);
/// Called when an existing `Seat` has been destroyed.
fn on_seat_destroyed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat);
fn on_seat_destroyed(&mut self, seat: &Seat);
/// Called when a `Seat`'s properties have changed.
///
/// ## Note:
///
/// It is not guaranteed that any change has actually happened.
fn on_seat_changed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat);
fn on_seat_changed(&mut self, seat: &Seat);
/// Called when a new keyboard event was received.
///
@ -554,7 +553,7 @@ pub trait InputHandler<B: InputBackend> {
/// - `seat` - The `Seat` the event belongs to
/// - `event` - The keyboard event
///
fn on_keyboard_key(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::KeyboardKeyEvent);
fn on_keyboard_key(&mut self, seat: &Seat, event: B::KeyboardKeyEvent);
/// Called when a new pointer movement event was received.
///
@ -562,30 +561,28 @@ pub trait InputHandler<B: InputBackend> {
///
/// - `seat` - The `Seat` the event belongs to
/// - `event` - The pointer movement event
fn on_pointer_move(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionEvent);
fn on_pointer_move(&mut self, seat: &Seat, event: B::PointerMotionEvent);
/// Called when a new pointer absolute movement event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `event` - The pointer absolute movement event
fn on_pointer_move_absolute(
&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionAbsoluteEvent
);
fn on_pointer_move_absolute(&mut self, seat: &Seat, event: B::PointerMotionAbsoluteEvent);
/// Called when a new pointer button event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `event` - The pointer button event
fn on_pointer_button(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerButtonEvent);
fn on_pointer_button(&mut self, seat: &Seat, event: B::PointerButtonEvent);
/// Called when a new pointer scroll event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `event` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events.
fn on_pointer_axis(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerAxisEvent);
fn on_pointer_axis(&mut self, seat: &Seat, event: B::PointerAxisEvent);
/// Called when a new touch down event was received.
///
@ -593,99 +590,97 @@ pub trait InputHandler<B: InputBackend> {
///
/// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch down event
fn on_touch_down(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchDownEvent);
fn on_touch_down(&mut self, seat: &Seat, event: B::TouchDownEvent);
/// Called when a new touch motion event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch motion event.
fn on_touch_motion(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchMotionEvent);
fn on_touch_motion(&mut self, seat: &Seat, event: B::TouchMotionEvent);
/// Called when a new touch up event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch up event.
fn on_touch_up(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchUpEvent);
fn on_touch_up(&mut self, seat: &Seat, event: B::TouchUpEvent);
/// Called when a new touch cancel event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch cancel event.
fn on_touch_cancel(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchCancelEvent);
fn on_touch_cancel(&mut self, seat: &Seat, event: B::TouchCancelEvent);
/// Called when a new touch frame event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch frame event.
fn on_touch_frame(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchFrameEvent);
fn on_touch_frame(&mut self, seat: &Seat, event: B::TouchFrameEvent);
/// Called when the `InputConfig` was changed through an external event.
///
/// What kind of events can trigger this call is completely backend dependent.
/// E.g. an input devices was attached/detached or changed it's own configuration.
fn on_input_config_changed(&mut self, evlh: &mut EventLoopHandle, config: &mut B::InputConfig);
fn on_input_config_changed(&mut self, config: &mut B::InputConfig);
}
impl<B: InputBackend> InputHandler<B> for Box<InputHandler<B>> {
fn on_seat_created(&mut self, evlh: &mut EventLoopHandle, seat: &Seat) {
(**self).on_seat_created(evlh, seat)
fn on_seat_created(&mut self, seat: &Seat) {
(**self).on_seat_created(seat)
}
fn on_seat_destroyed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat) {
(**self).on_seat_destroyed(evlh, seat)
fn on_seat_destroyed(&mut self, seat: &Seat) {
(**self).on_seat_destroyed(seat)
}
fn on_seat_changed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat) {
(**self).on_seat_changed(evlh, seat)
fn on_seat_changed(&mut self, seat: &Seat) {
(**self).on_seat_changed(seat)
}
fn on_keyboard_key(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::KeyboardKeyEvent) {
(**self).on_keyboard_key(evlh, seat, event)
fn on_keyboard_key(&mut self, seat: &Seat, event: B::KeyboardKeyEvent) {
(**self).on_keyboard_key(seat, event)
}
fn on_pointer_move(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionEvent) {
(**self).on_pointer_move(evlh, seat, event)
fn on_pointer_move(&mut self, seat: &Seat, event: B::PointerMotionEvent) {
(**self).on_pointer_move(seat, event)
}
fn on_pointer_move_absolute(
&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionAbsoluteEvent
) {
(**self).on_pointer_move_absolute(evlh, seat, event)
fn on_pointer_move_absolute(&mut self, seat: &Seat, event: B::PointerMotionAbsoluteEvent) {
(**self).on_pointer_move_absolute(seat, event)
}
fn on_pointer_button(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerButtonEvent) {
(**self).on_pointer_button(evlh, seat, event)
fn on_pointer_button(&mut self, seat: &Seat, event: B::PointerButtonEvent) {
(**self).on_pointer_button(seat, event)
}
fn on_pointer_axis(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerAxisEvent) {
(**self).on_pointer_axis(evlh, seat, event)
fn on_pointer_axis(&mut self, seat: &Seat, event: B::PointerAxisEvent) {
(**self).on_pointer_axis(seat, event)
}
fn on_touch_down(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchDownEvent) {
(**self).on_touch_down(evlh, seat, event)
fn on_touch_down(&mut self, seat: &Seat, event: B::TouchDownEvent) {
(**self).on_touch_down(seat, event)
}
fn on_touch_motion(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchMotionEvent) {
(**self).on_touch_motion(evlh, seat, event)
fn on_touch_motion(&mut self, seat: &Seat, event: B::TouchMotionEvent) {
(**self).on_touch_motion(seat, event)
}
fn on_touch_up(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchUpEvent) {
(**self).on_touch_up(evlh, seat, event)
fn on_touch_up(&mut self, seat: &Seat, event: B::TouchUpEvent) {
(**self).on_touch_up(seat, event)
}
fn on_touch_cancel(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchCancelEvent) {
(**self).on_touch_cancel(evlh, seat, event)
fn on_touch_cancel(&mut self, seat: &Seat, event: B::TouchCancelEvent) {
(**self).on_touch_cancel(seat, event)
}
fn on_touch_frame(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchFrameEvent) {
(**self).on_touch_frame(evlh, seat, event)
fn on_touch_frame(&mut self, seat: &Seat, event: B::TouchFrameEvent) {
(**self).on_touch_frame(seat, event)
}
fn on_input_config_changed(&mut self, evlh: &mut EventLoopHandle, config: &mut B::InputConfig) {
(**self).on_input_config_changed(evlh, config)
fn on_input_config_changed(&mut self, config: &mut B::InputConfig) {
(**self).on_input_config_changed(config)
}
}

View File

@ -11,8 +11,9 @@ use std::hash::{Hash, Hasher};
use std::io::Error as IoError;
use std::os::unix::io::RawFd;
use std::path::Path;
use wayland_server::EventLoopHandle;
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
use wayland_server::LoopToken;
use wayland_server::commons::Implementation;
use wayland_server::sources::{FdEvent, FdInterest, Source};
// No idea if this is the same across unix platforms
// Lets make this linux exclusive for now, once someone tries to build it for
@ -256,16 +257,14 @@ impl backend::InputBackend for LibinputInputBackend {
type TouchCancelEvent = event::touch::TouchCancelEvent;
type TouchFrameEvent = event::touch::TouchFrameEvent;
fn set_handler<H: backend::InputHandler<Self> + 'static>(
&mut self, evlh: &mut EventLoopHandle, mut handler: H
) {
fn set_handler<H: backend::InputHandler<Self> + 'static>(&mut self, mut handler: H) {
if self.handler.is_some() {
self.clear_handler(evlh);
self.clear_handler();
}
info!(self.logger, "New input handler set");
for seat in self.seats.values() {
trace!(self.logger, "Calling on_seat_created with {:?}", seat);
handler.on_seat_created(evlh, seat);
handler.on_seat_created(seat);
}
self.handler = Some(Box::new(handler));
}
@ -276,11 +275,11 @@ impl backend::InputBackend for LibinputInputBackend {
.map(|handler| handler as &mut backend::InputHandler<Self>)
}
fn clear_handler(&mut self, evlh: &mut EventLoopHandle) {
fn clear_handler(&mut self) {
if let Some(mut handler) = self.handler.take() {
for seat in self.seats.values() {
trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat);
handler.on_seat_destroyed(evlh, seat);
handler.on_seat_destroyed(seat);
}
info!(self.logger, "Removing input handler");
}
@ -290,7 +289,7 @@ impl backend::InputBackend for LibinputInputBackend {
&mut self.devices
}
fn dispatch_new_events(&mut self, evlh: &mut EventLoopHandle) -> Result<(), IoError> {
fn dispatch_new_events(&mut self) -> Result<(), IoError> {
use input::event::EventTrait;
self.context.dispatch()?;
@ -323,7 +322,7 @@ impl backend::InputBackend for LibinputInputBackend {
}
if let Some(ref mut handler) = self.handler {
trace!(self.logger, "Calling on_seat_changed with {:?}", old_seat);
handler.on_seat_changed(evlh, old_seat);
handler.on_seat_changed(old_seat);
}
}
Entry::Vacant(seat_entry) => {
@ -340,7 +339,7 @@ impl backend::InputBackend for LibinputInputBackend {
));
if let Some(ref mut handler) = self.handler {
trace!(self.logger, "Calling on_seat_created with {:?}", seat);
handler.on_seat_created(evlh, seat);
handler.on_seat_created(seat);
}
}
}
@ -379,7 +378,7 @@ impl backend::InputBackend for LibinputInputBackend {
if let Some(seat) = self.seats.remove(&device_seat) {
if let Some(ref mut handler) = self.handler {
trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat);
handler.on_seat_destroyed(evlh, &seat);
handler.on_seat_destroyed(&seat);
}
} else {
warn!(self.logger, "Seat destroyed that was never created");
@ -389,7 +388,7 @@ impl backend::InputBackend for LibinputInputBackend {
} else if let Some(ref mut handler) = self.handler {
if let Some(seat) = self.seats.get(&device_seat) {
trace!(self.logger, "Calling on_seat_changed with {:?}", seat);
handler.on_seat_changed(evlh, &seat);
handler.on_seat_changed(&seat);
} else {
warn!(self.logger, "Seat changed that was never created");
continue;
@ -398,7 +397,7 @@ impl backend::InputBackend for LibinputInputBackend {
}
}
if let Some(ref mut handler) = self.handler {
handler.on_input_config_changed(evlh, &mut self.devices);
handler.on_input_config_changed(&mut self.devices);
}
}
libinput::Event::Touch(touch_event) => {
@ -409,7 +408,7 @@ impl backend::InputBackend for LibinputInputBackend {
match touch_event {
TouchEvent::Down(down_event) => {
trace!(self.logger, "Calling on_touch_down with {:?}", down_event);
handler.on_touch_down(evlh, seat, down_event)
handler.on_touch_down(seat, down_event)
}
TouchEvent::Motion(motion_event) => {
trace!(
@ -417,11 +416,11 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_touch_motion with {:?}",
motion_event
);
handler.on_touch_motion(evlh, seat, motion_event)
handler.on_touch_motion(seat, motion_event)
}
TouchEvent::Up(up_event) => {
trace!(self.logger, "Calling on_touch_up with {:?}", up_event);
handler.on_touch_up(evlh, seat, up_event)
handler.on_touch_up(seat, up_event)
}
TouchEvent::Cancel(cancel_event) => {
trace!(
@ -429,11 +428,11 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_touch_cancel with {:?}",
cancel_event
);
handler.on_touch_cancel(evlh, seat, cancel_event)
handler.on_touch_cancel(seat, cancel_event)
}
TouchEvent::Frame(frame_event) => {
trace!(self.logger, "Calling on_touch_frame with {:?}", frame_event);
handler.on_touch_frame(evlh, seat, frame_event)
handler.on_touch_frame(seat, frame_event)
}
}
} else {
@ -449,7 +448,7 @@ impl backend::InputBackend for LibinputInputBackend {
let device_seat = key_event.device().seat();
if let &Some(ref seat) = &self.seats.get(&device_seat) {
trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event);
handler.on_keyboard_key(evlh, seat, key_event);
handler.on_keyboard_key(seat, key_event);
} else {
warn!(self.logger, "Recieved key event of non existing Seat");
continue;
@ -469,7 +468,7 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_pointer_move with {:?}",
motion_event
);
handler.on_pointer_move(evlh, seat, motion_event);
handler.on_pointer_move(seat, motion_event);
}
PointerEvent::MotionAbsolute(motion_abs_event) => {
trace!(
@ -477,11 +476,11 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_pointer_move_absolute with {:?}",
motion_abs_event
);
handler.on_pointer_move_absolute(evlh, seat, motion_abs_event);
handler.on_pointer_move_absolute(seat, motion_abs_event);
}
PointerEvent::Axis(axis_event) => {
trace!(self.logger, "Calling on_pointer_axis with {:?}", axis_event);
handler.on_pointer_axis(evlh, seat, axis_event);
handler.on_pointer_axis(seat, axis_event);
}
PointerEvent::Button(button_event) => {
trace!(
@ -489,7 +488,7 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_pointer_button with {:?}",
button_event
);
handler.on_pointer_button(evlh, seat, button_event);
handler.on_pointer_button(seat, button_event);
}
}
} else {
@ -554,7 +553,7 @@ impl From<event::pointer::ButtonState> for backend::MouseButtonState {
#[cfg(feature = "backend_session")]
impl SessionObserver for libinput::Libinput {
fn pause(&mut self, _state: &mut EventLoopHandle, device: Option<(u32, u32)>) {
fn pause(&mut self, device: Option<(u32, u32)>) {
if let Some((major, _)) = device {
if major != INPUT_MAJOR {
return;
@ -564,7 +563,7 @@ impl SessionObserver for libinput::Libinput {
self.suspend()
}
fn activate(&mut self, _state: &mut EventLoopHandle, _device: Option<(u32, u32, Option<RawFd>)>) {
fn activate(&mut self, _device: Option<(u32, u32, Option<RawFd>)>) {
// libinput closes the devices on suspend, so we should not get any INPUT_MAJOR calls
// also lets hope multiple resumes are okay in case of logind
self.resume().expect("Unable to resume libinput context");
@ -602,27 +601,25 @@ impl<S: Session> libinput::LibinputInterface for LibinputSessionInterface<S> {
/// Automatically feeds the backend with incoming events without any manual calls to
/// `dispatch_new_events`. Should be used to achieve the smallest possible latency.
pub fn libinput_bind(
backend: LibinputInputBackend, evlh: &mut EventLoopHandle
) -> ::std::result::Result<FdEventSource<LibinputInputBackend>, (IoError, LibinputInputBackend)> {
backend: LibinputInputBackend,
token: LoopToken,
) -> ::std::result::Result<Source<FdEvent>, (IoError, LibinputInputBackend)> {
let fd = unsafe { backend.context.fd() };
evlh.add_fd_event_source(
fd,
fd_event_source_implementation(),
backend,
FdInterest::READ,
)
token.add_fd_event_source(fd, FdInterest::READ, backend)
}
fn fd_event_source_implementation() -> FdEventSourceImpl<LibinputInputBackend> {
FdEventSourceImpl {
ready: |evlh, ref mut backend, _, _| {
use backend::input::InputBackend;
if let Err(error) = backend.dispatch_new_events(evlh) {
warn!(backend.logger, "Libinput errored: {}", error);
impl Implementation<(), FdEvent> for LibinputInputBackend {
fn receive(&mut self, event: FdEvent, (): ()) {
match event {
FdEvent::Ready { .. } => {
use backend::input::InputBackend;
if let Err(error) = self.dispatch_new_events() {
warn!(self.logger, "Libinput errored: {}", error);
}
}
},
error: |_evlh, ref backend, _, error| {
warn!(backend.logger, "Libinput fd errored: {}", error);
},
FdEvent::Error { error, .. } => {
warn!(self.logger, "Libinput fd errored: {}", error);
}
}
}
}

View File

@ -39,8 +39,9 @@ use std::io::Error as IoError;
use std::os::unix::io::RawFd;
use std::path::Path;
use std::rc::Rc;
use wayland_server::EventLoopHandle;
use wayland_server::sources::{EventSource, SignalEventSource};
use wayland_server::LoopToken;
use wayland_server::commons::downcast_impl;
use wayland_server::sources::{SignalEvent, Source};
/// `Session` using the best available inteface
#[derive(Clone)]
@ -71,7 +72,7 @@ pub enum BoundAutoSession {
#[cfg(feature = "backend_session_logind")]
Logind(BoundLogindSession),
/// Bound direct / tty session
Direct(SignalEventSource<DirectSessionNotifier>),
Direct(Source<SignalEvent>),
}
/// Id's used by the `AutoSessionNotifier` internally.
@ -153,13 +154,14 @@ impl AutoSession {
/// If you don't use this function `AutoSessionNotifier` will not correctly tell you the
/// session state and call it's `SessionObservers`.
pub fn auto_session_bind(
notifier: AutoSessionNotifier, evlh: &mut EventLoopHandle
notifier: AutoSessionNotifier,
token: &LoopToken,
) -> ::std::result::Result<BoundAutoSession, (IoError, AutoSessionNotifier)> {
Ok(match notifier {
#[cfg(feature = "backend_session_logind")]
AutoSessionNotifier::Logind(logind) => BoundAutoSession::Logind(logind_session_bind(logind, evlh)
AutoSessionNotifier::Logind(logind) => BoundAutoSession::Logind(logind_session_bind(logind, token)
.map_err(|(error, notifier)| (error, AutoSessionNotifier::Logind(notifier)))?),
AutoSessionNotifier::Direct(direct) => BoundAutoSession::Direct(direct_session_bind(direct, evlh)
AutoSessionNotifier::Direct(direct) => BoundAutoSession::Direct(direct_session_bind(direct, token)
.map_err(|(error, notifier)| (error, AutoSessionNotifier::Direct(notifier)))?),
})
}
@ -210,7 +212,8 @@ impl SessionNotifier for AutoSessionNotifier {
type Id = AutoId;
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
&mut self, signal: &mut A
&mut self,
signal: &mut A,
) -> Self::Id {
match self {
#[cfg(feature = "backend_session_logind")]
@ -257,7 +260,9 @@ impl BoundAutoSession {
match self {
#[cfg(feature = "backend_session_logind")]
BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()),
BoundAutoSession::Direct(source) => AutoSessionNotifier::Direct(source.remove()),
BoundAutoSession::Direct(source) => {
AutoSessionNotifier::Direct(*downcast_impl(source.remove()).unwrap_or_else(|_| unreachable!()))
}
}
}
}

View File

@ -42,8 +42,9 @@ use std::path::Path;
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use systemd::login;
use wayland_server::EventLoopHandle;
use wayland_server::sources::{EventSource, FdEventSource, FdEventSourceImpl, FdInterest};
use wayland_server::LoopToken;
use wayland_server::commons::Implementation;
use wayland_server::sources::{FdEvent, FdInterest, Source};
struct LogindSessionImpl {
conn: RefCell<Connection>,
@ -62,6 +63,7 @@ pub struct LogindSession {
}
/// `SessionNotifier` via the logind dbus interface
#[derive(Clone)]
pub struct LogindSessionNotifier {
internal: Rc<LogindSessionImpl>,
}
@ -186,7 +188,11 @@ impl LogindSessionNotifier {
impl LogindSessionImpl {
fn blocking_call<'d, 'p, 'i, 'm, D, P, I, M>(
conn: &Connection, destination: D, path: P, interface: I, method: M,
conn: &Connection,
destination: D,
path: P,
interface: I,
method: M,
arguments: Option<Vec<MessageItem>>,
) -> Result<Message>
where
@ -230,7 +236,7 @@ impl LogindSessionImpl {
}
}
fn handle_signals(&self, evlh: &mut EventLoopHandle, signals: ConnectionItems) -> Result<()> {
fn handle_signals(&self, signals: ConnectionItems) -> Result<()> {
for item in signals {
let message = if let ConnectionItem::Signal(ref s) = item {
s
@ -246,7 +252,7 @@ impl LogindSessionImpl {
//So lets just put it to sleep.. forever
for signal in &mut *self.signals.borrow_mut() {
if let &mut Some(ref mut signal) = signal {
signal.pause(evlh, None);
signal.pause(None);
}
}
self.active.store(false, Ordering::SeqCst);
@ -263,7 +269,7 @@ impl LogindSessionImpl {
);
for signal in &mut *self.signals.borrow_mut() {
if let &mut Some(ref mut signal) = signal {
signal.pause(evlh, Some((major, minor)));
signal.pause(Some((major, minor)));
}
}
// the other possible types are "force" or "gone" (unplugged),
@ -289,7 +295,7 @@ impl LogindSessionImpl {
debug!(self.logger, "Reactivating device ({},{})", major, minor);
for signal in &mut *self.signals.borrow_mut() {
if let &mut Some(ref mut signal) = signal {
signal.activate(evlh, Some((major, minor, Some(fd))));
signal.activate(Some((major, minor, Some(fd))));
}
}
}
@ -393,7 +399,8 @@ impl SessionNotifier for LogindSessionNotifier {
type Id = Id;
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
&mut self, signal: &mut A
&mut self,
signal: &mut A,
) -> Self::Id {
self.internal
.signals
@ -422,7 +429,7 @@ impl SessionNotifier for LogindSessionNotifier {
pub struct BoundLogindSession {
notifier: LogindSessionNotifier,
_watches: Vec<Watch>,
sources: Vec<FdEventSource<Rc<LogindSessionImpl>>>,
sources: Vec<Source<FdEvent>>,
}
/// Bind a `LogindSessionNotifier` to an `EventLoop`.
@ -431,7 +438,8 @@ pub struct BoundLogindSession {
/// If you don't use this function `LogindSessionNotifier` will not correctly tell you the logind
/// session state and call it's `SessionObservers`.
pub fn logind_session_bind(
notifier: LogindSessionNotifier, evlh: &mut EventLoopHandle
notifier: LogindSessionNotifier,
token: &LoopToken,
) -> ::std::result::Result<BoundLogindSession, (IoError, LogindSessionNotifier)> {
let watches = notifier.internal.conn.borrow().watch_fds();
@ -443,14 +451,9 @@ pub fn logind_session_bind(
let mut interest = FdInterest::empty();
interest.set(FdInterest::READ, watch.readable());
interest.set(FdInterest::WRITE, watch.writable());
evlh.add_fd_event_source(
watch.fd(),
fd_event_source_implementation(),
notifier.internal.clone(),
interest,
)
token.add_fd_event_source(watch.fd(), interest, notifier.clone())
})
.collect::<::std::result::Result<Vec<FdEventSource<Rc<LogindSessionImpl>>>, (IoError, _)>>()
.collect::<::std::result::Result<Vec<Source<FdEvent>>, (IoError, _)>>()
.map_err(|(err, _)| {
(
err,
@ -492,35 +495,40 @@ impl Drop for LogindSessionNotifier {
}
}
fn fd_event_source_implementation() -> FdEventSourceImpl<Rc<LogindSessionImpl>> {
FdEventSourceImpl {
ready: |evlh, session, fd, interest| {
let conn = session.conn.borrow();
let items = conn.watch_handle(
fd,
match interest {
x if x.contains(FdInterest::READ) && x.contains(FdInterest::WRITE) => {
WatchEvent::Readable as u32 | WatchEvent::Writable as u32
}
x if x.contains(FdInterest::READ) => WatchEvent::Readable as u32,
x if x.contains(FdInterest::WRITE) => WatchEvent::Writable as u32,
_ => return,
},
);
if let Err(err) = session.handle_signals(evlh, items) {
error!(session.logger, "Error handling dbus signals: {}", err);
impl Implementation<(), FdEvent> for LogindSessionNotifier {
fn receive(&mut self, event: FdEvent, (): ()) {
match event {
FdEvent::Ready { fd, mask } => {
let conn = self.internal.conn.borrow();
let items = conn.watch_handle(
fd,
match mask {
x if x.contains(FdInterest::READ) && x.contains(FdInterest::WRITE) => {
WatchEvent::Readable as u32 | WatchEvent::Writable as u32
}
x if x.contains(FdInterest::READ) => WatchEvent::Readable as u32,
x if x.contains(FdInterest::WRITE) => WatchEvent::Writable as u32,
_ => return,
},
);
if let Err(err) = self.internal.handle_signals(items) {
error!(self.internal.logger, "Error handling dbus signals: {}", err);
}
}
},
error: |evlh, session, fd, error| {
warn!(session.logger, "Error on dbus connection: {:?}", error);
// handle the remaining messages, they might contain the SessionRemoved event
// in case the server did close the connection.
let conn = session.conn.borrow();
let items = conn.watch_handle(fd, WatchEvent::Error as u32);
if let Err(err) = session.handle_signals(evlh, items) {
error!(session.logger, "Error handling dbus signals: {}", err);
FdEvent::Error { fd, error } => {
warn!(
self.internal.logger,
"Error on dbus connection: {:?}", error
);
// handle the remaining messages, they might contain the SessionRemoved event
// in case the server did close the connection.
let conn = self.internal.conn.borrow();
let items = conn.watch_handle(fd, WatchEvent::Error as u32);
if let Err(err) = self.internal.handle_signals(items) {
error!(self.internal.logger, "Error handling dbus signals: {}", err);
}
}
},
}
}
}

View File

@ -59,8 +59,9 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(feature = "backend_session_udev")]
use udev::Context;
use wayland_server::EventLoopHandle;
use wayland_server::sources::SignalEventSource;
use wayland_server::LoopToken;
use wayland_server::commons::Implementation;
use wayland_server::sources::{SignalEvent, Source};
#[allow(dead_code)]
mod tty {
@ -171,8 +172,11 @@ impl DirectSession {
.new(o!("smithay_module" => "backend_session", "session_type" => "direct/vt"));
let fd = tty.map(|path| {
open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC, Mode::empty())
.chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy())))
open(
path,
fcntl::OFlag::O_RDWR | fcntl::OFlag::O_CLOEXEC,
Mode::empty(),
).chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy())))
}).unwrap_or(dup(0 /*stdin*/).chain_err(|| ErrorKind::FailedToOpenTTY(String::from("<stdin>"))))?;
let active = Arc::new(AtomicBool::new(true));
@ -345,7 +349,8 @@ impl SessionNotifier for DirectSessionNotifier {
type Id = Id;
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
&mut self, signal: &mut A
&mut self,
signal: &mut A,
) -> Self::Id {
self.signals.push(Some(Box::new(signal.observer())));
Id(self.signals.len() - 1)
@ -362,47 +367,48 @@ impl SessionNotifier for DirectSessionNotifier {
}
}
impl Implementation<(), SignalEvent> for DirectSessionNotifier {
fn receive(&mut self, _signal: SignalEvent, (): ()) {
if self.is_active() {
info!(self.logger, "Session shall become inactive.");
for signal in &mut self.signals {
if let &mut Some(ref mut signal) = signal {
signal.pause(None);
}
}
self.active.store(false, Ordering::SeqCst);
unsafe {
tty::vt_rel_disp(self.tty, 1).expect("Unable to release tty lock");
}
debug!(self.logger, "Session is now inactive");
} else {
debug!(self.logger, "Session will become active again");
unsafe {
tty::vt_rel_disp(self.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock");
}
for signal in &mut self.signals {
if let &mut Some(ref mut signal) = signal {
signal.activate(None);
}
}
self.active.store(true, Ordering::SeqCst);
info!(self.logger, "Session is now active again");
}
}
}
/// Bind a `DirectSessionNotifier` to an `EventLoop`.
///
/// Allows the `DirectSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current
/// session state and call it's `SessionObservers`.
pub fn direct_session_bind(
notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle
) -> ::std::result::Result<SignalEventSource<DirectSessionNotifier>, (IoError, DirectSessionNotifier)> {
notifier: DirectSessionNotifier,
token: &LoopToken,
) -> ::std::result::Result<Source<SignalEvent>, (IoError, DirectSessionNotifier)> {
let signal = notifier.signal;
evlh.add_signal_event_source(
|evlh, notifier, _| {
if notifier.is_active() {
info!(notifier.logger, "Session shall become inactive");
for signal in &mut notifier.signals {
if let &mut Some(ref mut signal) = signal {
signal.pause(evlh, None);
}
}
notifier.active.store(false, Ordering::SeqCst);
unsafe {
tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock");
}
debug!(notifier.logger, "Session is now inactive");
} else {
debug!(notifier.logger, "Session will become active again");
unsafe {
tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock");
}
for signal in &mut notifier.signals {
if let &mut Some(ref mut signal) = signal {
signal.activate(evlh, None);
}
}
notifier.active.store(true, Ordering::SeqCst);
info!(notifier.logger, "Session is now active again");
}
},
notifier,
signal,
)
token.add_signal_event_source(signal, notifier)
}
error_chain! {

View File

@ -16,7 +16,6 @@ use std::os::unix::io::RawFd;
use std::path::Path;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use wayland_server::EventLoopHandle;
/// General session interface.
///
@ -88,7 +87,7 @@ pub trait SessionObserver {
/// If only a specific device shall be closed a device number in the form of
/// (major, minor) is provided. All observers not using the specified device should
/// ignore the signal in that case.
fn pause(&mut self, evlh: &mut EventLoopHandle, device: Option<(u32, u32)>);
fn pause(&mut self, device: Option<(u32, u32)>);
/// Session/Device got active again
///
/// If only a specific device shall be activated again a device number in the form of
@ -96,7 +95,7 @@ pub trait SessionObserver {
/// the currently open file descriptor of the device with a new one. In that case the old one
/// should not be used anymore and be closed. All observers not using the specified device should
/// ignore the signal in that case.
fn activate(&mut self, evlh: &mut EventLoopHandle, device: Option<(u32, u32, Option<RawFd>)>);
fn activate(&mut self, device: Option<(u32, u32, Option<RawFd>)>);
}
impl Session for () {

View File

@ -24,8 +24,9 @@ use std::os::unix::io::{AsRawFd, RawFd};
use std::path::{Path, PathBuf};
use std::rc::{Rc, Weak};
use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult};
use wayland_server::EventLoopHandle;
use wayland_server::sources::{EventSource, FdEventSource, FdEventSourceImpl, FdInterest};
use wayland_server::LoopToken;
use wayland_server::commons::Implementation;
use wayland_server::sources::{FdEvent, FdInterest, Source};
/// Udev's `DrmDevice` type based on the underlying session
pub struct SessionFdDrmDevice(RawFd);
@ -48,11 +49,13 @@ pub struct UdevBackend<
S: Session + 'static,
T: UdevHandler<H> + 'static,
> {
devices: Rc<RefCell<HashMap<dev_t, FdEventSource<(DrmDevice<SessionFdDrmDevice>, H)>>>>,
_handler: ::std::marker::PhantomData<H>,
devices: Rc<RefCell<HashMap<dev_t, (Source<FdEvent>, Rc<RefCell<DrmDevice<SessionFdDrmDevice>>>)>>>,
monitor: MonitorSocket,
session: S,
handler: T,
logger: ::slog::Logger,
token: LoopToken,
}
impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevHandler<H> + 'static>
@ -67,7 +70,11 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
/// `handler` - User-provided handler to respond to any detected changes
/// `logger` - slog Logger to be used by the backend and its `DrmDevices`.
pub fn new<'a, L>(
mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, mut handler: T, logger: L
token: LoopToken,
context: &Context,
mut session: S,
mut handler: T,
logger: L,
) -> Result<UdevBackend<H, S, T>>
where
L: Into<Option<::slog::Logger>>,
@ -81,7 +88,7 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
.flat_map(|path| {
match DrmDevice::new(
{
match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) {
match session.open(&path, fcntl::OFlag::O_RDWR | fcntl::OFlag::O_CLOEXEC | fcntl::OFlag::O_NOCTTY | fcntl::OFlag::O_NONBLOCK) {
Ok(fd) => SessionFdDrmDevice(fd),
Err(err) => {
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
@ -94,13 +101,13 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
Ok(mut device) => {
let devnum = device.device_id();
let fd = device.as_raw_fd();
match handler.device_added(evlh, &mut device) {
match handler.device_added(&mut device) {
Some(drm_handler) => {
match drm_device_bind(&mut evlh, device, drm_handler) {
Ok(event_source) => Some((devnum, event_source)),
match drm_device_bind(&token, device, drm_handler) {
Ok((event_source, device)) => Some((devnum, (event_source, device))),
Err((err, (mut device, _))) => {
warn!(logger, "Failed to bind device. Error: {:?}.", err);
handler.device_removed(evlh, &mut device);
handler.device_removed(&mut device);
drop(device);
if let Err(err) = session.close(fd) {
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
@ -124,7 +131,7 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
}
}
})
.collect::<HashMap<dev_t, FdEventSource<(DrmDevice<SessionFdDrmDevice>, H)>>>();
.collect::<HashMap<dev_t, _>>();
let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?;
builder
@ -135,20 +142,25 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
.chain_err(|| ErrorKind::FailedToInitMonitor)?;
Ok(UdevBackend {
_handler: ::std::marker::PhantomData,
devices: Rc::new(RefCell::new(devices)),
monitor,
session,
handler,
logger,
token,
})
}
/// Closes the udev backend and frees all remaining open devices.
pub fn close(&mut self, evlh: &mut EventLoopHandle) {
pub fn close(&mut self) {
let mut devices = self.devices.borrow_mut();
for (_, event_source) in devices.drain() {
let (mut device, _) = event_source.remove();
self.handler.device_removed(evlh, &mut device);
for (_, (event_source, device)) in devices.drain() {
event_source.remove();
let mut device = Rc::try_unwrap(device)
.unwrap_or_else(|_| unreachable!())
.into_inner();
self.handler.device_removed(&mut device);
let fd = device.as_raw_fd();
drop(device);
if let Err(err) = self.session.close(fd) {
@ -163,8 +175,8 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
}
/// `SessionObserver` linked to the `UdevBackend` it was created from.
pub struct UdevBackendObserver<H: DrmHandler<SessionFdDrmDevice> + 'static> {
devices: Weak<RefCell<HashMap<dev_t, FdEventSource<(DrmDevice<SessionFdDrmDevice>, H)>>>>,
pub struct UdevBackendObserver {
devices: Weak<RefCell<HashMap<dev_t, (Source<FdEvent>, Rc<RefCell<DrmDevice<SessionFdDrmDevice>>>)>>>,
logger: ::slog::Logger,
}
@ -172,9 +184,9 @@ impl<
H: DrmHandler<SessionFdDrmDevice> + 'static,
S: Session + 'static,
T: UdevHandler<H> + 'static,
> AsSessionObserver<UdevBackendObserver<H>> for UdevBackend<H, S, T>
> AsSessionObserver<UdevBackendObserver> for UdevBackend<H, S, T>
{
fn observer(&mut self) -> UdevBackendObserver<H> {
fn observer(&mut self) -> UdevBackendObserver {
UdevBackendObserver {
devices: Rc::downgrade(&self.devices),
logger: self.logger.clone(),
@ -182,25 +194,21 @@ impl<
}
}
impl<H: DrmHandler<SessionFdDrmDevice> + 'static> SessionObserver for UdevBackendObserver<H> {
fn pause<'a>(&mut self, evlh: &mut EventLoopHandle, devnum: Option<(u32, u32)>) {
impl SessionObserver for UdevBackendObserver {
fn pause<'a>(&mut self, devnum: Option<(u32, u32)>) {
if let Some(devices) = self.devices.upgrade() {
for fd_event_source in devices.borrow_mut().values_mut() {
fd_event_source.with_idata(evlh, |&mut (ref mut device, _), evlh| {
info!(self.logger, "changed successful");
device.observer().pause(evlh, devnum);
})
for &mut (_, ref device) in devices.borrow_mut().values_mut() {
info!(self.logger, "changed successful");
device.borrow_mut().observer().pause(devnum);
}
}
}
fn activate<'a>(&mut self, evlh: &mut EventLoopHandle, devnum: Option<(u32, u32, Option<RawFd>)>) {
fn activate<'a>(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
if let Some(devices) = self.devices.upgrade() {
for fd_event_source in devices.borrow_mut().values_mut() {
fd_event_source.with_idata(evlh, |&mut (ref mut device, _), evlh| {
info!(self.logger, "changed successful");
device.observer().activate(evlh, devnum);
})
for &mut (_, ref device) in devices.borrow_mut().values_mut() {
info!(self.logger, "changed successful");
device.borrow_mut().observer().activate(devnum);
}
}
}
@ -210,143 +218,153 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static> SessionObserver for UdevBacken
///
/// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`.
/// No runtime functionality can be provided without using this function.
pub fn udev_backend_bind<S, H, T>(
evlh: &mut EventLoopHandle, udev: UdevBackend<H, S, T>
) -> ::std::result::Result<FdEventSource<UdevBackend<H, S, T>>, (IoError, UdevBackend<H, S, T>)>
pub fn udev_backend_bind<H, S, T>(
token: &LoopToken,
udev: UdevBackend<H, S, T>,
) -> ::std::result::Result<Source<FdEvent>, (IoError, UdevBackend<H, S, T>)>
where
H: DrmHandler<SessionFdDrmDevice> + 'static,
T: UdevHandler<H> + 'static,
S: Session + 'static,
{
let fd = udev.monitor.as_raw_fd();
evlh.add_fd_event_source(fd, fd_event_source_implementation(), udev, FdInterest::READ)
token.add_fd_event_source(fd, FdInterest::READ, udev)
}
fn fd_event_source_implementation<S, H, T>() -> FdEventSourceImpl<UdevBackend<H, S, T>>
impl<H, S, T> Implementation<(), FdEvent> for UdevBackend<H, S, T>
where
H: DrmHandler<SessionFdDrmDevice> + 'static,
T: UdevHandler<H> + 'static,
S: Session + 'static,
{
FdEventSourceImpl {
ready: |mut evlh, udev, _, _| {
let events = udev.monitor.clone().collect::<Vec<Event>>();
for event in events {
match event.event_type() {
// New device
EventType::Add => {
info!(udev.logger, "Device Added");
if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) {
let mut device = {
match DrmDevice::new(
{
let logger = udev.logger.clone();
match udev.session.open(
path,
fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY
| fcntl::O_NONBLOCK,
) {
Ok(fd) => SessionFdDrmDevice(fd),
Err(err) => {
fn receive(&mut self, event: FdEvent, (): ()) {
match event {
FdEvent::Ready { .. } => {
let events = self.monitor.clone().collect::<Vec<Event>>();
for event in events {
match event.event_type() {
// New device
EventType::Add => {
info!(self.logger, "Device Added");
if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) {
let mut device = {
match DrmDevice::new(
{
let logger = self.logger.clone();
match self.session.open(
path,
fcntl::OFlag::O_RDWR | fcntl::OFlag::O_CLOEXEC
| fcntl::OFlag::O_NOCTTY
| fcntl::OFlag::O_NONBLOCK,
) {
Ok(fd) => SessionFdDrmDevice(fd),
Err(err) => {
warn!(
logger,
"Unable to open drm device {:?}, Error: {:?}. Skipping",
path,
err
);
continue;
}
}
},
self.logger.clone(),
) {
Ok(dev) => dev,
Err(err) => {
warn!(
self.logger,
"Failed to initialize device {:?}. Error: {}. Skipping",
path,
err
);
continue;
}
}
};
let fd = device.as_raw_fd();
match self.handler.device_added(&mut device) {
Some(drm_handler) => {
match drm_device_bind(&self.token, device, drm_handler) {
Ok(fd_event_source) => {
self.devices.borrow_mut().insert(devnum, fd_event_source);
}
Err((err, (mut device, _))) => {
warn!(
logger,
"Unable to open drm device {:?}, Error: {:?}. Skipping",
path,
err
self.logger,
"Failed to bind device. Error: {:?}.", err
);
continue;
self.handler.device_removed(&mut device);
drop(device);
if let Err(err) = self.session.close(fd) {
warn!(
self.logger,
"Failed to close dropped device. Error: {:?}. Ignoring", err
);
};
}
}
},
udev.logger.clone(),
) {
Ok(dev) => dev,
Err(err) => {
warn!(
udev.logger,
"Failed to initialize device {:?}. Error: {}. Skipping",
path,
err
);
continue;
}
}
};
let fd = device.as_raw_fd();
match udev.handler.device_added(evlh, &mut device) {
Some(drm_handler) => match drm_device_bind(&mut evlh, device, drm_handler) {
Ok(fd_event_source) => {
udev.devices.borrow_mut().insert(devnum, fd_event_source);
}
Err((err, (mut device, _))) => {
warn!(udev.logger, "Failed to bind device. Error: {:?}.", err);
udev.handler.device_removed(evlh, &mut device);
None => {
self.handler.device_removed(&mut device);
drop(device);
if let Err(err) = udev.session.close(fd) {
if let Err(err) = self.session.close(fd) {
warn!(
udev.logger,
"Failed to close dropped device. Error: {:?}. Ignoring", err
self.logger,
"Failed to close unused device. Error: {:?}", err
);
};
}
}
},
None => {
udev.handler.device_removed(evlh, &mut device);
drop(device);
if let Err(err) = udev.session.close(fd) {
warn!(
udev.logger,
"Failed to close unused device. Error: {:?}", err
);
}
}
};
}
}
// Device removed
EventType::Remove => {
info!(udev.logger, "Device Remove");
if let Some(devnum) = event.devnum() {
if let Some(fd_event_source) = udev.devices.borrow_mut().remove(&devnum) {
let (mut device, _) = fd_event_source.remove();
udev.handler.device_removed(evlh, &mut device);
let fd = device.as_raw_fd();
drop(device);
if let Err(err) = udev.session.close(fd) {
warn!(
udev.logger,
"Failed to close device {:?}. Error: {:?}. Ignoring",
event.sysname(),
err
);
};
}
}
}
// New connector
EventType::Change => {
info!(udev.logger, "Device Changed");
if let Some(devnum) = event.devnum() {
info!(udev.logger, "Devnum: {:b}", devnum);
if let Some(fd_event_source) = udev.devices.borrow_mut().get_mut(&devnum) {
let handler = &mut udev.handler;
let logger = &udev.logger;
fd_event_source.with_idata(evlh, move |&mut (ref mut device, _), evlh| {
info!(logger, "changed successful");
handler.device_changed(evlh, device);
})
} else {
info!(udev.logger, "changed, but device not tracked by backend");
};
} else {
info!(udev.logger, "changed, but no devnum");
// Device removed
EventType::Remove => {
info!(self.logger, "Device Remove");
if let Some(devnum) = event.devnum() {
if let Some((fd_event_source, device)) =
self.devices.borrow_mut().remove(&devnum)
{
fd_event_source.remove();
let mut device = Rc::try_unwrap(device)
.unwrap_or_else(|_| unreachable!())
.into_inner();
self.handler.device_removed(&mut device);
let fd = device.as_raw_fd();
drop(device);
if let Err(err) = self.session.close(fd) {
warn!(
self.logger,
"Failed to close device {:?}. Error: {:?}. Ignoring",
event.sysname(),
err
);
};
}
}
}
// New connector
EventType::Change => {
info!(self.logger, "Device Changed");
if let Some(devnum) = event.devnum() {
info!(self.logger, "Devnum: {:b}", devnum);
if let Some(&(_, ref device)) = self.devices.borrow_mut().get(&devnum) {
let handler = &mut self.handler;
handler.device_changed(&mut device.borrow_mut());
} else {
info!(self.logger, "changed, but device not tracked by backend");
};
} else {
info!(self.logger, "changed, but no devnum");
}
}
_ => {}
}
_ => {}
}
}
},
error: |evlh, udev, _, err| udev.handler.error(evlh, err),
FdEvent::Error { error, .. } => self.handler.error(error),
}
}
}
@ -358,9 +376,7 @@ pub trait UdevHandler<H: DrmHandler<SessionFdDrmDevice> + 'static> {
///
/// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn device_added(
&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>
) -> Option<H>;
fn device_added(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) -> Option<H>;
/// Called when an open device is changed.
///
/// This usually indicates that some connectors did become available or were unplugged. The handler
@ -368,7 +384,7 @@ pub trait UdevHandler<H: DrmHandler<SessionFdDrmDevice> + 'static> {
///
/// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn device_changed(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>);
fn device_changed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>);
/// Called when a device was removed.
///
/// The device will not accept any operations anymore and its file descriptor will be closed once
@ -376,12 +392,12 @@ pub trait UdevHandler<H: DrmHandler<SessionFdDrmDevice> + 'static> {
///
/// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn device_removed(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>);
fn device_removed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>);
/// Called when the udev context has encountered and error.
///
/// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn error(&mut self, evlh: &mut EventLoopHandle, error: IoError);
fn error(&mut self, error: IoError);
}
/// Returns the path of the primary gpu device if any

View File

@ -18,7 +18,7 @@ use std::fmt;
use std::rc::Rc;
use std::time::Instant;
use wayland_client::egl as wegl;
use wayland_server::{Display, EventLoopHandle};
use wayland_server::Display;
use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor,
MouseScrollDelta, Touch, TouchPhase, Window as WinitWindow, WindowBuilder, WindowEvent};
@ -102,7 +102,8 @@ where
/// graphics backend trait, from a given `WindowBuilder` struct and a corresponding
/// `WinitInputBackend`, which implements the `InputBackend` trait
pub fn init_from_builder<L>(
builder: WindowBuilder, logger: L
builder: WindowBuilder,
logger: L,
) -> Result<(WinitGraphicsBackend, WinitInputBackend)>
where
L: Into<Option<::slog::Logger>>,
@ -124,7 +125,9 @@ where
/// `GlAttributes` for further customization of the rendering pipeline and a
/// corresponding `WinitInputBackend`, which implements the `InputBackend` trait.
pub fn init_from_builder_with_gl_attr<L>(
builder: WindowBuilder, attributes: GlAttributes, logger: L
builder: WindowBuilder,
attributes: GlAttributes,
logger: L,
) -> Result<(WinitGraphicsBackend, WinitInputBackend)>
where
L: Into<Option<::slog::Logger>>,
@ -185,15 +188,15 @@ where
/// Handler trait to recieve window-related events to provide a better *nested* experience.
pub trait WinitEventsHandler {
/// The window was resized, can be used to adjust the associated `wayland::output::Output`s mode.
fn resized(&mut self, evlh: &mut EventLoopHandle, width: u32, height: u32);
fn resized(&mut self, width: u32, height: u32);
/// The window was moved
fn moved(&mut self, evlh: &mut EventLoopHandle, x: i32, h: i32);
fn moved(&mut self, x: i32, h: i32);
/// The window gained or lost focus
fn focus_changed(&mut self, evlh: &mut EventLoopHandle, focused: bool);
fn focus_changed(&mut self, focused: bool);
/// The window needs to be redrawn
fn refresh(&mut self, evlh: &mut EventLoopHandle);
fn refresh(&mut self);
/// The window's hidpi factor changed
fn hidpi_changed(&mut self, evlh: &mut EventLoopHandle, scale: f32);
fn hidpi_changed(&mut self, scale: f32);
}
impl WinitGraphicsBackend {
@ -213,7 +216,9 @@ impl GraphicsBackend for WinitGraphicsBackend {
}
fn set_cursor_representation(
&self, cursor: &Self::CursorFormat, _hotspot: (u32, u32)
&self,
cursor: &Self::CursorFormat,
_hotspot: (u32, u32),
) -> ::std::result::Result<(), ()> {
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
debug!(self.logger, "Changing cursor representation");
@ -621,13 +626,13 @@ impl InputBackend for WinitInputBackend {
type TouchCancelEvent = WinitTouchCancelledEvent;
type TouchFrameEvent = UnusedEvent;
fn set_handler<H: InputHandler<Self> + 'static>(&mut self, evlh: &mut EventLoopHandle, mut handler: H) {
fn set_handler<H: InputHandler<Self> + 'static>(&mut self, mut handler: H) {
if self.handler.is_some() {
self.clear_handler(evlh);
self.clear_handler();
}
info!(self.logger, "New input handler set.");
trace!(self.logger, "Calling on_seat_created with {:?}", self.seat);
handler.on_seat_created(evlh, &self.seat);
handler.on_seat_created(&self.seat);
self.handler = Some(Box::new(handler));
}
@ -637,14 +642,14 @@ impl InputBackend for WinitInputBackend {
.map(|handler| handler as &mut InputHandler<Self>)
}
fn clear_handler(&mut self, evlh: &mut EventLoopHandle) {
fn clear_handler(&mut self) {
if let Some(mut handler) = self.handler.take() {
trace!(
self.logger,
"Calling on_seat_destroyed with {:?}",
self.seat
);
handler.on_seat_destroyed(evlh, &self.seat);
handler.on_seat_destroyed(&self.seat);
}
info!(self.logger, "Removing input handler");
}
@ -665,9 +670,7 @@ impl InputBackend for WinitInputBackend {
///
/// The linked `WinitGraphicsBackend` will error with a lost Context and should
/// not be used anymore as well.
fn dispatch_new_events(
&mut self, evlh: &mut EventLoopHandle
) -> ::std::result::Result<(), WinitInputError> {
fn dispatch_new_events(&mut self) -> ::std::result::Result<(), WinitInputError> {
let mut closed = false;
{
@ -701,18 +704,16 @@ impl InputBackend for WinitInputBackend {
_ => {}
};
if let Some(events_handler) = events_handler {
events_handler.resized(evlh, w, h);
events_handler.resized(w, h);
}
}
(WindowEvent::Moved(x, y), _, Some(events_handler)) => {
events_handler.moved(evlh, x, y)
}
(WindowEvent::Moved(x, y), _, Some(events_handler)) => events_handler.moved(x, y),
(WindowEvent::Focused(focus), _, Some(events_handler)) => {
events_handler.focus_changed(evlh, focus)
events_handler.focus_changed(focus)
}
(WindowEvent::Refresh, _, Some(events_handler)) => events_handler.refresh(evlh),
(WindowEvent::Refresh, _, Some(events_handler)) => events_handler.refresh(),
(WindowEvent::HiDPIFactorChanged(factor), _, Some(events_handler)) => {
events_handler.hidpi_changed(evlh, factor)
events_handler.hidpi_changed(factor)
}
(
WindowEvent::KeyboardInput {
@ -737,7 +738,6 @@ impl InputBackend for WinitInputBackend {
(scancode, state)
);
handler.on_keyboard_key(
evlh,
seat,
WinitKeyboardInputEvent {
time,
@ -756,7 +756,6 @@ impl InputBackend for WinitInputBackend {
) => {
trace!(logger, "Calling on_pointer_move_absolute with {:?}", (x, y));
handler.on_pointer_move_absolute(
evlh,
seat,
WinitMouseMovedEvent {
window: window.clone(),
@ -769,7 +768,7 @@ impl InputBackend for WinitInputBackend {
(WindowEvent::MouseWheel { delta, .. }, Some(handler), _) => {
let event = WinitMouseWheelEvent { time, delta };
trace!(logger, "Calling on_pointer_axis with {:?}", delta);
handler.on_pointer_axis(evlh, seat, event);
handler.on_pointer_axis(seat, event);
}
(WindowEvent::MouseInput { state, button, .. }, Some(handler), _) => {
trace!(
@ -778,7 +777,6 @@ impl InputBackend for WinitInputBackend {
(button, state)
);
handler.on_pointer_button(
evlh,
seat,
WinitMouseInputEvent {
time,
@ -799,7 +797,6 @@ impl InputBackend for WinitInputBackend {
) => {
trace!(logger, "Calling on_touch_down at {:?}", (x, y));
handler.on_touch_down(
evlh,
seat,
WinitTouchStartedEvent {
window: window.clone(),
@ -821,7 +818,6 @@ impl InputBackend for WinitInputBackend {
) => {
trace!(logger, "Calling on_touch_motion at {:?}", (x, y));
handler.on_touch_motion(
evlh,
seat,
WinitTouchMovedEvent {
window: window.clone(),
@ -843,7 +839,6 @@ impl InputBackend for WinitInputBackend {
) => {
trace!(logger, "Calling on_touch_motion at {:?}", (x, y));
handler.on_touch_motion(
evlh,
seat,
WinitTouchMovedEvent {
window: window.clone(),
@ -853,7 +848,7 @@ impl InputBackend for WinitInputBackend {
},
);
trace!(logger, "Calling on_touch_up");
handler.on_touch_up(evlh, seat, WinitTouchEndedEvent { time, id: id });
handler.on_touch_up(seat, WinitTouchEndedEvent { time, id: id });
}
(
WindowEvent::Touch(Touch {
@ -865,7 +860,7 @@ impl InputBackend for WinitInputBackend {
_,
) => {
trace!(logger, "Calling on_touch_cancel");
handler.on_touch_cancel(evlh, seat, WinitTouchCancelledEvent { time, id: id })
handler.on_touch_cancel(seat, WinitTouchCancelledEvent { time, id: id })
}
(WindowEvent::Closed, _, _) => {
warn!(logger, "Window closed");

View File

@ -1,168 +1,188 @@
use super::{CompositorToken, Damage, Rectangle, RectangleKind, Role, RoleType, SubsurfaceRole,
SurfaceUserImplementation};
use super::{CompositorToken, Damage, Rectangle, RectangleKind, Role, RoleType, SubsurfaceRole, SurfaceEvent};
use super::region::RegionData;
use super::tree::{Location, SurfaceData};
use std::cell::RefCell;
use std::rc::Rc;
use wayland_server::{Client, EventLoopHandle, Liveness, Resource};
use wayland_server::{LoopToken, NewResource, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_compositor, wl_region, wl_subcompositor, wl_subsurface, wl_surface};
/*
* wl_compositor
*/
pub(crate) fn compositor_bind<U, R, ID>(
evlh: &mut EventLoopHandle, idata: &mut SurfaceIData<U, R, ID>, _: &Client,
compositor: wl_compositor::WlCompositor,
) where
U: Default + 'static,
R: Default + 'static,
ID: 'static,
{
trace!(idata.log, "Binding a new wl_compositor.");
evlh.register(
&compositor,
compositor_implementation::<U, R, ID>(),
idata.clone(),
None,
);
}
fn compositor_implementation<U, R, ID>() -> wl_compositor::Implementation<SurfaceIData<U, R, ID>>
pub(crate) fn implement_compositor<U, R, Impl>(
compositor: NewResource<wl_compositor::WlCompositor>,
token: LoopToken,
log: ::slog::Logger,
implem: Rc<RefCell<Impl>>,
) -> Resource<wl_compositor::WlCompositor>
where
U: Default + 'static,
R: Default + 'static,
ID: 'static,
Impl: Implementation<(Resource<wl_surface::WlSurface>, CompositorToken<U, R>), SurfaceEvent> + 'static,
{
wl_compositor::Implementation {
create_surface: |evlh, idata, _, _, surface| {
unsafe { SurfaceData::<U, R>::init(&surface) };
evlh.register(
&surface,
surface_implementation::<U, R, ID>(),
idata.clone(),
Some(destroy_surface::<U, R>),
);
let my_token = token.clone();
compositor.implement_nonsend(
move |request, _compositor| match request {
wl_compositor::Request::CreateSurface { id } => {
trace!(log, "Creating a new wl_surface.");
implement_surface(id, &token, log.clone(), implem.clone());
}
wl_compositor::Request::CreateRegion { id } => {
trace!(log, "Creating a new wl_region.");
implement_region(id, &token);
}
},
create_region: |evlh, _, _, _, region| {
unsafe { RegionData::init(&region) };
evlh.register(&region, region_implementation(), (), Some(destroy_region));
},
}
None::<fn(_, _)>,
&my_token,
)
}
/*
* wl_surface
*/
/// Internal implementation data of surfaces
///
/// This type is only visible as type parameter of
/// the `Global` handle you are provided.
pub struct SurfaceIData<U, R, ID> {
// Internal implementation data of surfaces
pub(crate) struct SurfaceImplem<U, R> {
log: ::slog::Logger,
implem: SurfaceUserImplementation<U, R, ID>,
idata: Rc<RefCell<ID>>,
implem:
Rc<RefCell<Implementation<(Resource<wl_surface::WlSurface>, CompositorToken<U, R>), SurfaceEvent>>>,
}
impl<U, R, ID> SurfaceIData<U, R, ID> {
pub(crate) fn make(
log: ::slog::Logger, implem: SurfaceUserImplementation<U, R, ID>, idata: ID
) -> SurfaceIData<U, R, ID> {
SurfaceIData {
impl<U, R> SurfaceImplem<U, R> {
fn make<Impl>(log: ::slog::Logger, implem: Rc<RefCell<Impl>>) -> SurfaceImplem<U, R>
where
Impl: Implementation<(Resource<wl_surface::WlSurface>, CompositorToken<U, R>), SurfaceEvent>
+ 'static,
{
SurfaceImplem {
log: log,
implem: implem,
idata: Rc::new(RefCell::new(idata)),
}
}
}
impl<U, R, ID> Clone for SurfaceIData<U, R, ID> {
fn clone(&self) -> SurfaceIData<U, R, ID> {
SurfaceIData {
log: self.log.clone(),
implem: self.implem,
idata: self.idata.clone(),
impl<U, R> Implementation<Resource<wl_surface::WlSurface>, wl_surface::Request> for SurfaceImplem<U, R>
where
U: 'static,
R: 'static,
{
fn receive(&mut self, req: wl_surface::Request, surface: Resource<wl_surface::WlSurface>) {
match req {
wl_surface::Request::Attach { buffer, x, y } => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| {
d.buffer = Some(buffer.map(|b| (b.clone(), (x, y))))
});
},
wl_surface::Request::Damage {
x,
y,
width,
height,
} => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| {
d.damage = Damage::Surface(Rectangle {
x,
y,
width,
height,
})
});
},
wl_surface::Request::Frame { callback } => {
let mut user_impl = self.implem.borrow_mut();
trace!(self.log, "Calling user implementation for wl_surface.frame");
user_impl.receive(
SurfaceEvent::Frame { callback },
(surface, CompositorToken::make()),
);
}
wl_surface::Request::SetOpaqueRegion { region } => unsafe {
let attributes = region.map(|r| RegionData::get_attributes(&r));
SurfaceData::<U, R>::with_data(&surface, |d| d.opaque_region = attributes);
},
wl_surface::Request::SetInputRegion { region } => unsafe {
let attributes = region.map(|r| RegionData::get_attributes(&r));
SurfaceData::<U, R>::with_data(&surface, |d| d.input_region = attributes);
},
wl_surface::Request::Commit => {
let mut user_impl = self.implem.borrow_mut();
trace!(
self.log,
"Calling user implementation for wl_surface.commit"
);
user_impl.receive(SurfaceEvent::Commit, (surface, CompositorToken::make()));
}
wl_surface::Request::SetBufferTransform { transform } => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| d.buffer_transform = transform);
},
wl_surface::Request::SetBufferScale { scale } => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| d.buffer_scale = scale);
},
wl_surface::Request::DamageBuffer {
x,
y,
width,
height,
} => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| {
d.damage = Damage::Buffer(Rectangle {
x,
y,
width,
height,
})
});
},
wl_surface::Request::Destroy => {
// All is already handled by our destructor
}
}
}
}
pub(crate) fn surface_implementation<U: 'static, R: 'static, ID: 'static>(
) -> wl_surface::Implementation<SurfaceIData<U, R, ID>> {
wl_surface::Implementation {
attach: |_, _, _, surface, buffer, x, y| unsafe {
SurfaceData::<U, R>::with_data(surface, |d| {
d.buffer = Some(buffer.map(|b| (b.clone_unchecked(), (x, y))))
});
},
damage: |_, _, _, surface, x, y, width, height| unsafe {
SurfaceData::<U, R>::with_data(surface, |d| {
d.damage = Damage::Surface(Rectangle {
x,
y,
width,
height,
})
});
},
frame: |evlh, idata, _, surface, callback| {
let mut user_idata = idata.idata.borrow_mut();
trace!(idata.log, "Calling user callback for wl_surface.frame");
(idata.implem.frame)(
evlh,
&mut *user_idata,
surface,
callback,
CompositorToken::make(),
)
},
set_opaque_region: |_, _, _, surface, region| unsafe {
let attributes = region.map(|r| RegionData::get_attributes(r));
SurfaceData::<U, R>::with_data(surface, |d| d.opaque_region = attributes);
},
set_input_region: |_, _, _, surface, region| unsafe {
let attributes = region.map(|r| RegionData::get_attributes(r));
SurfaceData::<U, R>::with_data(surface, |d| d.input_region = attributes);
},
commit: |evlh, idata, _, surface| {
let mut user_idata = idata.idata.borrow_mut();
trace!(idata.log, "Calling user callback for wl_surface.commit");
(idata.implem.commit)(evlh, &mut *user_idata, surface, CompositorToken::make())
},
set_buffer_transform: |_, _, _, surface, transform| unsafe {
SurfaceData::<U, R>::with_data(surface, |d| d.buffer_transform = transform);
},
set_buffer_scale: |_, _, _, surface, scale| unsafe {
SurfaceData::<U, R>::with_data(surface, |d| d.buffer_scale = scale);
},
damage_buffer: |_, _, _, surface, x, y, width, height| unsafe {
SurfaceData::<U, R>::with_data(surface, |d| {
d.damage = Damage::Buffer(Rectangle {
x,
y,
width,
height,
})
});
},
destroy: |_, _, _, _| {},
fn implement_surface<U, R, Impl>(
surface: NewResource<wl_surface::WlSurface>,
token: &LoopToken,
log: ::slog::Logger,
implem: Rc<RefCell<Impl>>,
) -> Resource<wl_surface::WlSurface>
where
U: Default + 'static,
R: Default + 'static,
Impl: Implementation<(Resource<wl_surface::WlSurface>, CompositorToken<U, R>), SurfaceEvent> + 'static,
{
let surface = surface.implement_nonsend(
SurfaceImplem::make(log, implem),
Some(|surface, _| unsafe {
SurfaceData::<U, R>::cleanup(&surface);
}),
token,
);
unsafe {
SurfaceData::<U, R>::init(&surface);
}
}
fn destroy_surface<U: 'static, R: 'static>(surface: &wl_surface::WlSurface) {
unsafe { SurfaceData::<U, R>::cleanup(surface) }
surface
}
/*
* wl_region
*/
pub(crate) fn region_implementation() -> wl_region::Implementation<()> {
wl_region::Implementation {
add: |_, _, _, region, x, y, width, height| {
unsafe {
RegionData::add_rectangle(
region,
pub(crate) struct RegionImplem;
impl Implementation<Resource<wl_region::WlRegion>, wl_region::Request> for RegionImplem {
fn receive(&mut self, request: wl_region::Request, region: Resource<wl_region::WlRegion>) {
unsafe {
match request {
wl_region::Request::Add {
x,
y,
width,
height,
} => RegionData::add_rectangle(
&region,
RectangleKind::Add,
Rectangle {
x,
@ -170,13 +190,14 @@ pub(crate) fn region_implementation() -> wl_region::Implementation<()> {
width,
height,
},
)
};
},
subtract: |_, _, _, region, x, y, width, height| {
unsafe {
RegionData::add_rectangle(
region,
),
wl_region::Request::Subtract {
x,
y,
width,
height,
} => RegionData::add_rectangle(
&region,
RectangleKind::Subtract,
Rectangle {
x,
@ -184,134 +205,148 @@ pub(crate) fn region_implementation() -> wl_region::Implementation<()> {
width,
height,
},
)
};
},
destroy: |_, _, _, _| {},
),
wl_region::Request::Destroy => {
// all is handled by our destructor
}
}
}
}
}
fn destroy_region(region: &wl_region::WlRegion) {
unsafe { RegionData::cleanup(region) };
fn implement_region(
region: NewResource<wl_region::WlRegion>,
token: &LoopToken,
) -> Resource<wl_region::WlRegion> {
let region = region.implement_nonsend(
RegionImplem,
Some(|region, _| unsafe { RegionData::cleanup(&region) }),
token,
);
unsafe {
RegionData::init(&region);
}
region
}
/*
* wl_subcompositor
*/
pub(crate) fn subcompositor_bind<U, R>(
evlh: &mut EventLoopHandle, _: &mut (), _: &Client, subcompositor: wl_subcompositor::WlSubcompositor
) where
R: RoleType + Role<SubsurfaceRole> + 'static,
U: 'static,
{
evlh.register(
&subcompositor,
subcompositor_implementation::<U, R>(),
(),
None,
);
}
fn subcompositor_implementation<U, R>() -> wl_subcompositor::Implementation<()>
pub(crate) fn implement_subcompositor<U, R>(
subcompositor: NewResource<wl_subcompositor::WlSubcompositor>,
token: LoopToken,
) -> Resource<wl_subcompositor::WlSubcompositor>
where
R: RoleType + Role<SubsurfaceRole> + 'static,
U: 'static,
{
wl_subcompositor::Implementation {
get_subsurface: |evlh, _, _, subcompositor, subsurface, surface, parent| {
if let Err(()) = unsafe { SurfaceData::<U, R>::set_parent(surface, parent) } {
subcompositor.post_error(
wl_subcompositor::Error::BadSurface as u32,
"Surface already has a role.".into(),
);
return;
let my_token = token.clone();
subcompositor.implement_nonsend(
move |request, subcompositor: Resource<_>| match request {
wl_subcompositor::Request::GetSubsurface {
id,
surface,
parent,
} => {
if let Err(()) = unsafe { SurfaceData::<U, R>::set_parent(&surface, &parent) } {
subcompositor.post_error(
wl_subcompositor::Error::BadSurface as u32,
"Surface already has a role.".into(),
);
return;
}
let subsurface = implement_subsurface::<U, R>(id, &token);
subsurface.set_user_data(Box::into_raw(Box::new(surface.clone())) as *mut ());
}
subsurface.set_user_data(Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _);
evlh.register(
&subsurface,
subsurface_implementation::<U, R>(),
(),
Some(destroy_subsurface::<U, R>),
);
wl_subcompositor::Request::Destroy => {}
},
destroy: |_, _, _, _| {},
}
None::<fn(_, _)>,
&my_token,
)
}
/*
* wl_subsurface
*/
unsafe fn with_subsurface_attributes<U, R, F>(subsurface: &wl_subsurface::WlSubsurface, f: F)
unsafe fn with_subsurface_attributes<U, R, F>(subsurface: &Resource<wl_subsurface::WlSubsurface>, f: F)
where
F: FnOnce(&mut SubsurfaceRole),
U: 'static,
R: RoleType + Role<SubsurfaceRole> + 'static,
{
let ptr = subsurface.get_user_data();
let surface = &*(ptr as *mut wl_surface::WlSurface);
let surface = &*(ptr as *mut Resource<wl_surface::WlSurface>);
SurfaceData::<U, R>::with_role_data::<SubsurfaceRole, _, _>(surface, |d| f(d))
.expect("The surface does not have a subsurface role while it has a wl_subsurface?!");
}
fn subsurface_implementation<U, R>() -> wl_subsurface::Implementation<()>
fn implement_subsurface<U, R>(
subsurface: NewResource<wl_subsurface::WlSubsurface>,
token: &LoopToken,
) -> Resource<wl_subsurface::WlSubsurface>
where
R: RoleType + Role<SubsurfaceRole> + 'static,
U: 'static,
R: RoleType + Role<SubsurfaceRole> + 'static,
{
wl_subsurface::Implementation {
set_position: |_, _, _, subsurface, x, y| unsafe {
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| {
attrs.x = x;
attrs.y = y;
});
},
place_above: |_, _, _, subsurface, sibling| unsafe {
let ptr = subsurface.get_user_data();
let surface = &*(ptr as *mut wl_surface::WlSurface);
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::After, sibling) {
subsurface.post_error(
wl_subsurface::Error::BadSurface as u32,
"Provided surface is not a sibling or parent.".into(),
);
subsurface.implement_nonsend(
|request, subsurface| unsafe {
match request {
wl_subsurface::Request::SetPosition { x, y } => {
with_subsurface_attributes::<U, R, _>(&subsurface, |attrs| {
attrs.location = (x, y);
})
}
wl_subsurface::Request::PlaceAbove { sibling } => {
let surface = &*(subsurface.get_user_data() as *mut Resource<wl_surface::WlSurface>);
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::After, &sibling) {
subsurface.post_error(
wl_subsurface::Error::BadSurface as u32,
"Provided surface is not a sibling or parent.".into(),
)
}
}
wl_subsurface::Request::PlaceBelow { sibling } => {
let surface = &*(subsurface.get_user_data() as *mut Resource<wl_surface::WlSurface>);
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::Before, &sibling) {
subsurface.post_error(
wl_subsurface::Error::BadSurface as u32,
"Provided surface is not a sibling or parent.".into(),
)
}
}
wl_subsurface::Request::SetSync => {
with_subsurface_attributes::<U, R, _>(&subsurface, |attrs| {
attrs.sync = true;
})
}
wl_subsurface::Request::SetDesync => {
with_subsurface_attributes::<U, R, _>(&subsurface, |attrs| {
attrs.sync = false;
})
}
wl_subsurface::Request::Destroy => {
// Our destructor already handles it
}
}
},
place_below: |_, _, _, subsurface, sibling| unsafe {
let ptr = subsurface.get_user_data();
let surface = &*(ptr as *mut wl_surface::WlSurface);
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::Before, sibling) {
subsurface.post_error(
wl_subsurface::Error::BadSurface as u32,
"Provided surface is not a sibling or parent.".into(),
);
}
},
set_sync: |_, _, _, subsurface| unsafe {
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| {
attrs.sync = true;
});
},
set_desync: |_, _, _, subsurface| unsafe {
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| {
attrs.sync = false;
});
},
destroy: |_, _, _, _| {},
}
Some(|subsurface, _| unsafe {
destroy_subsurface::<U, R>(&subsurface);
}),
token,
)
}
fn destroy_subsurface<U, R>(subsurface: &wl_subsurface::WlSubsurface)
unsafe fn destroy_subsurface<U, R>(subsurface: &Resource<wl_subsurface::WlSubsurface>)
where
U: 'static,
R: RoleType + Role<SubsurfaceRole> + 'static,
{
let ptr = subsurface.get_user_data();
subsurface.set_user_data(::std::ptr::null_mut());
unsafe {
let surface = Box::from_raw(ptr as *mut wl_surface::WlSurface);
if surface.status() == Liveness::Alive {
SurfaceData::<U, R>::unset_parent(&surface);
}
let surface = Box::from_raw(ptr as *mut Resource<wl_surface::WlSurface>);
if surface.is_alive() {
SurfaceData::<U, R>::unset_parent(&surface);
}
}

View File

@ -29,9 +29,7 @@
//! ```
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! use wayland_server::protocol::wl_compositor::WlCompositor;
//! use wayland_server::protocol::wl_subcompositor::WlSubcompositor;
//! use smithay::wayland::compositor::{compositor_init, SurfaceUserImplementation};
//! use smithay::wayland::compositor::compositor_init;
//!
//! // Define some user data to be associated with the surfaces.
//! // It must implement the Default trait, which will represent the state of a surface which
@ -45,21 +43,18 @@
//! define_roles!(MyRoles);
//!
//! # fn main() {
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! // define your implementation for surface
//! let my_implementation = SurfaceUserImplementation {
//! commit: |evlh, idata, surface, token| { /* ... */ },
//! frame: |evlh, idata, surface, callback, token| { /* ... */ }
//! };
//! // define your implementation data
//! let my_implementation_data = ();
//!
//! # let (mut display, event_loop) = wayland_server::Display::new();
//! // Call the init function:
//! let (token, _, _) = compositor_init::<MyData, MyRoles, _, _>(
//! &mut event_loop,
//! my_implementation, // instance of compositor::SurfaceUserImplementation
//! my_implementation_data, // whatever implementation data you need
//! None // put a logger here
//! &mut display,
//! event_loop.token(),
//! |request, (surface, compositor_token)| {
//! /*
//! Your handling of the user requests. This closure can also
//! be a struct implementing the appropriate Implementation trait.
//! */
//! },
//! None // put a logger here
//! );
//!
//! // this `token` is what you'll use to access the surface associated data
@ -82,20 +77,23 @@
//! This `CompositorToken` also provides access to the metadata associated with the role of the
//! surfaces. See the documentation of the `roles` submodule for a detailed explanation.
use std::cell::RefCell;
use std::rc::Rc;
mod handlers;
mod tree;
mod region;
pub mod roles;
pub use self::handlers::SurfaceIData;
use self::region::RegionData;
use self::roles::{Role, RoleType, WrongRole};
use self::tree::SurfaceData;
pub use self::tree::TraversalAction;
use utils::Rectangle;
use wayland_server::{resource_is_registered, EventLoopHandle, Global};
use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region,
wl_subcompositor, wl_surface};
use wayland_server::{Display, Global, LoopToken, NewResource, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, wl_subcompositor};
use wayland_server::protocol::wl_surface::WlSurface;
/// Description of which part of a surface
/// should be considered damaged and needs to be redrawn
@ -135,7 +133,7 @@ pub struct SurfaceAttributes<U> {
/// You are free to set this field to `None` to avoid processing it several
/// times. It'll be set to `Some(...)` if the user attaches a buffer (or NULL) to
/// the surface.
pub buffer: Option<Option<(wl_buffer::WlBuffer, (i32, i32))>>,
pub buffer: Option<Option<(Resource<wl_buffer::WlBuffer>, (i32, i32))>>,
/// Scale of the contents of the buffer, for higher-resolution contents.
///
/// If it matches the one of the output displaying this surface, no change
@ -182,12 +180,9 @@ impl<U: Default> Default for SurfaceAttributes<U> {
/// Attributes defining the behaviour of a sub-surface relative to its parent
#[derive(Copy, Clone, Debug)]
pub struct SubsurfaceRole {
/// Horizontal location of the top-left corner of this sub-surface relative to
/// Location of the top-left corner of this sub-surface relative to
/// the top-left corner of its parent
pub x: i32,
/// Vertical location of the top-left corner of this sub-surface relative to
/// the top-left corner of its parent
pub y: i32,
pub location: (i32, i32),
/// Sync status of this sub-surface
///
/// If `true`, this surface should be repainted synchronously with its parent
@ -199,8 +194,7 @@ pub struct SubsurfaceRole {
impl Default for SubsurfaceRole {
fn default() -> SubsurfaceRole {
SubsurfaceRole {
x: 0,
y: 0,
location: (0, 0),
sync: true,
}
}
@ -240,58 +234,52 @@ impl Default for RegionAttributes {
/// This token can be cloned at will, and is the entry-point to
/// access data associated with the `wl_surface` and `wl_region` managed
/// by the `CompositorGlobal` that provided it.
pub struct CompositorToken<U, R, ID> {
pub struct CompositorToken<U, R> {
_data: ::std::marker::PhantomData<*mut U>,
_role: ::std::marker::PhantomData<*mut R>,
_idata: ::std::marker::PhantomData<*mut ID>,
}
// we implement them manually because #[derive(..)] would require
// U: Clone and R: Clone
impl<U, R, ID> Copy for CompositorToken<U, R, ID> {}
impl<U, R, ID> Clone for CompositorToken<U, R, ID> {
fn clone(&self) -> CompositorToken<U, R, ID> {
impl<U, R> Copy for CompositorToken<U, R> {}
impl<U, R> Clone for CompositorToken<U, R> {
fn clone(&self) -> CompositorToken<U, R> {
*self
}
}
impl<U, R, ID> CompositorToken<U, R, ID> {
pub(crate) fn make() -> CompositorToken<U, R, ID> {
impl<U, R> CompositorToken<U, R> {
pub(crate) fn make() -> CompositorToken<U, R> {
CompositorToken {
_data: ::std::marker::PhantomData,
_role: ::std::marker::PhantomData,
_idata: ::std::marker::PhantomData,
}
}
}
impl<U: 'static, R: 'static, ID: 'static> CompositorToken<U, R, ID> {
impl<U: 'static, R: 'static> CompositorToken<U, R> {
/// Access the data of a surface
///
/// The closure will be called with the contents of the data associated with this surface.
///
/// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn with_surface_data<F, T>(&self, surface: &wl_surface::WlSurface, f: F) -> T
pub fn with_surface_data<F, T>(&self, surface: &Resource<WlSurface>, f: F) -> T
where
F: FnOnce(&mut SurfaceAttributes<U>) -> T,
{
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe { SurfaceData::<U, R>::with_data(surface, f) }
}
}
impl<U, R, ID> CompositorToken<U, R, ID>
impl<U, R> CompositorToken<U, R>
where
U: 'static,
R: RoleType + Role<SubsurfaceRole> + 'static,
ID: 'static,
{
/// Access the data of a surface tree from bottom to top
///
@ -304,22 +292,22 @@ where
/// - The surface object itself
/// - a mutable reference to its surface attribute data
/// - a mutable reference to its role data,
/// - a custom value that is passer in a fold-like maneer, but only from the output of a parent
/// - a custom value that is passed in a fold-like maneer, but only from the output of a parent
/// to its children. See `TraversalAction` for details.
///
/// If the surface not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn with_surface_tree_upward<F, T>(
&self, surface: &wl_surface::WlSurface, initial: T, f: F
&self,
surface: &Resource<WlSurface>,
initial: T,
f: F,
) -> Result<(), ()>
where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
F: FnMut(&Resource<WlSurface>, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
{
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe {
@ -336,16 +324,16 @@ where
///
/// Behavior is the same as `with_surface_tree_upward`.
pub fn with_surface_tree_downward<F, T>(
&self, surface: &wl_surface::WlSurface, initial: T, f: F
&self,
surface: &Resource<WlSurface>,
initial: T,
f: F,
) -> Result<(), ()>
where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
F: FnMut(&Resource<WlSurface>, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
{
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe {
@ -360,12 +348,9 @@ where
///
/// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn get_parent(&self, surface: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> {
pub fn get_parent(&self, surface: &Resource<WlSurface>) -> Option<Resource<WlSurface>> {
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe { SurfaceData::<U, R>::get_parent(surface) }
@ -375,29 +360,23 @@ where
///
/// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn get_children(&self, surface: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> {
pub fn get_children(&self, surface: &Resource<WlSurface>) -> Vec<Resource<WlSurface>> {
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe { SurfaceData::<U, R>::get_children(surface) }
}
}
impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
impl<U: 'static, R: RoleType + 'static> CompositorToken<U, R> {
/// Check wether this surface as a role or not
///
/// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn has_a_role(&self, surface: &wl_surface::WlSurface) -> bool {
pub fn has_a_role(&self, surface: &Resource<WlSurface>) -> bool {
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe { SurfaceData::<U, R>::has_a_role(surface) }
@ -407,15 +386,12 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
///
/// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn has_role<RoleData>(&self, surface: &wl_surface::WlSurface) -> bool
pub fn has_role<RoleData>(&self, surface: &Resource<WlSurface>) -> bool
where
R: Role<RoleData>,
{
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe { SurfaceData::<U, R>::has_role::<RoleData>(surface) }
@ -427,16 +403,13 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
///
/// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn give_role<RoleData>(&self, surface: &wl_surface::WlSurface) -> Result<(), ()>
pub fn give_role<RoleData>(&self, surface: &Resource<WlSurface>) -> Result<(), ()>
where
R: Role<RoleData>,
RoleData: Default,
{
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe { SurfaceData::<U, R>::give_role::<RoleData>(surface) }
@ -449,16 +422,15 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
/// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn give_role_with<RoleData>(
&self, surface: &wl_surface::WlSurface, data: RoleData
&self,
surface: &Resource<WlSurface>,
data: RoleData,
) -> Result<(), RoleData>
where
R: Role<RoleData>,
{
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe { SurfaceData::<U, R>::give_role_with::<RoleData>(surface, data) }
@ -470,18 +442,13 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
///
/// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn with_role_data<RoleData, F, T>(
&self, surface: &wl_surface::WlSurface, f: F
) -> Result<T, WrongRole>
pub fn with_role_data<RoleData, F, T>(&self, surface: &Resource<WlSurface>, f: F) -> Result<T, WrongRole>
where
R: Role<RoleData>,
F: FnOnce(&mut RoleData) -> T,
{
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe { SurfaceData::<U, R>::with_role_data::<RoleData, _, _>(surface, f) }
@ -493,15 +460,12 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
///
/// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn remove_role<RoleData>(&self, surface: &wl_surface::WlSurface) -> Result<RoleData, WrongRole>
pub fn remove_role<RoleData>(&self, surface: &Resource<WlSurface>) -> Result<RoleData, WrongRole>
where
R: Role<RoleData>,
{
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
"Accessing the data of foreign surfaces is not supported."
);
unsafe { SurfaceData::<U, R>::remove_role::<RoleData>(surface) }
@ -511,10 +475,10 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
///
/// If the region is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn get_region_attributes(&self, region: &wl_region::WlRegion) -> RegionAttributes {
pub fn get_region_attributes(&self, region: &Resource<wl_region::WlRegion>) -> RegionAttributes {
assert!(
resource_is_registered(region, &self::handlers::region_implementation()),
"Accessing the data of foreign surfaces is not supported."
region.is_implemented_with::<self::handlers::RegionImplem>(),
"Accessing the data of foreign regions is not supported."
);
unsafe { RegionData::get_attributes(region) }
}
@ -528,46 +492,50 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
///
/// It also returns the two global handles, in case you whish to remove these
/// globals from the event loop in the future.
pub fn compositor_init<U, R, ID, L>(
evlh: &mut EventLoopHandle, implem: SurfaceUserImplementation<U, R, ID>, idata: ID, logger: L
pub fn compositor_init<U, R, Impl, L>(
display: &mut Display,
token: LoopToken,
implem: Impl,
logger: L,
) -> (
CompositorToken<U, R, ID>,
Global<wl_compositor::WlCompositor, self::handlers::SurfaceIData<U, R, ID>>,
Global<wl_subcompositor::WlSubcompositor, ()>,
CompositorToken<U, R>,
Global<wl_compositor::WlCompositor>,
Global<wl_subcompositor::WlSubcompositor>,
)
where
L: Into<Option<::slog::Logger>>,
U: Default + 'static,
R: Default + RoleType + Role<SubsurfaceRole> + 'static,
ID: 'static,
Impl: Implementation<(Resource<WlSurface>, CompositorToken<U, R>), SurfaceEvent> + 'static,
{
let log = ::slog_or_stdlog(logger);
let idata = self::handlers::SurfaceIData::make(
log.new(o!("smithay_module" => "compositor_handler")),
implem,
idata,
);
let compositor_global_token =
evlh.register_global::<wl_compositor::WlCompositor, _>(4, self::handlers::compositor_bind, idata);
let subcompositor_global_token = evlh.register_global::<wl_subcompositor::WlSubcompositor, _>(
1,
self::handlers::subcompositor_bind::<U, R>,
(),
);
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "compositor_handler"));
let implem = Rc::new(RefCell::new(implem));
(
CompositorToken::make(),
compositor_global_token,
subcompositor_global_token,
)
let comp_token = token.clone();
let sub_token = token.clone();
let compositor = display.create_global(&token, 4, move |_version, new_compositor| {
self::handlers::implement_compositor::<U, R, Impl>(
new_compositor,
comp_token.clone(),
log.clone(),
implem.clone(),
);
});
let subcompositor = display.create_global(&token, 1, move |_version, new_subcompositor| {
self::handlers::implement_subcompositor::<U, R>(new_subcompositor, sub_token.clone());
});
(CompositorToken::make(), compositor, subcompositor)
}
/// Sub-implementation for surface event handling
/// User-handled events for surfaces
///
/// The global provided by Smithay cannot process these events for you, so they
/// are forwarded directly to this implementation that you must provide
/// at creation of the compositor global.
pub struct SurfaceUserImplementation<U, R, ID> {
/// The global provided by smithay cannot process these events for you, so
/// they are forwarded directly via your provided implementation, and are
/// described by this global.
pub enum SurfaceEvent {
/// The double-buffered state has been validated by the client
///
/// At this point, the pending state that has been accumulated in the `SurfaceAttributes` associated
@ -575,12 +543,7 @@ pub struct SurfaceUserImplementation<U, R, ID> {
///
/// See [`wayland_server::protocol::wl_surface::Implementation::commit`](https://docs.rs/wayland-server/0.10.1/wayland_server/protocol/wl_surface/struct.Implementation.html#structfield.commit)
/// for more details
pub commit: fn(
evlh: &mut EventLoopHandle,
idata: &mut ID,
surface: &wl_surface::WlSurface,
token: CompositorToken<U, R, ID>,
),
Commit,
/// The client asks to be notified when would be a good time to update the contents of this surface
///
/// You must keep the provided `WlCallback` and trigger it at the appropriate time by calling
@ -588,18 +551,8 @@ pub struct SurfaceUserImplementation<U, R, ID> {
///
/// See [`wayland_server::protocol::wl_surface::Implementation::frame`](https://docs.rs/wayland-server/0.10.1/wayland_server/protocol/wl_surface/struct.Implementation.html#structfield.frame)
/// for more details
pub frame: fn(
evlh: &mut EventLoopHandle,
idata: &mut ID,
surface: &wl_surface::WlSurface,
callback: wl_callback::WlCallback,
token: CompositorToken<U, R, ID>,
),
}
impl<U, R, ID> Copy for SurfaceUserImplementation<U, R, ID> {}
impl<U, R, ID> Clone for SurfaceUserImplementation<U, R, ID> {
fn clone(&self) -> SurfaceUserImplementation<U, R, ID> {
*self
}
Frame {
/// The created `WlCallback`
callback: NewResource<wl_callback::WlCallback>,
},
}

View File

@ -10,29 +10,33 @@ pub struct RegionData {
impl RegionData {
/// Initialize the user_data of a region, must be called right when the surface is created
pub unsafe fn init(region: &wl_region::WlRegion) {
pub unsafe fn init(region: &Resource<wl_region::WlRegion>) {
region.set_user_data(Box::into_raw(Box::new(Mutex::new(RegionData::default()))) as *mut _)
}
/// Cleans the user_data of that surface, must be called when it is destroyed
pub unsafe fn cleanup(region: &wl_region::WlRegion) {
pub unsafe fn cleanup(region: &Resource<wl_region::WlRegion>) {
let ptr = region.get_user_data();
region.set_user_data(::std::ptr::null_mut());
let _my_data_mutex: Box<Mutex<RegionData>> = Box::from_raw(ptr as *mut _);
}
unsafe fn get_data(region: &wl_region::WlRegion) -> &Mutex<RegionData> {
unsafe fn get_data(region: &Resource<wl_region::WlRegion>) -> &Mutex<RegionData> {
let ptr = region.get_user_data();
&*(ptr as *mut _)
}
pub unsafe fn get_attributes(region: &wl_region::WlRegion) -> RegionAttributes {
pub unsafe fn get_attributes(region: &Resource<wl_region::WlRegion>) -> RegionAttributes {
let data_mutex = Self::get_data(region);
let data_guard = data_mutex.lock().unwrap();
data_guard.attributes.clone()
}
pub unsafe fn add_rectangle(region: &wl_region::WlRegion, kind: RectangleKind, rect: Rectangle) {
pub unsafe fn add_rectangle(
region: &Resource<wl_region::WlRegion>,
kind: RectangleKind,
rect: Rectangle,
) {
let data_mutex = Self::get_data(region);
let mut data_guard = data_mutex.lock().unwrap();
data_guard.attributes.rects.push((kind, rect));

View File

@ -206,7 +206,11 @@ macro_rules! define_roles(
}
}
fn data(&self) -> ::std::result::Result<&$role_data, $crate::wayland::compositor::roles::WrongRole> {
fn data(&self) -> ::std::result::Result<
&$role_data,
$crate::wayland::compositor::roles::WrongRole
>
{
if let $enum_name::$role_name(ref data) = *self {
Ok(data)
} else {
@ -214,7 +218,11 @@ macro_rules! define_roles(
}
}
fn data_mut(&mut self) -> ::std::result::Result<&mut $role_data, $crate::wayland::compositor::roles::WrongRole> {
fn data_mut(&mut self) -> ::std::result::Result<
&mut $role_data,
$crate::wayland::compositor::roles::WrongRole
>
{
if let $enum_name::$role_name(ref mut data) = *self {
Ok(data)
} else {
@ -222,7 +230,11 @@ macro_rules! define_roles(
}
}
fn unset(&mut self) -> ::std::result::Result<$role_data, $crate::wayland::compositor::roles::WrongRole> {
fn unset(&mut self) -> ::std::result::Result<
$role_data,
$crate::wayland::compositor::roles::WrongRole
>
{
// remove self to make borrow checker happy
let temp = ::std::mem::replace(self, $enum_name::NoRole);
if let $enum_name::$role_name(data) = temp {

View File

@ -1,8 +1,8 @@
use super::{SubsurfaceRole, SurfaceAttributes};
use super::roles::*;
use std::sync::Mutex;
use wayland_server::{Liveness, Resource};
use wayland_server::protocol::wl_surface;
use wayland_server::Resource;
use wayland_server::protocol::wl_surface::WlSurface;
/// Node of a subsurface tree, holding some user specified data type U
/// at each node
@ -24,8 +24,8 @@ use wayland_server::protocol::wl_surface;
/// All the methods here are unsafe, because they assume the provided `wl_surface` object
/// is correctly initialized regarding its `user_data`.
pub struct SurfaceData<U, R> {
parent: Option<wl_surface::WlSurface>,
children: Vec<wl_surface::WlSurface>,
parent: Option<Resource<WlSurface>>,
children: Vec<Resource<WlSurface>>,
role: R,
attributes: SurfaceAttributes<U>,
}
@ -56,7 +56,7 @@ impl<U: Default, R: Default> SurfaceData<U, R> {
}
/// Initialize the user_data of a surface, must be called right when the surface is created
pub unsafe fn init(surface: &wl_surface::WlSurface) {
pub unsafe fn init(surface: &Resource<WlSurface>) {
surface.set_user_data(Box::into_raw(Box::new(Mutex::new(SurfaceData::<U, R>::new()))) as *mut _)
}
}
@ -66,13 +66,13 @@ where
U: 'static,
R: 'static,
{
unsafe fn get_data(surface: &wl_surface::WlSurface) -> &Mutex<SurfaceData<U, R>> {
unsafe fn get_data(surface: &Resource<WlSurface>) -> &Mutex<SurfaceData<U, R>> {
let ptr = surface.get_user_data();
&*(ptr as *mut _)
}
/// Cleans the user_data of that surface, must be called when it is destroyed
pub unsafe fn cleanup(surface: &wl_surface::WlSurface) {
pub unsafe fn cleanup(surface: &Resource<WlSurface>) {
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
let my_data_mutex: Box<Mutex<SurfaceData<U, R>>> = Box::from_raw(ptr as *mut _);
@ -99,31 +99,31 @@ where
}
impl<U: 'static, R: RoleType + 'static> SurfaceData<U, R> {
pub unsafe fn has_a_role(surface: &wl_surface::WlSurface) -> bool {
debug_assert!(surface.status() == Liveness::Alive);
pub unsafe fn has_a_role(surface: &Resource<WlSurface>) -> bool {
debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface);
let data_guard = data_mutex.lock().unwrap();
<R as RoleType>::has_role(&data_guard.role)
}
/// Check wether a surface has a given role
pub unsafe fn has_role<RoleData>(surface: &wl_surface::WlSurface) -> bool
pub unsafe fn has_role<RoleData>(surface: &Resource<WlSurface>) -> bool
where
R: Role<RoleData>,
{
debug_assert!(surface.status() == Liveness::Alive);
debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface);
let data_guard = data_mutex.lock().unwrap();
<R as Role<RoleData>>::has(&data_guard.role)
}
/// Register that this surface has a role, fails if it already has one
pub unsafe fn give_role<RoleData>(surface: &wl_surface::WlSurface) -> Result<(), ()>
pub unsafe fn give_role<RoleData>(surface: &Resource<WlSurface>) -> Result<(), ()>
where
R: Role<RoleData>,
RoleData: Default,
{
debug_assert!(surface.status() == Liveness::Alive);
debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface);
let mut data_guard = data_mutex.lock().unwrap();
<R as Role<RoleData>>::set(&mut data_guard.role)
@ -133,12 +133,13 @@ impl<U: 'static, R: RoleType + 'static> SurfaceData<U, R> {
///
/// Fails if it already has one and returns the data
pub unsafe fn give_role_with<RoleData>(
surface: &wl_surface::WlSurface, data: RoleData
surface: &Resource<WlSurface>,
data: RoleData,
) -> Result<(), RoleData>
where
R: Role<RoleData>,
{
debug_assert!(surface.status() == Liveness::Alive);
debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface);
let mut data_guard = data_mutex.lock().unwrap();
<R as Role<RoleData>>::set_with(&mut data_guard.role, data)
@ -148,25 +149,23 @@ impl<U: 'static, R: RoleType + 'static> SurfaceData<U, R> {
///
/// It is a noop if this surface already didn't have one, but fails if
/// the role was "subsurface", it must be removed by the `unset_parent` method.
pub unsafe fn remove_role<RoleData>(surface: &wl_surface::WlSurface) -> Result<RoleData, WrongRole>
pub unsafe fn remove_role<RoleData>(surface: &Resource<WlSurface>) -> Result<RoleData, WrongRole>
where
R: Role<RoleData>,
{
debug_assert!(surface.status() == Liveness::Alive);
debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface);
let mut data_guard = data_mutex.lock().unwrap();
<R as Role<RoleData>>::unset(&mut data_guard.role)
}
/// Access to the role data
pub unsafe fn with_role_data<RoleData, F, T>(
surface: &wl_surface::WlSurface, f: F
) -> Result<T, WrongRole>
pub unsafe fn with_role_data<RoleData, F, T>(surface: &Resource<WlSurface>, f: F) -> Result<T, WrongRole>
where
R: Role<RoleData>,
F: FnOnce(&mut RoleData) -> T,
{
debug_assert!(surface.status() == Liveness::Alive);
debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface);
let mut data_guard = data_mutex.lock().unwrap();
let data = <R as Role<RoleData>>::data_mut(&mut data_guard.role)?;
@ -179,11 +178,9 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
///
/// if this surface already has a role, does nothing and fails, otherwise
/// its role is now to be a subsurface
pub unsafe fn set_parent(
child: &wl_surface::WlSurface, parent: &wl_surface::WlSurface
) -> Result<(), ()> {
debug_assert!(child.status() == Liveness::Alive);
debug_assert!(parent.status() == Liveness::Alive);
pub unsafe fn set_parent(child: &Resource<WlSurface>, parent: &Resource<WlSurface>) -> Result<(), ()> {
debug_assert!(child.is_alive());
debug_assert!(parent.is_alive());
// change child's parent
{
@ -192,14 +189,14 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
// if surface already has a role, it cannot become a subsurface
<R as Role<SubsurfaceRole>>::set(&mut child_guard.role)?;
debug_assert!(child_guard.parent.is_none());
child_guard.parent = Some(parent.clone_unchecked());
child_guard.parent = Some(parent.clone());
}
// register child to new parent
// double scoping is to be robust to have a child be its own parent
{
let parent_mutex = Self::get_data(parent);
let mut parent_guard = parent_mutex.lock().unwrap();
parent_guard.children.push(child.clone_unchecked())
parent_guard.children.push(child.clone())
}
Ok(())
}
@ -207,8 +204,8 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
/// Remove a pre-existing parent of this child
///
/// Does nothing if it has no parent
pub unsafe fn unset_parent(child: &wl_surface::WlSurface) {
debug_assert!(child.status() == Liveness::Alive);
pub unsafe fn unset_parent(child: &Resource<WlSurface>) {
debug_assert!(child.is_alive());
let old_parent = {
let child_mutex = Self::get_data(child);
let mut child_guard = child_mutex.lock().unwrap();
@ -229,44 +226,38 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
}
/// Retrieve the parent surface (if any) of this surface
pub unsafe fn get_parent(child: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> {
pub unsafe fn get_parent(child: &Resource<WlSurface>) -> Option<Resource<WlSurface>> {
let child_mutex = Self::get_data(child);
let child_guard = child_mutex.lock().unwrap();
child_guard.parent.as_ref().map(|p| p.clone_unchecked())
child_guard.parent.as_ref().map(|p| p.clone())
}
/// Retrieve the parent surface (if any) of this surface
pub unsafe fn get_children(child: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> {
pub unsafe fn get_children(child: &Resource<WlSurface>) -> Vec<Resource<WlSurface>> {
let child_mutex = Self::get_data(child);
let child_guard = child_mutex.lock().unwrap();
child_guard
.children
.iter()
.map(|p| p.clone_unchecked())
.collect()
child_guard.children.iter().map(|p| p.clone()).collect()
}
/// Reorders a surface relative to one of its sibling
///
/// Fails if `relative_to` is not a sibling or parent of `surface`.
pub unsafe fn reorder(
surface: &wl_surface::WlSurface, to: Location, relative_to: &wl_surface::WlSurface
surface: &Resource<WlSurface>,
to: Location,
relative_to: &Resource<WlSurface>,
) -> Result<(), ()> {
let parent = {
let data_mutex = Self::get_data(surface);
let data_guard = data_mutex.lock().unwrap();
data_guard
.parent
.as_ref()
.map(|p| p.clone_unchecked())
.unwrap()
data_guard.parent.as_ref().map(|p| p.clone()).unwrap()
};
if parent.equals(relative_to) {
// TODO: handle positioning relative to parent
return Ok(());
}
fn index_of(surface: &wl_surface::WlSurface, slice: &[wl_surface::WlSurface]) -> Option<usize> {
fn index_of(surface: &Resource<WlSurface>, slice: &[Resource<WlSurface>]) -> Option<usize> {
for (i, s) in slice.iter().enumerate() {
if s.equals(surface) {
return Some(i);
@ -300,7 +291,7 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
///
/// Note that an internal lock is taken during access of this data,
/// so the tree cannot be manipulated at the same time
pub unsafe fn with_data<T, F>(surface: &wl_surface::WlSurface, f: F) -> T
pub unsafe fn with_data<T, F>(surface: &Resource<WlSurface>, f: F) -> T
where
F: FnOnce(&mut SurfaceAttributes<U>) -> T,
{
@ -317,17 +308,20 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
///
/// The callback returns wether the traversal should continue or not. Returning
/// false will cause an early-stopping.
pub unsafe fn map_tree<F, T>(root: &wl_surface::WlSurface, initial: T, mut f: F, reverse: bool)
pub unsafe fn map_tree<F, T>(root: &Resource<WlSurface>, initial: T, mut f: F, reverse: bool)
where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
F: FnMut(&Resource<WlSurface>, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
{
// helper function for recursion
unsafe fn map<U: 'static, R: 'static, F, T>(
surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, initial: &T, f: &mut F,
surface: &Resource<WlSurface>,
root: &Resource<WlSurface>,
initial: &T,
f: &mut F,
reverse: bool,
) -> bool
where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
F: FnMut(&Resource<WlSurface>, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
{
// stop if we met the root, so to not deadlock/inifinte loop
if surface.equals(root) {

View File

@ -10,9 +10,9 @@
//! You need to instanciate an `Output` for each output global you want
//! to advertize to clients.
//!
//! Just insert it in your event loop using the `Output::new(..)` method.
//! It returns a state token that gives you access to the `Output` in order
//! to change it if needed (if the current resolution mode changes for example),
//! Just add it to your Display using the `Output::new(..)` method.
//! You can use the returned `Output` to change the properties of your
//! output (if the current resolution mode changes for example),
//! it'll automatically forward any changes to the clients.
//!
//! ```
@ -22,10 +22,11 @@
//! use wayland_server::protocol::wl_output;
//!
//! # fn main() {
//! # let (display, mut event_loop) = wayland_server::create_display();
//! // Insert the Output with given name and physical properties
//! let (output_state_token, _output_global) = Output::new(
//! &mut event_loop, // the event loop
//! # let (mut display, event_loop) = wayland_server::Display::new();
//! // Create the Output with given name and physical properties
//! let (output, _output_global) = Output::new(
//! &mut display, // the display
//! event_loop.token(), // the LoopToken
//! "output-0".into(), // the name of this output,
//! PhysicalProperties {
//! width: 200, // width in mm
@ -37,33 +38,33 @@
//! None // insert a logger here
//! );
//! // Now you can configure it
//! {
//! let output = event_loop.state().get_mut(&output_state_token);
//! // set the current state
//! output.change_current_state(
//! Some(Mode { width: 1902, height: 1080, refresh: 60000 }), // the resolution mode,
//! Some(wl_output::Transform::Normal), // global screen transformation
//! Some(1), // global screen scaling factor
//! );
//! // set the prefered mode
//! output.set_preferred(Mode { width: 1920, height: 1080, refresh: 60000 });
//! // add other supported modes
//! output.add_mode(Mode { width: 800, height: 600, refresh: 60000 });
//! output.add_mode(Mode { width: 1024, height: 768, refresh: 60000 });
//! }
//! output.change_current_state(
//! Some(Mode { width: 1902, height: 1080, refresh: 60000 }), // the resolution mode,
//! Some(wl_output::Transform::Normal), // global screen transformation
//! Some(1), // global screen scaling factor
//! );
//! // set the prefered mode
//! output.set_preferred(Mode { width: 1920, height: 1080, refresh: 60000 });
//! // add other supported modes
//! output.add_mode(Mode { width: 800, height: 600, refresh: 60000 });
//! output.add_mode(Mode { width: 1024, height: 768, refresh: 60000 });
//! # }
//! ```
use wayland_server::{Client, EventLoopHandle, Global, Liveness, Resource, StateToken};
use wayland_server::protocol::wl_output;
use std::sync::{Arc, Mutex};
use wayland_server::{Display, Global, LoopToken, NewResource, Resource};
use wayland_server::commons::{downcast_impl, Implementation};
use wayland_server::protocol::wl_output::{Event, Mode as WMode, Request, WlOutput};
pub use wayland_server::protocol::wl_output::{Subpixel, Transform};
#[derive(Copy, Clone, PartialEq)]
/// An output mode
///
/// A possible combination of dimensions and refresh rate for an output.
///
/// This should only describe the characteristics of the video driver,
/// not taking into account any global scaling.
#[derive(Copy, Clone, PartialEq)]
pub struct Mode {
/// The width in pixels
pub width: i32,
@ -82,68 +83,28 @@ pub struct PhysicalProperties {
/// The height in milimeters
pub height: i32,
/// The subpixel geometry
pub subpixel: wl_output::Subpixel,
pub subpixel: Subpixel,
/// Textual representation of the manufacturer
pub maker: String,
/// Textual representation of the model
pub model: String,
}
/// An output as seen by the clients
///
/// This handle is stored in the events loop, and allows you to notify clients
/// about any change in the properties of this output.
pub struct Output {
struct Inner {
name: String,
log: ::slog::Logger,
instances: Vec<wl_output::WlOutput>,
instances: Vec<Resource<WlOutput>>,
physical: PhysicalProperties,
location: (i32, i32),
transform: wl_output::Transform,
transform: Transform,
scale: i32,
modes: Vec<Mode>,
current_mode: Option<Mode>,
preferred_mode: Option<Mode>,
}
impl Output {
/// Create a new output global with given name and physical properties
///
/// The global is directly registered into the eventloop, and this function
/// returns the state token allowing you to access it, as well as the global handle,
/// in case you whish to remove this global in the future.
pub fn new<L>(
evlh: &mut EventLoopHandle, name: String, physical: PhysicalProperties, logger: L
) -> (
StateToken<Output>,
Global<wl_output::WlOutput, StateToken<Output>>,
)
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "output_handler"));
info!(log, "Creating new wl_output"; "name" => &name);
let token = evlh.state().insert(Output {
name: name,
log: log,
instances: Vec::new(),
physical: physical,
location: (0, 0),
transform: wl_output::Transform::Normal,
scale: 1,
modes: Vec::new(),
current_mode: None,
preferred_mode: None,
});
let global = evlh.register_global(3, output_bind, token.clone());
(token, global)
}
fn new_global(&mut self, output: wl_output::WlOutput) {
impl Inner {
fn new_global(&mut self, output: Resource<WlOutput>) {
trace!(self.log, "New global instanciated.");
if self.modes.is_empty() {
@ -158,51 +119,140 @@ impl Output {
self.send_geometry(&output);
for &mode in &self.modes {
let mut flags = wl_output::Mode::empty();
let mut flags = WMode::empty();
if Some(mode) == self.current_mode {
flags |= wl_output::Mode::Current;
flags |= WMode::Current;
}
if Some(mode) == self.preferred_mode {
flags |= wl_output::Mode::Preferred;
flags |= WMode::Preferred;
}
output.mode(flags, mode.width, mode.height, mode.refresh);
output.send(Event::Mode {
flags: flags,
width: mode.width,
height: mode.height,
refresh: mode.refresh,
});
}
if output.version() >= 2 {
output.scale(self.scale);
output.done();
output.send(Event::Scale { factor: self.scale });
output.send(Event::Done);
}
self.instances.push(output);
}
fn send_geometry(&self, output: &wl_output::WlOutput) {
output.geometry(
self.location.0,
self.location.1,
self.physical.width,
self.physical.height,
self.physical.subpixel,
self.physical.maker.clone(),
self.physical.model.clone(),
self.transform,
);
fn send_geometry(&self, output: &Resource<WlOutput>) {
output.send(Event::Geometry {
x: self.location.0,
y: self.location.1,
physical_width: self.physical.width,
physical_height: self.physical.height,
subpixel: self.physical.subpixel,
make: self.physical.maker.clone(),
model: self.physical.model.clone(),
transform: self.transform,
});
}
}
struct InnerWrapper {
inner: Arc<Mutex<Inner>>,
}
// This implementation does nothing, we just use it as a stable type to downcast the
// implementation in the destructor of wl_output, in order to retrieve the Arc to the
// inner and remove this output from the list
impl Implementation<Resource<WlOutput>, Request> for InnerWrapper {
fn receive(&mut self, req: Request, _res: Resource<WlOutput>) {
// this will break if new variants are added :)
let Request::Release = req;
}
}
/// An output as seen by the clients
///
/// This handle is stored in the events loop, and allows you to notify clients
/// about any change in the properties of this output.
pub struct Output {
inner: Arc<Mutex<Inner>>,
}
impl Output {
/// Create a new output global with given name and physical properties
///
/// The global is directly registered into the eventloop, and this function
/// returns the state token allowing you to access it, as well as the global handle,
/// in case you whish to remove this global in the future.
pub fn new<L>(
display: &mut Display,
token: LoopToken,
name: String,
physical: PhysicalProperties,
logger: L,
) -> (Output, Global<WlOutput>)
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "output_handler"));
info!(log, "Creating new wl_output"; "name" => &name);
let inner = Arc::new(Mutex::new(Inner {
name: name,
log: log,
instances: Vec::new(),
physical: physical,
location: (0, 0),
transform: Transform::Normal,
scale: 1,
modes: Vec::new(),
current_mode: None,
preferred_mode: None,
}));
let output = Output {
inner: inner.clone(),
};
let global = display.create_global(&token, 3, move |_version, new_output: NewResource<_>| {
let output = new_output.implement(
InnerWrapper {
inner: inner.clone(),
},
Some(|output, boxed_impl| {
let wrapper: Box<InnerWrapper> =
downcast_impl(boxed_impl).unwrap_or_else(|_| unreachable!());
wrapper
.inner
.lock()
.unwrap()
.instances
.retain(|o| !o.equals(&output));
}),
);
inner.lock().unwrap().new_global(output);
});
(output, global)
}
/// Sets the preferred mode of this output
///
/// If the provided mode was not previously known to this output, it is added to its
/// internal list.
pub fn set_preferred(&mut self, mode: Mode) {
self.preferred_mode = Some(mode);
if self.modes.iter().find(|&m| *m == mode).is_none() {
self.modes.push(mode);
pub fn set_preferred(&self, mode: Mode) {
let mut inner = self.inner.lock().unwrap();
inner.preferred_mode = Some(mode);
if inner.modes.iter().find(|&m| *m == mode).is_none() {
inner.modes.push(mode);
}
}
/// Adds a mode to the list of known modes to this output
pub fn add_mode(&mut self, mode: Mode) {
if self.modes.iter().find(|&m| *m == mode).is_none() {
self.modes.push(mode);
pub fn add_mode(&self, mode: Mode) {
let mut inner = self.inner.lock().unwrap();
if inner.modes.iter().find(|&m| *m == mode).is_none() {
inner.modes.push(mode);
}
}
@ -210,13 +260,14 @@ impl Output {
///
/// It will not de-advertize it from existing clients (the protocol does not
/// allow it), but it won't be advertized to now clients from now on.
pub fn delete_mode(&mut self, mode: Mode) {
self.modes.retain(|&m| m != mode);
if self.current_mode == Some(mode) {
self.current_mode = None;
pub fn delete_mode(&self, mode: Mode) {
let mut inner = self.inner.lock().unwrap();
inner.modes.retain(|&m| m != mode);
if inner.current_mode == Some(mode) {
inner.current_mode = None;
}
if self.preferred_mode == Some(mode) {
self.preferred_mode = None;
if inner.preferred_mode == Some(mode) {
inner.preferred_mode = None;
}
}
@ -230,75 +281,58 @@ impl Output {
///
/// By default, transform status is `Normal`, and scale is `1`.
pub fn change_current_state(
&mut self, new_mode: Option<Mode>, new_transform: Option<wl_output::Transform>,
&self,
new_mode: Option<Mode>,
new_transform: Option<Transform>,
new_scale: Option<i32>,
) {
let mut inner = self.inner.lock().unwrap();
if let Some(mode) = new_mode {
if self.modes.iter().find(|&m| *m == mode).is_none() {
self.modes.push(mode);
if inner.modes.iter().find(|&m| *m == mode).is_none() {
inner.modes.push(mode);
}
self.current_mode = new_mode;
inner.current_mode = new_mode;
}
if let Some(transform) = new_transform {
self.transform = transform;
inner.transform = transform;
}
if let Some(scale) = new_scale {
self.scale = scale;
inner.scale = scale;
}
let mut flags = wl_output::Mode::Current;
if self.preferred_mode == new_mode {
flags |= wl_output::Mode::Preferred;
let mut flags = WMode::Current;
if inner.preferred_mode == new_mode {
flags |= WMode::Preferred;
}
for output in &self.instances {
for output in &inner.instances {
if let Some(mode) = new_mode {
output.mode(flags, mode.width, mode.height, mode.refresh);
output.send(Event::Mode {
flags: flags,
width: mode.width,
height: mode.height,
refresh: mode.refresh,
});
}
if new_transform.is_some() {
self.send_geometry(output);
inner.send_geometry(output);
}
if let Some(scale) = new_scale {
if output.version() >= 2 {
output.scale(scale);
output.send(Event::Scale { factor: scale });
}
}
if output.version() >= 2 {
output.done();
output.send(Event::Done);
}
}
}
/// Chech is given wl_output instance is managed by this `Output`.
pub fn owns(&self, output: &wl_output::WlOutput) -> bool {
self.instances.iter().any(|o| o.equals(output))
}
/// Cleanup internal `wl_output` instances list
///
/// Clients do not necessarily notify the server on the destruction
/// of their `wl_output` instances. This can lead to accumulation of
/// stale values in the internal instances list. This methods delete
/// them.
///
/// It can be good to call this regularly (but not necessarily very often).
pub fn cleanup(&mut self) {
self.instances.retain(|o| o.status() == Liveness::Alive);
}
}
fn output_bind(
evlh: &mut EventLoopHandle, token: &mut StateToken<Output>, _: &Client, global: wl_output::WlOutput
) {
evlh.register(&global, output_implementation(), token.clone(), None);
evlh.state().get_mut(token).new_global(global);
}
fn output_implementation() -> wl_output::Implementation<StateToken<Output>> {
wl_output::Implementation {
release: |evlh, token, _, output| {
evlh.state()
.get_mut(token)
.instances
.retain(|o| !o.equals(output));
},
pub fn owns(&self, output: &Resource<WlOutput>) -> bool {
self.inner
.lock()
.unwrap()
.instances
.iter()
.any(|o| o.equals(output))
}
}

View File

@ -3,8 +3,9 @@ use std::io::{Error as IoError, Write};
use std::os::unix::io::AsRawFd;
use std::sync::{Arc, Mutex};
use tempfile::tempfile;
use wayland_server::{Liveness, Resource};
use wayland_server::protocol::{wl_keyboard, wl_surface};
use wayland_server::{NewResource, Resource};
use wayland_server::protocol::wl_surface::WlSurface;
use wayland_server::protocol::wl_keyboard::{Event, KeyState as WlKeyState, KeymapFormat, Request, WlKeyboard};
use xkbcommon::xkb;
pub use xkbcommon::xkb::{keysyms, Keysym};
@ -55,8 +56,8 @@ impl ModifiersState {
}
struct KbdInternal {
known_kbds: Vec<wl_keyboard::WlKeyboard>,
focus: Option<wl_surface::WlSurface>,
known_kbds: Vec<Resource<WlKeyboard>>,
focus: Option<Resource<WlSurface>>,
pressed_keys: Vec<u32>,
mods_state: ModifiersState,
keymap: xkb::Keymap,
@ -65,9 +66,18 @@ struct KbdInternal {
repeat_delay: i32,
}
// This is OK because all parts of `xkb` will remain on the
// same thread
unsafe impl Send for KbdInternal {}
impl KbdInternal {
fn new(
rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>, repeat_rate: i32,
rules: &str,
model: &str,
layout: &str,
variant: &str,
options: Option<String>,
repeat_rate: i32,
repeat_delay: i32,
) -> Result<KbdInternal, ()> {
// we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe
@ -147,7 +157,7 @@ impl KbdInternal {
fn with_focused_kbds<F>(&self, mut f: F)
where
F: FnMut(&wl_keyboard::WlKeyboard, &wl_surface::WlSurface),
F: FnMut(&Resource<WlKeyboard>, &Resource<WlSurface>),
{
if let Some(ref surface) = self.focus {
for kbd in &self.known_kbds {
@ -170,8 +180,14 @@ pub enum Error {
/// Create a keyboard handler from a set of RMLVO rules
pub(crate) fn create_keyboard_handler(
rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>, repeat_delay: i32,
repeat_rate: i32, logger: &::slog::Logger,
rules: &str,
model: &str,
layout: &str,
variant: &str,
options: Option<String>,
repeat_delay: i32,
repeat_rate: i32,
logger: &::slog::Logger,
) -> Result<KeyboardHandle, Error> {
let log = logger.new(o!("smithay_module" => "xkbcommon_handler"));
info!(log, "Initializing a xkbcommon handler with keymap query";
@ -252,7 +268,7 @@ impl KeyboardHandle {
///
/// The module `smithay::keyboard::keysyms` exposes definitions of all possible keysyms
/// to be compared against. This includes non-characted keysyms, such as XF86 special keys.
pub fn input<F>(&self, keycode: u32, state: KeyState, serial: u32, filter: F)
pub fn input<F>(&self, keycode: u32, state: KeyState, serial: u32, time: u32, filter: F)
where
F: FnOnce(&ModifiersState, Keysym) -> bool,
{
@ -282,14 +298,25 @@ impl KeyboardHandle {
None
};
let wl_state = match state {
KeyState::Pressed => wl_keyboard::KeyState::Pressed,
KeyState::Released => wl_keyboard::KeyState::Released,
KeyState::Pressed => WlKeyState::Pressed,
KeyState::Released => WlKeyState::Released,
};
guard.with_focused_kbds(|kbd, _| {
if let Some((dep, la, lo, gr)) = modifiers {
kbd.modifiers(serial, dep, la, lo, gr);
kbd.send(Event::Modifiers {
serial,
mods_depressed: dep,
mods_latched: la,
mods_locked: lo,
group: gr,
});
}
kbd.key(serial, 0, keycode, wl_state);
kbd.send(Event::Key {
serial,
time,
key: keycode,
state: wl_state,
});
});
if guard.focus.is_some() {
trace!(self.arc.logger, "Input forwarded to client");
@ -303,7 +330,7 @@ impl KeyboardHandle {
/// If the ne focus is different from the previous one, any previous focus
/// will be sent a `wl_keyboard::leave` event, and if the new focus is not `None`,
/// a `wl_keyboard::enter` event will be sent.
pub fn set_focus(&self, focus: Option<&wl_surface::WlSurface>, serial: u32) {
pub fn set_focus(&self, focus: Option<&Resource<WlSurface>>, serial: u32) {
let mut guard = self.arc.internal.lock().unwrap();
let same = guard
@ -315,16 +342,29 @@ impl KeyboardHandle {
if !same {
// unset old focus
guard.with_focused_kbds(|kbd, s| {
kbd.leave(serial, s);
kbd.send(Event::Leave {
serial,
surface: s.clone(),
});
});
// set new focus
guard.focus = focus.and_then(|s| s.clone());
guard.focus = focus.map(|s| s.clone());
let (dep, la, lo, gr) = guard.serialize_modifiers();
let keys = guard.serialize_pressed_keys();
guard.with_focused_kbds(|kbd, s| {
kbd.modifiers(serial, dep, la, lo, gr);
kbd.enter(serial, s, keys.clone());
guard.with_focused_kbds(|kbd, surface| {
kbd.send(Event::Modifiers {
serial,
mods_depressed: dep,
mods_latched: la,
mods_locked: lo,
group: gr,
});
kbd.send(Event::Enter {
serial,
surface: surface.clone(),
keys: keys.clone(),
});
});
if guard.focus.is_some() {
trace!(self.arc.logger, "Focus set to new surface");
@ -341,16 +381,19 @@ impl KeyboardHandle {
/// The keymap will automatically be sent to it
///
/// This should be done first, before anything else is done with this keyboard.
pub(crate) fn new_kbd(&self, kbd: wl_keyboard::WlKeyboard) {
pub(crate) fn new_kbd(&self, kbd: Resource<WlKeyboard>) {
trace!(self.arc.logger, "Sending keymap to client");
kbd.keymap(
wl_keyboard::KeymapFormat::XkbV1,
self.arc.keymap_file.as_raw_fd(),
self.arc.keymap_len,
);
kbd.send(Event::Keymap {
format: KeymapFormat::XkbV1,
fd: self.arc.keymap_file.as_raw_fd(),
size: self.arc.keymap_len,
});
let mut guard = self.arc.internal.lock().unwrap();
if kbd.version() >= 4 {
kbd.repeat_info(guard.repeat_rate, guard.repeat_delay);
kbd.send(Event::RepeatInfo {
rate: guard.repeat_rate,
delay: guard.repeat_delay,
});
}
guard.known_kbds.push(kbd);
}
@ -361,17 +404,36 @@ impl KeyboardHandle {
guard.repeat_delay = delay;
guard.repeat_rate = rate;
for kbd in &guard.known_kbds {
kbd.repeat_info(rate, delay);
kbd.send(Event::RepeatInfo { rate, delay });
}
}
/// Performs an internal cleanup of known kbds
///
/// Drops any wl_keyboard that is no longer alive
pub(crate) fn cleanup_old_kbds(&self) {
let mut guard = self.arc.internal.lock().unwrap();
guard
.known_kbds
.retain(|kbd| kbd.status() != Liveness::Dead);
}
}
pub(crate) fn implement_keyboard(
new_keyboard: NewResource<WlKeyboard>,
handle: Option<&KeyboardHandle>,
) -> Resource<WlKeyboard> {
let destructor = match handle {
Some(h) => {
let arc = h.arc.clone();
Some(move |keyboard: Resource<_>, _| {
arc.internal
.lock()
.unwrap()
.known_kbds
.retain(|k| !k.equals(&keyboard))
})
}
None => None,
};
new_keyboard.implement(
|request, _keyboard| {
match request {
Request::Release => {
// Our destructors already handle it
}
}
},
destructor,
)
}

View File

@ -10,14 +10,14 @@
//! ```
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//!
//! use smithay::wayland::seat::Seat;
//!
//! # fn main(){
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! # let (mut display, event_loop) = wayland_server::Display::new();
//! // insert the seat:
//! let (seat_state_token, seat_global) = Seat::new(
//! &mut event_loop,
//! let (seat, seat_global) = Seat::new(
//! &mut display, // the display
//! event_loop.token(), // a LoopToken
//! "seat-0".into(), // the name of the seat, will be advertize to clients
//! None /* insert a logger here*/
//! );
@ -31,8 +31,7 @@
//! Currently, only pointer and keyboard capabilities are supported by
//! smithay.
//!
//! You can add these capabilities via methods of the `Seat` struct that was
//! inserted in the event loop, that you can retreive via its token:
//! You can add these capabilities via methods of the `Seat` struct:
//!
//! ```
//! # extern crate wayland_server;
@ -41,28 +40,59 @@
//! # use smithay::wayland::seat::Seat;
//! #
//! # fn main(){
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! # let (seat_state_token, seat_global) = Seat::new(
//! # &mut event_loop,
//! # let (mut display, event_loop) = wayland_server::Display::new();
//! # let (mut seat, seat_global) = Seat::new(
//! # &mut display,
//! # event_loop.token(),
//! # "seat-0".into(), // the name of the seat, will be advertize to clients
//! # None /* insert a logger here*/
//! # );
//! let pointer_handle = event_loop.state().get_mut(&seat_state_token).add_pointer();
//! let pointer_handle = seat.add_pointer();
//! # }
//! ```
//!
//! These handles can be cloned and sent accross thread, so you can keep one around
//! in your event-handling code to forward inputs to your clients.
use std::sync::{Arc, Mutex};
mod keyboard;
mod pointer;
pub use self::keyboard::{Error as KeyboardError, KeyboardHandle, ModifiersState};
pub use self::pointer::{PointerAxisHandle, PointerHandle};
use wayland_server::{Client, EventLoopHandle, Global, Liveness, Resource, StateToken};
use wayland_server::protocol::{wl_keyboard, wl_pointer, wl_seat};
use wayland_server::{Display, Global, LoopToken, NewResource, Resource};
use wayland_server::protocol::wl_seat;
/// Internal data of a seat global
struct Inner {
log: ::slog::Logger,
name: String,
pointer: Option<PointerHandle>,
keyboard: Option<KeyboardHandle>,
known_seats: Vec<Resource<wl_seat::WlSeat>>,
}
impl Inner {
fn compute_caps(&self) -> wl_seat::Capability {
let mut caps = wl_seat::Capability::empty();
if self.pointer.is_some() {
caps |= wl_seat::Capability::Pointer;
}
if self.keyboard.is_some() {
caps |= wl_seat::Capability::Keyboard;
}
caps
}
fn send_all_caps(&self) {
let capabilities = self.compute_caps();
for seat in &self.known_seats {
seat.send(wl_seat::Event::Capabilities { capabilities });
}
}
}
/// A Seat handle
///
/// This struct gives you access to the control of the
/// capabilities of the associated seat.
@ -71,11 +101,7 @@ use wayland_server::protocol::{wl_keyboard, wl_pointer, wl_seat};
///
/// See module-level documentation for details of use.
pub struct Seat {
log: ::slog::Logger,
name: String,
pointer: Option<PointerHandle>,
keyboard: Option<KeyboardHandle>,
known_seats: Vec<wl_seat::WlSeat>,
inner: Arc<Mutex<Inner>>,
}
impl Seat {
@ -88,22 +114,39 @@ impl Seat {
/// you to add or remove capabilities from it), and the global handle,
/// in case you want to remove it.
pub fn new<L>(
evlh: &mut EventLoopHandle, name: String, logger: L
) -> (StateToken<Seat>, Global<wl_seat::WlSeat, StateToken<Seat>>)
display: &mut Display,
token: LoopToken,
name: String,
logger: L,
) -> (Seat, Global<wl_seat::WlSeat>)
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger);
let seat = Seat {
let inner = Arc::new(Mutex::new(Inner {
log: log.new(o!("smithay_module" => "seat_handler", "seat_name" => name.clone())),
name: name,
pointer: None,
keyboard: None,
known_seats: Vec::new(),
}));
let seat = Seat {
inner: inner.clone(),
};
let token = evlh.state().insert(seat);
let global = evlh.register_global(5, seat_global_bind, token.clone());
(token, global)
let global = display.create_global(&token, 5, move |_version, new_seat| {
let seat = implement_seat(new_seat, inner.clone());
let mut inner = inner.lock().unwrap();
if seat.version() >= 2 {
seat.send(wl_seat::Event::Name {
name: inner.name.clone(),
});
}
seat.send(wl_seat::Event::Capabilities {
capabilities: inner.compute_caps(),
});
inner.known_seats.push(seat);
});
(seat, global)
}
/// Adds the pointer capability to this seat
@ -115,21 +158,16 @@ impl Seat {
/// will overwrite it, and will be seen by the clients as if the
/// mouse was unplugged and a new one was plugged.
pub fn add_pointer(&mut self) -> PointerHandle {
let mut inner = self.inner.lock().unwrap();
let pointer = self::pointer::create_pointer_handler();
if self.pointer.is_some() {
if inner.pointer.is_some() {
// there is already a pointer, remove it and notify the clients
// of the change
self.pointer = None;
let caps = self.compute_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
}
}
self.pointer = Some(pointer.clone());
let caps = self.compute_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
inner.pointer = None;
inner.send_all_caps();
}
inner.pointer = Some(pointer.clone());
inner.send_all_caps();
pointer
}
@ -137,12 +175,10 @@ impl Seat {
///
/// Clients will be appropriately notified.
pub fn remove_pointer(&mut self) {
if self.pointer.is_some() {
self.pointer = None;
let caps = self.compute_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
}
let mut inner = self.inner.lock().unwrap();
if inner.pointer.is_some() {
inner.pointer = None;
inner.send_all_caps();
}
}
@ -159,9 +195,15 @@ impl Seat {
/// will overwrite it, and will be seen by the clients as if the
/// keyboard was unplugged and a new one was plugged.
pub fn add_keyboard(
&mut self, model: &str, layout: &str, variant: &str, options: Option<String>, repeat_delay: i32,
&mut self,
model: &str,
layout: &str,
variant: &str,
options: Option<String>,
repeat_delay: i32,
repeat_rate: i32,
) -> Result<KeyboardHandle, KeyboardError> {
let mut inner = self.inner.lock().unwrap();
let keyboard = self::keyboard::create_keyboard_handler(
"evdev", // we need this one
model,
@ -170,22 +212,16 @@ impl Seat {
options,
repeat_delay,
repeat_rate,
&self.log,
&inner.log,
)?;
if self.keyboard.is_some() {
if inner.keyboard.is_some() {
// there is already a keyboard, remove it and notify the clients
// of the change
self.keyboard = None;
let caps = self.compute_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
}
}
self.keyboard = Some(keyboard.clone());
let caps = self.compute_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
inner.keyboard = None;
inner.send_all_caps();
}
inner.keyboard = Some(keyboard.clone());
inner.send_all_caps();
Ok(keyboard)
}
@ -193,95 +229,60 @@ impl Seat {
///
/// Clients will be appropriately notified.
pub fn remove_keyboard(&mut self) {
if self.keyboard.is_some() {
self.keyboard = None;
let caps = self.compute_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
}
let mut inner = self.inner.lock().unwrap();
if inner.keyboard.is_some() {
inner.keyboard = None;
inner.send_all_caps();
}
}
/// Checks wether a given `WlSeat` is associated with this `Seat`
pub fn owns(&self, seat: &wl_seat::WlSeat) -> bool {
self.known_seats.iter().any(|s| s.equals(seat))
}
/// Cleanup internal states from old resources
///
/// Deletes all remnnant of ressources from clients that
/// are now disconnected.
///
/// It can be wise to run this from time to time.
pub fn cleanup(&mut self) {
if let Some(ref pointer) = self.pointer {
pointer.cleanup_old_pointers();
}
if let Some(ref kbd) = self.keyboard {
kbd.cleanup_old_kbds();
}
self.known_seats.retain(|s| s.status() == Liveness::Alive);
}
fn compute_caps(&self) -> wl_seat::Capability {
let mut caps = wl_seat::Capability::empty();
if self.pointer.is_some() {
caps |= wl_seat::Capability::Pointer;
}
if self.keyboard.is_some() {
caps |= wl_seat::Capability::Keyboard;
}
caps
pub fn owns(&self, seat: &Resource<wl_seat::WlSeat>) -> bool {
let inner = self.inner.lock().unwrap();
inner.known_seats.iter().any(|s| s.equals(seat))
}
}
fn seat_global_bind(
evlh: &mut EventLoopHandle, token: &mut StateToken<Seat>, _: &Client, seat: wl_seat::WlSeat
) {
evlh.register(&seat, seat_implementation(), token.clone(), None);
let seat_mgr = evlh.state().get_mut(token);
if seat.version() >= 2 {
seat.name(seat_mgr.name.clone());
}
seat.capabilities(seat_mgr.compute_caps());
seat_mgr.known_seats.push(seat);
}
fn seat_implementation() -> wl_seat::Implementation<StateToken<Seat>> {
wl_seat::Implementation {
get_pointer: |evlh, token, _, _, pointer| {
evlh.register(&pointer, pointer_implementation(), (), None);
if let Some(ref ptr_handle) = evlh.state().get(token).pointer {
ptr_handle.new_pointer(pointer);
} else {
// we should send a protocol error... but the protocol does not allow
// us, so this pointer will just remain inactive ¯\_(ツ)_/¯
fn implement_seat(
new_seat: NewResource<wl_seat::WlSeat>,
inner: Arc<Mutex<Inner>>,
) -> Resource<wl_seat::WlSeat> {
let dest_inner = inner.clone();
new_seat.implement(
move |request, _seat| {
let inner = inner.lock().unwrap();
match request {
wl_seat::Request::GetPointer { id } => {
let pointer = self::pointer::implement_pointer(id, inner.pointer.as_ref());
if let Some(ref ptr_handle) = inner.pointer {
ptr_handle.new_pointer(pointer);
} else {
// we should send a protocol error... but the protocol does not allow
// us, so this pointer will just remain inactive ¯\_(ツ)_/¯
}
}
wl_seat::Request::GetKeyboard { id } => {
let keyboard = self::keyboard::implement_keyboard(id, inner.keyboard.as_ref());
if let Some(ref kbd_handle) = inner.keyboard {
kbd_handle.new_kbd(keyboard);
} else {
// same as pointer, should error but cannot
}
}
wl_seat::Request::GetTouch { id: _ } => {
// TODO
}
wl_seat::Request::Release => {
// Our destructors already handle it
}
}
},
get_keyboard: |evlh, token, _, _, keyboard| {
evlh.register(&keyboard, keyboard_implementation(), (), None);
if let Some(ref kbd_handle) = evlh.state().get(token).keyboard {
kbd_handle.new_kbd(keyboard);
} else {
// same, should error but cant
}
},
get_touch: |_evlh, _token, _, _, _touch| {
// TODO
},
release: |_, _, _, _| {},
}
}
fn pointer_implementation() -> wl_pointer::Implementation<()> {
wl_pointer::Implementation {
set_cursor: |_, _, _, _, _, _, _, _| {},
release: |_, _, _, _| {},
}
}
fn keyboard_implementation() -> wl_keyboard::Implementation<()> {
wl_keyboard::Implementation {
release: |_, _, _, _| {},
}
Some(move |seat, _| {
dest_inner
.lock()
.unwrap()
.known_seats
.retain(|s| !s.equals(&seat));
}),
)
}

View File

@ -1,12 +1,13 @@
use std::sync::{Arc, Mutex, MutexGuard};
use wayland_server::{Liveness, Resource};
use wayland_server::protocol::{wl_pointer, wl_surface};
use wayland_server::{NewResource, Resource};
use wayland_server::protocol::wl_surface::WlSurface;
use wayland_server::protocol::wl_pointer::{Axis, AxisSource, ButtonState, Event, Request, WlPointer};
// TODO: handle pointer surface role
struct PointerInternal {
known_pointers: Vec<wl_pointer::WlPointer>,
focus: Option<wl_surface::WlSurface>,
known_pointers: Vec<Resource<WlPointer>>,
focus: Option<Resource<WlSurface>>,
}
impl PointerInternal {
@ -19,7 +20,7 @@ impl PointerInternal {
fn with_focused_pointers<F>(&self, mut f: F)
where
F: FnMut(&wl_pointer::WlPointer, &wl_surface::WlSurface),
F: FnMut(&Resource<WlPointer>, &Resource<WlSurface>),
{
if let Some(ref focus) = self.focus {
for ptr in &self.known_pointers {
@ -44,7 +45,7 @@ pub struct PointerHandle {
}
impl PointerHandle {
pub(crate) fn new_pointer(&self, pointer: wl_pointer::WlPointer) {
pub(crate) fn new_pointer(&self, pointer: Resource<WlPointer>) {
let mut guard = self.inner.lock().unwrap();
guard.known_pointers.push(pointer);
}
@ -59,7 +60,7 @@ impl PointerHandle {
///
/// This will internally take care of notifying the appropriate client objects
/// of enter/motion/leave events.
pub fn motion(&self, location: Option<(&wl_surface::WlSurface, f64, f64)>, serial: u32, time: u32) {
pub fn motion(&self, location: Option<(&Resource<WlSurface>, f64, f64)>, serial: u32, time: u32) {
let mut guard = self.inner.lock().unwrap();
// do we leave a surface ?
let mut leave = true;
@ -72,9 +73,12 @@ impl PointerHandle {
}
if leave {
guard.with_focused_pointers(|pointer, surface| {
pointer.leave(serial, surface);
pointer.send(Event::Leave {
serial,
surface: surface.clone(),
});
if pointer.version() >= 5 {
pointer.frame();
pointer.send(Event::Frame);
}
});
guard.focus = None;
@ -83,19 +87,28 @@ impl PointerHandle {
// do we enter one ?
if let Some((surface, x, y)) = location {
if guard.focus.is_none() {
guard.focus = surface.clone();
guard.focus = Some(surface.clone());
guard.with_focused_pointers(|pointer, surface| {
pointer.enter(serial, surface, x, y);
pointer.send(Event::Enter {
serial,
surface: surface.clone(),
surface_x: x,
surface_y: y,
});
if pointer.version() >= 5 {
pointer.frame();
pointer.send(Event::Frame);
}
})
} else {
// we were on top of a surface and remained on it
guard.with_focused_pointers(|pointer, _| {
pointer.motion(time, x, y);
pointer.send(Event::Motion {
time,
surface_x: x,
surface_y: y,
});
if pointer.version() >= 5 {
pointer.frame();
pointer.send(Event::Frame);
}
})
}
@ -106,12 +119,17 @@ impl PointerHandle {
///
/// This will internally send the appropriate button event to the client
/// objects matching with the currently focused surface.
pub fn button(&self, button: u32, state: wl_pointer::ButtonState, serial: u32, time: u32) {
pub fn button(&self, button: u32, state: ButtonState, serial: u32, time: u32) {
let guard = self.inner.lock().unwrap();
guard.with_focused_pointers(|pointer, _| {
pointer.button(serial, time, button, state);
pointer.send(Event::Button {
serial,
time,
button,
state,
});
if pointer.version() >= 5 {
pointer.frame();
pointer.send(Event::Frame);
}
})
}
@ -125,18 +143,12 @@ impl PointerHandle {
inner: self.inner.lock().unwrap(),
}
}
pub(crate) fn cleanup_old_pointers(&self) {
let mut guard = self.inner.lock().unwrap();
guard
.known_pointers
.retain(|p| p.status() != Liveness::Dead);
}
}
/// A frame of pointer axis events.
///
/// Can be used with the builder pattern, e.g.:
///
/// ```ignore
/// pointer.axis()
/// .source(AxisSource::Wheel)
@ -156,10 +168,12 @@ impl<'a> PointerAxisHandle<'a> {
///
/// Using the `AxisSource::Finger` requires a stop event to be send,
/// when the user lifts off the finger (not necessarily in the same frame).
pub fn source(&mut self, source: wl_pointer::AxisSource) -> &mut Self {
pub fn source(&mut self, source: AxisSource) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 {
pointer.axis_source(source);
pointer.send(Event::AxisSource {
axis_source: source,
});
}
});
self
@ -170,10 +184,13 @@ impl<'a> PointerAxisHandle<'a> {
/// This event is optional and gives the client additional information about
/// the nature of the axis event. E.g. a scroll wheel might issue separate steps,
/// while a touchpad may never issue this event as it has no steps.
pub fn discrete(&mut self, axis: wl_pointer::Axis, steps: i32) -> &mut Self {
pub fn discrete(&mut self, axis: Axis, steps: i32) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 {
pointer.axis_discrete(axis, steps);
pointer.send(Event::AxisDiscrete {
axis,
discrete: steps,
});
}
});
self
@ -181,9 +198,9 @@ impl<'a> PointerAxisHandle<'a> {
/// The actual scroll value. This event is the only required one, but can also
/// be send multiple times. The values off one frame will be accumulated by the client.
pub fn value(&mut self, axis: wl_pointer::Axis, value: f64, time: u32) -> &mut Self {
pub fn value(&mut self, axis: Axis, value: f64, time: u32) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| {
pointer.axis(time, axis, value);
pointer.send(Event::Axis { time, axis, value });
});
self
}
@ -192,10 +209,10 @@ impl<'a> PointerAxisHandle<'a> {
///
/// This event is required for sources of the `AxisSource::Finger` type
/// and otherwise optional.
pub fn stop(&mut self, axis: wl_pointer::Axis, time: u32) -> &mut Self {
pub fn stop(&mut self, axis: Axis, time: u32) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 {
pointer.axis_stop(time, axis);
pointer.send(Event::AxisStop { time, axis });
}
});
self
@ -209,7 +226,7 @@ impl<'a> PointerAxisHandle<'a> {
pub fn done(&mut self) {
self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 {
pointer.frame();
pointer.send(Event::Frame);
}
})
}
@ -220,3 +237,35 @@ pub(crate) fn create_pointer_handler() -> PointerHandle {
inner: Arc::new(Mutex::new(PointerInternal::new())),
}
}
pub(crate) fn implement_pointer(
new_pointer: NewResource<WlPointer>,
handle: Option<&PointerHandle>,
) -> Resource<WlPointer> {
let destructor = match handle {
Some(h) => {
let inner = h.inner.clone();
Some(move |pointer: Resource<_>, _| {
inner
.lock()
.unwrap()
.known_pointers
.retain(|p| !p.equals(&pointer))
})
}
None => None,
};
new_pointer.implement(
|request, _pointer| {
match request {
Request::SetCursor { .. } => {
// TODO
}
Request::Release => {
// Our destructors already handle it
}
}
},
destructor,
)
}

View File

@ -0,0 +1,355 @@
//! Utilities for handling shell surfaces with the `wl_shell` protocol
//!
//! This module provides automatic handling of shell surfaces objects, by being registered
//! as a global handler for `wl_shell`. This protocol is deprecated in favor of `xdg_shell`,
//! thus this module is provided as a compatibility layer with older clients. As a consequence,
//! you can as a compositor-writer decide to only support its functionality in a best-effort
//! maneer: as this global is part of the core protocol, you are still required to provide
//! some support for it.
//!
//! ## Why use this implementation
//!
//! This implementation can track for you the various shell surfaces defined by the
//! clients by handling the `wl_shell` protocol.
//!
//! It allows you to easily access a list of all shell surfaces defined by your clients
//! access their associated metadata and underlying `wl_surface`s.
//!
//! This handler only handles the protocol exchanges with the client to present you the
//! information in a coherent and relatively easy to use maneer. All the actual drawing
//! and positioning logic of windows is out of its scope.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize this handler, simple use the `wl_shell_init` function provided in this
//! module. You will need to provide it the `CompositorToken` you retrieved from an
//! instanciation of the compositor handler provided by smithay.
//!
//! ```no_run
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! # extern crate wayland_protocols;
//! #
//! use smithay::wayland::compositor::roles::*;
//! use smithay::wayland::compositor::CompositorToken;
//! use smithay::wayland::shell::legacy::{wl_shell_init, ShellSurfaceRole, ShellRequest};
//! use wayland_server::{EventLoop, LoopToken};
//! # use wayland_server::protocol::{wl_seat, wl_output};
//! # #[derive(Default)] struct MySurfaceData;
//!
//! // define the metadata you want associated with the shell surfaces
//! #[derive(Default)]
//! pub struct MyShellSurfaceData {
//! /* ... */
//! }
//!
//! // define the roles type. You need to integrate the XdgSurface role:
//! define_roles!(MyRoles =>
//! [ShellSurface, ShellSurfaceRole<MyShellSurfaceData>]
//! );
//!
//! # fn main() {
//! # let (mut display, event_loop) = wayland_server::Display::new();
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<(), MyRoles, _, _>(
//! # &mut display,
//! # event_loop.token(),
//! # |_, _| {},
//! # None
//! # );
//! let (shell_state, _) = wl_shell_init(
//! &mut display,
//! event_loop.token(),
//! // token from the compositor implementation
//! compositor_token,
//! // your implementation, can also be a strucy implementing the
//! // appropriate Implementation<(), ShellRequest<_, _, _>> trait
//! |event: ShellRequest<_, _, MyShellSurfaceData>, ()| { /* ... */ },
//! None // put a logger if you want
//! );
//!
//! // You're now ready to go!
//! # }
//! ```
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::Role;
use wayland_server::{Display, Global, LoopToken, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface};
mod wl_handlers;
/// Metadata associated with the `xdg_surface` role
pub struct ShellSurfaceRole<D: 'static> {
/// Title of the surface
pub title: String,
/// Class of the surface
pub class: String,
pending_ping: u32,
/// Some user data you may want to associate with the surface
pub user_data: D,
}
/// A handle to a shell surface
pub struct ShellSurface<U, R, D> {
wl_surface: Resource<wl_surface::WlSurface>,
shell_surface: Resource<wl_shell_surface::WlShellSurface>,
token: CompositorToken<U, R>,
_d: ::std::marker::PhantomData<D>,
}
impl<U, R, D> ShellSurface<U, R, D>
where
U: 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
D: 'static,
{
/// Is the shell surface refered by this handle still alive?
pub fn alive(&self) -> bool {
self.shell_surface.is_alive() && self.wl_surface.is_alive()
}
/// Do this handle and the other one actually refer to the same shell surface?
pub fn equals(&self, other: &Self) -> bool {
self.shell_surface.equals(&other.shell_surface)
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&Resource<wl_surface::WlSurface>> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Send a ping request to this shell surface
///
/// You'll receive the reply as a `ShellRequest::Pong` request
///
/// A typical use is to start a timer at the same time you send this ping
/// request, and cancel it when you receive the pong. If the timer runs
/// down to 0 before a pong is received, mark the client as unresponsive.
///
/// Fails if this shell client already has a pending ping or is already dead.
pub fn send_ping(&self, serial: u32) -> Result<(), ()> {
if !self.alive() {
return Err(());
}
let ret = self.token.with_role_data(&self.wl_surface, |data| {
if data.pending_ping == 0 {
data.pending_ping = serial;
true
} else {
false
}
});
if let Ok(true) = ret {
self.shell_surface
.send(wl_shell_surface::Event::Ping { serial });
Ok(())
} else {
Err(())
}
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
pub fn send_configure(&self, size: (u32, u32), edges: wl_shell_surface::Resize) {
self.shell_surface.send(wl_shell_surface::Event::Configure {
edges,
width: size.0 as i32,
height: size.1 as i32,
})
}
/// Signal a popup surface that it has lost focus
pub fn send_popup_done(&self) {
self.shell_surface.send(wl_shell_surface::Event::PopupDone)
}
/// Access the user data you associated to this surface
pub fn with_user_data<F, T>(&self, f: F) -> Result<T, ()>
where
F: FnOnce(&mut D) -> T,
{
self.token
.with_role_data(&self.wl_surface, |data| f(&mut data.user_data))
.map_err(|_| ())
}
}
/// Possible kinds of shell surface of the `wl_shell` protocol
pub enum ShellSurfaceKind {
/// Toplevel, a regular window displayed somewhere in the compositor space
Toplevel,
/// Transient, this surface has a parent surface
///
/// These are sub-windows of an application (for example a configuration window),
/// and as such should only be visible in their parent window is, and on top of it.
Transient {
/// The surface considered as parent
parent: Resource<wl_surface::WlSurface>,
/// Location relative to the parent
location: (i32, i32),
/// Wether this window should be marked as inactive
inactive: bool,
},
/// Fullscreen surface, covering an entire output
Fullscreen {
/// Method used for fullscreen
method: wl_shell_surface::FullscreenMethod,
/// Framerate (relevant only for driver fullscreen)
framerate: u32,
/// Requested output if any
output: Option<Resource<wl_output::WlOutput>>,
},
/// A popup surface
///
/// Short-lived surface, typically refrered as "tooltips" in many
/// contexts.
Popup {
/// The parent surface of this popup
parent: Resource<wl_surface::WlSurface>,
/// The serial of the input event triggering the creation of this
/// popup
serial: u32,
/// Wether this popup should be marked as inactive
inactive: bool,
/// Location of the popup relative to its parent
location: (i32, i32),
/// Seat associated this the input that triggered the creation of the
/// popup. Used to define when the "popup done" event is sent.
seat: Resource<wl_seat::WlSeat>,
},
/// A maximized surface
///
/// Like a toplevel surface, but as big as possible on a single output
/// while keeping any relevant desktop-environment interface visible.
Maximized {
/// Requested output for maximization
output: Option<Resource<wl_output::WlOutput>>,
},
}
/// A request triggered by a wl_shell_surface
pub enum ShellRequest<U, R, D> {
/// A new shell surface was created
///
/// by default it has no kind and this should not be displayed
NewShellSurface {
/// The created surface
surface: ShellSurface<U, R, D>,
},
/// A pong event
///
/// The surface responded to its pending ping. If you receive this
/// event, smithay has already checked that the responded serial was valid.
Pong {
/// The surface that sent the pong
surface: ShellSurface<U, R, D>,
},
/// Start of an interactive move
///
/// The surface requests that an interactive move is started on it
Move {
/// The surface requesting the move
surface: ShellSurface<U, R, D>,
/// Serial of the implicit grab that initiated the move
serial: u32,
/// Seat associated with the move
seat: Resource<wl_seat::WlSeat>,
},
/// Start of an interactive resize
///
/// The surface requests that an interactive resize is started on it
Resize {
/// The surface requesting the resize
surface: ShellSurface<U, R, D>,
/// Serial of the implicit grab that initiated the resize
serial: u32,
/// Seat associated with the resize
seat: Resource<wl_seat::WlSeat>,
/// Direction of the resize
edges: wl_shell_surface::Resize,
},
/// The surface changed its kind
SetKind {
/// The surface
surface: ShellSurface<U, R, D>,
/// Its new kind
kind: ShellSurfaceKind,
},
}
/// Shell global state
///
/// This state allows you to retrieve a list of surfaces
/// currently known to the shell global.
pub struct ShellState<U, R, D> {
known_surfaces: Vec<ShellSurface<U, R, D>>,
}
impl<U, R, D> ShellState<U, R, D>
where
U: 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
D: 'static,
{
/// Cleans the internal surface storage by removing all dead surfaces
pub(crate) fn cleanup_surfaces(&mut self) {
self.known_surfaces.retain(|s| s.alive());
}
/// Access all the shell surfaces known by this handler
pub fn surfaces(&self) -> &[ShellSurface<U, R, D>] {
&self.known_surfaces[..]
}
}
/// Create a new `wl_shell` global
pub fn wl_shell_init<U, R, D, L, Impl>(
display: &mut Display,
ltoken: LoopToken,
ctoken: CompositorToken<U, R>,
implementation: Impl,
logger: L,
) -> (Arc<Mutex<ShellState<U, R, D>>>, Global<wl_shell::WlShell>)
where
U: 'static,
D: Default + 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
L: Into<Option<::slog::Logger>>,
Impl: Implementation<(), ShellRequest<U, R, D>>,
{
let _log = ::slog_or_stdlog(logger);
let implementation = Rc::new(RefCell::new(implementation));
let ltoken2 = ltoken.clone();
let state = Arc::new(Mutex::new(ShellState {
known_surfaces: Vec::new(),
}));
let state2 = state.clone();
let global = display.create_global(&ltoken2, 1, move |_version, shell| {
self::wl_handlers::implement_shell(
shell,
ltoken.clone(),
ctoken.clone(),
implementation.clone(),
state2.clone(),
);
});
(state, global)
}

View File

@ -0,0 +1,233 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use wayland_server::{LoopToken, NewResource, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_shell, wl_shell_surface, wl_surface};
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::Role;
use super::{ShellRequest, ShellState, ShellSurface, ShellSurfaceKind, ShellSurfaceRole};
pub(crate) fn implement_shell<U, R, D, Impl>(
shell: NewResource<wl_shell::WlShell>,
ltoken: LoopToken,
ctoken: CompositorToken<U, R>,
implementation: Rc<RefCell<Impl>>,
state: Arc<Mutex<ShellState<U, R, D>>>,
) where
U: 'static,
D: Default + 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
Impl: Implementation<(), ShellRequest<U, R, D>> + 'static,
{
let ltoken2 = ltoken.clone();
shell.implement_nonsend(
move |req, shell: Resource<_>| {
let wl_shell::Request::GetShellSurface { id, surface } = req;
let role_data = ShellSurfaceRole {
title: "".into(),
class: "".into(),
pending_ping: 0,
user_data: Default::default(),
};
if ctoken.give_role_with(&surface, role_data).is_err() {
shell.post_error(
wl_shell::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
let shell_surface = implement_shell_surface(
id,
surface,
implementation.clone(),
ltoken.clone(),
ctoken.clone(),
state.clone(),
);
state
.lock()
.unwrap()
.known_surfaces
.push(make_handle(&shell_surface, ctoken.clone()));
implementation.borrow_mut().receive(
ShellRequest::NewShellSurface {
surface: make_handle(&shell_surface, ctoken.clone()),
},
(),
);
},
None::<fn(_, _)>,
&ltoken2,
);
}
fn make_handle<U, R, D>(
shell_surface: &Resource<wl_shell_surface::WlShellSurface>,
token: CompositorToken<U, R>,
) -> ShellSurface<U, R, D> {
let data = unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData<U, R, D>) };
ShellSurface {
wl_surface: data.surface.clone(),
shell_surface: shell_surface.clone(),
token,
_d: ::std::marker::PhantomData,
}
}
pub(crate) struct ShellSurfaceUserData<U, R, D> {
surface: Resource<wl_surface::WlSurface>,
state: Arc<Mutex<ShellState<U, R, D>>>,
}
fn implement_shell_surface<U, R, Impl, D>(
shell_surface: NewResource<wl_shell_surface::WlShellSurface>,
surface: Resource<wl_surface::WlSurface>,
implementation: Rc<RefCell<Impl>>,
ltoken: LoopToken,
ctoken: CompositorToken<U, R>,
state: Arc<Mutex<ShellState<U, R, D>>>,
) -> Resource<wl_shell_surface::WlShellSurface>
where
U: 'static,
D: 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
Impl: Implementation<(), ShellRequest<U, R, D>> + 'static,
{
use self::wl_shell_surface::Request;
let shell_surface = shell_surface.implement_nonsend(
move |req, shell_surface: Resource<_>| {
let data = unsafe { &mut *(shell_surface.get_user_data() as *mut ShellSurfaceUserData<U, R, D>) };
let mut user_impl = implementation.borrow_mut();
match req {
Request::Pong { serial } => {
let valid = ctoken
.with_role_data(&data.surface, |data| {
if data.pending_ping == serial {
data.pending_ping = 0;
true
} else {
false
}
})
.expect("wl_shell_surface exists but surface has not the right role?");
if valid {
user_impl.receive(
ShellRequest::Pong {
surface: make_handle(&shell_surface, ctoken.clone()),
},
(),
);
}
}
Request::Move { seat, serial } => user_impl.receive(
ShellRequest::Move {
surface: make_handle(&shell_surface, ctoken.clone()),
serial,
seat,
},
(),
),
Request::Resize {
seat,
serial,
edges,
} => user_impl.receive(
ShellRequest::Resize {
surface: make_handle(&shell_surface, ctoken.clone()),
serial,
seat,
edges,
},
(),
),
Request::SetToplevel => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Toplevel,
},
(),
),
Request::SetTransient {
parent,
x,
y,
flags,
} => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Transient {
parent,
location: (x, y),
inactive: flags.contains(wl_shell_surface::Transient::Inactive),
},
},
(),
),
Request::SetFullscreen {
method,
framerate,
output,
} => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Fullscreen {
method,
framerate,
output,
},
},
(),
),
Request::SetPopup {
seat,
serial,
parent,
x,
y,
flags,
} => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Popup {
parent,
serial,
seat,
location: (x, y),
inactive: flags.contains(wl_shell_surface::Transient::Inactive),
},
},
(),
),
Request::SetMaximized { output } => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Maximized { output },
},
(),
),
Request::SetTitle { title } => {
ctoken
.with_role_data(&data.surface, |data| data.title = title)
.expect("wl_shell_surface exists but surface has not shell_surface role?!");
}
Request::SetClass { class_ } => {
ctoken
.with_role_data(&data.surface, |data| data.class = class_)
.expect("wl_shell_surface exists but surface has not shell_surface role?!");
}
}
},
Some(|shell_surface: Resource<_>, _| {
let data =
unsafe { Box::from_raw(shell_surface.get_user_data() as *mut ShellSurfaceUserData<U, R, D>) };
data.state.lock().unwrap().cleanup_surfaces();
}),
&ltoken,
);
shell_surface.set_user_data(Box::into_raw(Box::new(ShellSurfaceUserData { surface, state })) as *mut ());
shell_surface
}

View File

@ -1,940 +1,2 @@
//! Utilities for handling shell surfaces, toplevel and popups
//!
//! This module provides automatic handling of shell surfaces objects, by being registered
//! as a global handler for `wl_shell` and `xdg_shell`.
//!
//! ## Why use this implementation
//!
//! This implementation can track for you the various shell surfaces defined by the
//! clients by handling the `xdg_shell` protocol. It also includes a compatibility
//! layer for the deprecated `wl_shell` global.
//!
//! It allows you to easily access a list of all shell surfaces defined by your clients
//! access their associated metadata and underlying `wl_surface`s.
//!
//! This handler only handles the protocol exchanges with the client to present you the
//! information in a coherent and relatively easy to use maneer. All the actual drawing
//! and positioning logic of windows is out of its scope.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize this handler, simple use the `shell_init` function provided in this
//! module. You will need to provide it the `CompositorToken` you retrieved from an
//! instanciation of the `CompositorHandler` provided by smithay.
//!
//! ```no_run
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! # extern crate wayland_protocols;
//! #
//! use smithay::wayland::compositor::roles::*;
//! use smithay::wayland::compositor::CompositorToken;
//! use smithay::wayland::shell::{shell_init, ShellSurfaceRole, ShellSurfaceUserImplementation};
//! use wayland_server::protocol::wl_shell::WlShell;
//! use wayland_protocols::unstable::xdg_shell::v6::server::zxdg_shell_v6::ZxdgShellV6;
//! use wayland_server::{EventLoop, EventLoopHandle};
//! # use wayland_server::protocol::{wl_seat, wl_output};
//! # use wayland_protocols::unstable::xdg_shell::v6::server::zxdg_toplevel_v6;
//! # #[derive(Default)] struct MySurfaceData;
//!
//! // define the roles type. You need to integrate the ShellSurface role:
//! define_roles!(MyRoles =>
//! [ShellSurface, ShellSurfaceRole]
//! );
//!
//! // define the metadata you want associated with the shell clients
//! #[derive(Default)]
//! struct MyShellData {
//! /* ... */
//! }
//!
//! # fn main() {
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<(), MyRoles, _, _>(
//! # &mut event_loop,
//! # unimplemented!(),
//! # (),
//! # None
//! # );
//! // define your implementation for shell
//! let my_shell_implementation = ShellSurfaceUserImplementation {
//! new_client: |evlh, idata, client| { unimplemented!() },
//! client_pong: |evlh, idata, client| { unimplemented!() },
//! new_toplevel: |evlh, idata, toplevel| { unimplemented!() },
//! new_popup: |evlh, idata, popup| { unimplemented!() },
//! move_: |evlh, idata, toplevel, seat, serial| { unimplemented!() },
//! resize: |evlh, idata, toplevel, seat, serial, edges| { unimplemented!() },
//! grab: |evlh, idata, popup, seat, serial| { unimplemented!() },
//! change_display_state: |evlh, idata, toplevel, maximized, minimized, fullscreen, output| {
//! unimplemented!()
//! },
//! show_window_menu: |evlh, idata, toplevel, seat, serial, x, y| { unimplemented!() },
//! };
//!
//! // define your implementation data
//! let my_shell_implementation_data = ();
//!
//! let (shell_state_token, _, _) = shell_init::<_, _, _, _, MyShellData, _>(
//! &mut event_loop,
//! compositor_token, // token from the compositor implementation
//! my_shell_implementation, // instance of shell::ShellSurfaceUserImplementation
//! my_shell_implementation_data, // whatever data you need here
//! None // put a logger if you want
//! );
//!
//! // You're now ready to go!
//! # }
//! ```
//!
//! ### Access to shell surface and clients data
//!
//! There are mainly 3 kind of objects that you'll manipulate from this implementation:
//!
//! - `ShellClient`: This is a handle representing an isntanciation of a shell global
//! you can associate client-wise metadata to it (this is the `MyShellData` type in
//! the example above).
//! - `ToplevelSurface`: This is a handle representing a toplevel surface, you can
//! retrive a list of all currently alive toplevel surface from the `ShellState`.
//! - `PopupSurface`: This is a handle representing a popup/tooltip surface. Similarly,
//! you can get a list of all currently alive popup surface from the `ShellState`.
//!
//! You'll obtain these objects though two means: either via the callback methods of
//! the subhandler you provided, or via methods on the `ShellState` that you can
//! access from the `state()` of the event loop and the token returned by the init
//! function.
use std::cell::RefCell;
use std::rc::Rc;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::Role;
use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6,
zxdg_positioner_v6 as xdg_positioner,
zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6};
use wayland_server::{EventLoopHandle, EventResult, Global, Liveness, Resource, StateToken};
use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface};
mod wl_handlers;
mod xdg_handlers;
/// Metadata associated with the `shell_surface` role
pub struct ShellSurfaceRole {
/// Pending state as requested by the client
///
/// The data in this field are double-buffered, you should
/// apply them on a surface commit.
pub pending_state: ShellSurfacePendingState,
/// Geometry of the surface
///
/// Defines, in surface relative coordinates, what should
/// be considered as "the surface itself", regarding focus,
/// window alignment, etc...
///
/// By default, you should consider the full contents of the
/// buffers of this surface and its subsurfaces.
pub window_geometry: Option<Rectangle>,
/// List of non-acked configures pending
///
/// Whenever a configure is acked by the client, all configure
/// older than it are discarded as well. As such, this vec contains
/// the serials of all the configure send to this surface that are
/// newer than the last ack received.
pub pending_configures: Vec<u32>,
/// Has this surface acked at least one configure?
///
/// xdg_shell defines it as illegal to commit on a surface that has
/// not yet acked a configure.
pub configured: bool,
}
#[derive(Copy, Clone, Debug)]
/// The state of a positioner, as set by the client
pub struct PositionerState {
/// Size of the rectangle that needs to be positioned
pub rect_size: (i32, i32),
/// Anchor rectangle in the parent surface coordinates
/// relative to which the surface must be positioned
pub anchor_rect: Rectangle,
/// Edges defining the anchor point
pub anchor_edges: xdg_positioner::Anchor,
/// Gravity direction for positioning the child surface
/// relative to its anchor point
pub gravity: xdg_positioner::Gravity,
/// Adjustments to do if previous criterias constraint the
/// surface
pub constraint_adjustment: xdg_positioner::ConstraintAdjustment,
/// Offset placement relative to the anchor point
pub offset: (i32, i32),
}
/// Contents of the pending state of a shell surface, depending on its role
pub enum ShellSurfacePendingState {
/// This a regular, toplevel surface
///
/// This corresponds to either the `xdg_toplevel` role from the
/// `xdg_shell` protocol, or the result of `set_toplevel` using the
/// `wl_shell` protocol.
///
/// This is what you'll generaly interpret as "a window".
Toplevel(ToplevelState),
/// This is a popup surface
///
/// This corresponds to either the `xdg_popup` role from the
/// `xdg_shell` protocol, or the result of `set_popup` using the
/// `wl_shell` protocol.
///
/// This are mostly for small tooltips and similar short-lived
/// surfaces.
Popup(PopupState),
/// This surface was not yet assigned a kind
None,
}
/// State of a regular toplevel surface
pub struct ToplevelState {
/// Parent of this surface
///
/// If this surface has a parent, it should be hidden
/// or displayed, brought up at the same time as it.
pub parent: Option<wl_surface::WlSurface>,
/// Title of this shell surface
pub title: String,
/// App id for this shell surface
///
/// This identifier can be used to group surface together
/// as being several instance of the same app. This can
/// also be used as the D-Bus name for the app.
pub app_id: String,
/// Minimum size requested for this surface
///
/// A value of 0 on an axis means this axis is not constrained
pub min_size: (i32, i32),
/// Maximum size requested for this surface
///
/// A value of 0 on an axis means this axis is not constrained
pub max_size: (i32, i32),
}
impl ToplevelState {
/// Clone this ToplevelState
///
/// If the parent surface refers to a surface that no longer
/// exists, it is replaced by `None` in the process.
pub fn clone(&self) -> ToplevelState {
ToplevelState {
parent: self.parent.as_ref().and_then(|p| p.clone()),
title: self.title.clone(),
app_id: self.app_id.clone(),
min_size: self.min_size,
max_size: self.max_size,
}
}
}
/// The pending state of a popup surface
pub struct PopupState {
/// Parent of this popup surface
pub parent: wl_surface::WlSurface,
/// The positioner specifying how this tooltip should
/// be placed relative to its parent.
pub positioner: PositionerState,
}
impl PopupState {
/// Clone this PopupState
///
/// If the parent surface refers to a surface that no longer
/// exists, this will return `None`, as the popup can no
/// longer be meaningfully displayed.
pub fn clone(&self) -> Option<PopupState> {
if let Some(p) = self.parent.clone() {
Some(PopupState {
parent: p,
positioner: self.positioner,
})
} else {
// the parent surface does no exist any longer,
// this popup does not make any sense now
None
}
}
}
impl Default for ShellSurfacePendingState {
fn default() -> ShellSurfacePendingState {
ShellSurfacePendingState::None
}
}
/// Internal implementation data of shell surfaces
///
/// This type is only visible as type parameter of
/// the `Global` handle you are provided.
pub struct ShellSurfaceIData<U, R, CID, SID, SD> {
log: ::slog::Logger,
compositor_token: CompositorToken<U, R, CID>,
implementation: ShellSurfaceUserImplementation<U, R, CID, SID, SD>,
idata: Rc<RefCell<SID>>,
state_token: StateToken<ShellState<U, R, CID, SD>>,
}
impl<U, R, CID, SID, SD> Clone for ShellSurfaceIData<U, R, CID, SID, SD> {
fn clone(&self) -> ShellSurfaceIData<U, R, CID, SID, SD> {
ShellSurfaceIData {
log: self.log.clone(),
compositor_token: self.compositor_token,
implementation: self.implementation,
idata: self.idata.clone(),
state_token: self.state_token.clone(),
}
}
}
/// Create new `xdg_shell` and `wl_shell` globals.
///
/// The globals are directly registered into the eventloop, and this function
/// returns a `StateToken<_>` which you'll need access the list of shell
/// surfaces created by your clients.
///
/// It also returns the two global handles, in case you whish to remove these
/// globals from the event loop in the future.
pub fn shell_init<U, R, CID, SID, SD, L>(
evlh: &mut EventLoopHandle, token: CompositorToken<U, R, CID>,
implementation: ShellSurfaceUserImplementation<U, R, CID, SID, SD>, idata: SID, logger: L,
) -> (
StateToken<ShellState<U, R, CID, SD>>,
Global<wl_shell::WlShell, ShellSurfaceIData<U, R, CID, SID, SD>>,
Global<zxdg_shell_v6::ZxdgShellV6, ShellSurfaceIData<U, R, CID, SID, SD>>,
)
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: Default + 'static,
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger);
let shell_state = ShellState {
known_toplevels: Vec::new(),
known_popups: Vec::new(),
};
let shell_state_token = evlh.state().insert(shell_state);
let shell_surface_idata = ShellSurfaceIData {
log: log.new(o!("smithay_module" => "shell_handler")),
compositor_token: token,
implementation: implementation,
idata: Rc::new(RefCell::new(idata)),
state_token: shell_state_token.clone(),
};
// TODO: init globals
let wl_shell_global = evlh.register_global(
1,
self::wl_handlers::wl_shell_bind::<U, R, CID, SID, SD>,
shell_surface_idata.clone(),
);
let xdg_shell_global = evlh.register_global(
1,
self::xdg_handlers::xdg_shell_bind::<U, R, CID, SID, SD>,
shell_surface_idata.clone(),
);
(shell_state_token, wl_shell_global, xdg_shell_global)
}
/// Shell global state
///
/// This state allows you to retrieve a list of surfaces
/// currently known to the shell global.
pub struct ShellState<U, R, CID, SD> {
known_toplevels: Vec<ToplevelSurface<U, R, CID, SD>>,
known_popups: Vec<PopupSurface<U, R, CID, SD>>,
}
impl<U, R, CID, SD> ShellState<U, R, CID, SD>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SD: 'static,
{
/// Cleans the internal surface storage by removing all dead surfaces
pub fn cleanup_surfaces(&mut self) {
self.known_toplevels.retain(|s| s.alive());
self.known_popups.retain(|s| s.alive());
}
/// Access all the shell surfaces known by this handler
pub fn toplevel_surfaces(&self) -> &[ToplevelSurface<U, R, CID, SD>] {
&self.known_toplevels[..]
}
/// Access all the popup surfaces known by this handler
pub fn popup_surfaces(&self) -> &[PopupSurface<U, R, CID, SD>] {
&self.known_popups[..]
}
}
/*
* User interaction
*/
enum ShellClientKind {
Wl(wl_shell::WlShell),
Xdg(zxdg_shell_v6::ZxdgShellV6),
}
pub(crate) struct ShellClientData<SD> {
pending_ping: u32,
data: SD,
}
fn make_shell_client_data<SD: Default>() -> ShellClientData<SD> {
ShellClientData {
pending_ping: 0,
data: Default::default(),
}
}
/// A shell client
///
/// This represents an instanciation of a shell
/// global (be it `wl_shell` or `xdg_shell`).
///
/// Most of the time, you can consider that a
/// wayland client will be a single shell client.
///
/// You can use this handle to access a storage for any
/// client-specific data you wish to associate with it.
pub struct ShellClient<SD> {
kind: ShellClientKind,
_data: ::std::marker::PhantomData<*mut SD>,
}
impl<SD> ShellClient<SD> {
/// Is the shell client represented by this handle still connected?
pub fn alive(&self) -> bool {
match self.kind {
ShellClientKind::Wl(ref s) => s.status() == Liveness::Alive,
ShellClientKind::Xdg(ref s) => s.status() == Liveness::Alive,
}
}
/// Checks if this handle and the other one actually refer to the
/// same shell client
pub fn equals(&self, other: &Self) -> bool {
match (&self.kind, &other.kind) {
(&ShellClientKind::Wl(ref s1), &ShellClientKind::Wl(ref s2)) => s1.equals(s2),
(&ShellClientKind::Xdg(ref s1), &ShellClientKind::Xdg(ref s2)) => s1.equals(s2),
_ => false,
}
}
/// Send a ping request to this shell client
///
/// You'll receive the reply in the `Handler::cient_pong()` method.
///
/// A typical use is to start a timer at the same time you send this ping
/// request, and cancel it when you receive the pong. If the timer runs
/// down to 0 before a pong is received, mark the client as unresponsive.
///
/// Fails if this shell client already has a pending ping or is already dead.
pub fn send_ping(&self, serial: u32) -> Result<(), ()> {
if !self.alive() {
return Err(());
}
match self.kind {
ShellClientKind::Wl(ref shell) => {
let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.0.pending_ping == 0 {
return Err(());
}
guard.0.pending_ping = serial;
if let Some(surface) = guard.1.first() {
// there is at least one surface, send the ping
// if there is no surface, the ping will remain pending
// and will be sent when the client creates a surface
surface.ping(serial);
}
}
ShellClientKind::Xdg(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::xdg_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == 0 {
return Err(());
}
guard.pending_ping = serial;
shell.ping(serial);
}
}
Ok(())
}
/// Access the user data associated with this shell client
pub fn with_data<F, T>(&self, f: F) -> Result<T, ()>
where
F: FnOnce(&mut SD) -> T,
{
if !self.alive() {
return Err(());
}
match self.kind {
ShellClientKind::Wl(ref shell) => {
let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
Ok(f(&mut guard.0.data))
}
ShellClientKind::Xdg(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::xdg_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
Ok(f(&mut guard.data))
}
}
}
}
enum SurfaceKind {
Wl(wl_shell_surface::WlShellSurface),
XdgToplevel(zxdg_toplevel_v6::ZxdgToplevelV6),
XdgPopup(zxdg_popup_v6::ZxdgPopupV6),
}
/// A handle to a toplevel surface
///
/// This is an unified abstraction over the toplevel surfaces
/// of both `wl_shell` and `xdg_shell`.
pub struct ToplevelSurface<U, R, CID, SD> {
wl_surface: wl_surface::WlSurface,
shell_surface: SurfaceKind,
token: CompositorToken<U, R, CID>,
_shell_data: ::std::marker::PhantomData<SD>,
}
impl<U, R, CID, SD> ToplevelSurface<U, R, CID, SD>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SD: 'static,
{
/// Is the toplevel surface refered by this handle still alive?
pub fn alive(&self) -> bool {
let shell_surface_alive = match self.shell_surface {
SurfaceKind::Wl(ref s) => s.status() == Liveness::Alive,
SurfaceKind::XdgToplevel(ref s) => s.status() == Liveness::Alive,
SurfaceKind::XdgPopup(_) => unreachable!(),
};
shell_surface_alive && self.wl_surface.status() == Liveness::Alive
}
/// Do this handle and the other one actually refer to the same toplevel surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
/// Retrieve the shell client owning this toplevel surface
///
/// Returns `None` if the surface does actually no longer exist.
pub fn client(&self) -> Option<ShellClient<SD>> {
if !self.alive() {
return None;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => {
let &(_, ref shell) =
unsafe { &*(s.get_user_data() as *mut self::wl_handlers::ShellSurfaceUserData) };
Some(ShellClient {
kind: ShellClientKind::Wl(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
})
}
SurfaceKind::XdgToplevel(ref s) => {
let &(_, ref shell, _) =
unsafe { &*(s.get_user_data() as *mut self::xdg_handlers::ShellSurfaceUserData) };
Some(ShellClient {
kind: ShellClientKind::Xdg(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
})
}
SurfaceKind::XdgPopup(_) => unreachable!(),
}
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
///
/// The serial of this configure will be tracked waiting for the client to ACK it.
pub fn send_configure(&self, cfg: ToplevelConfigure) -> EventResult<()> {
if !self.alive() {
return EventResult::Destroyed;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => self::wl_handlers::send_toplevel_configure(s, cfg),
SurfaceKind::XdgToplevel(ref s) => {
self::xdg_handlers::send_toplevel_configure(self.token, s, cfg)
}
SurfaceKind::XdgPopup(_) => unreachable!(),
}
EventResult::Sent(())
}
/// Make sure this surface was configured
///
/// Returns `true` if it was, if not, returns `false` and raise
/// a protocol error to the associated client. Also returns `false`
/// if the surface is already destroyed.
///
/// xdg_shell mandates that a client acks a configure before commiting
/// anything.
pub fn ensure_configured(&self) -> bool {
if !self.alive() {
return false;
}
let configured = self.token
.with_role_data::<ShellSurfaceRole, _, _>(&self.wl_surface, |data| data.configured)
.expect("A shell surface object exists but the surface does not have the shell_surface role ?!");
if !configured {
if let SurfaceKind::XdgToplevel(ref s) = self.shell_surface {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::xdg_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
zxdg_surface_v6::Error::NotConstructed as u32,
"Surface has not been confgured yet.".into(),
);
} else {
unreachable!();
}
}
configured
}
/// Send a "close" event to the client
pub fn send_close(&self) -> EventResult<()> {
if !self.alive() {
return EventResult::Destroyed;
}
match self.shell_surface {
SurfaceKind::Wl(_) => EventResult::Sent(()),
SurfaceKind::XdgToplevel(ref s) => s.close(),
SurfaceKind::XdgPopup(_) => unreachable!(),
}
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Retrieve a copy of the pending state of this toplevel surface
///
/// Returns `None` of the toplevel surface actually no longer exists.
pub fn get_pending_state(&self) -> Option<ToplevelState> {
if !self.alive() {
return None;
}
self.token
.with_role_data::<ShellSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref state) => Some(state.clone()),
_ => None,
})
.ok()
.and_then(|x| x)
}
}
/// A handle to a popup surface
///
/// This is an unified abstraction over the popup surfaces
/// of both `wl_shell` and `xdg_shell`.
pub struct PopupSurface<U, R, CID, SD> {
wl_surface: wl_surface::WlSurface,
shell_surface: SurfaceKind,
token: CompositorToken<U, R, CID>,
_shell_data: ::std::marker::PhantomData<SD>,
}
impl<U, R, CID, SD> PopupSurface<U, R, CID, SD>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SD: 'static,
{
/// Is the popup surface refered by this handle still alive?
pub fn alive(&self) -> bool {
let shell_surface_alive = match self.shell_surface {
SurfaceKind::Wl(ref s) => s.status() == Liveness::Alive,
SurfaceKind::XdgPopup(ref s) => s.status() == Liveness::Alive,
SurfaceKind::XdgToplevel(_) => unreachable!(),
};
shell_surface_alive && self.wl_surface.status() == Liveness::Alive
}
/// Do this handle and the other one actually refer to the same popup surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
/// Retrieve the shell client owning this popup surface
///
/// Returns `None` if the surface does actually no longer exist.
pub fn client(&self) -> Option<ShellClient<SD>> {
if !self.alive() {
return None;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => {
let &(_, ref shell) =
unsafe { &*(s.get_user_data() as *mut self::wl_handlers::ShellSurfaceUserData) };
Some(ShellClient {
kind: ShellClientKind::Wl(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
})
}
SurfaceKind::XdgPopup(ref s) => {
let &(_, ref shell, _) =
unsafe { &*(s.get_user_data() as *mut self::xdg_handlers::ShellSurfaceUserData) };
Some(ShellClient {
kind: ShellClientKind::Xdg(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
})
}
SurfaceKind::XdgToplevel(_) => unreachable!(),
}
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
///
/// The serial of this configure will be tracked waiting for the client to ACK it.
pub fn send_configure(&self, cfg: PopupConfigure) -> EventResult<()> {
if !self.alive() {
return EventResult::Destroyed;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => self::wl_handlers::send_popup_configure(s, cfg),
SurfaceKind::XdgPopup(ref s) => self::xdg_handlers::send_popup_configure(self.token, s, cfg),
SurfaceKind::XdgToplevel(_) => unreachable!(),
}
EventResult::Sent(())
}
/// Make sure this surface was configured
///
/// Returns `true` if it was, if not, returns `false` and raise
/// a protocol error to the associated client. Also returns `false`
/// if the surface is already destroyed.
///
/// xdg_shell mandates that a client acks a configure before commiting
/// anything.
pub fn ensure_configured(&self) -> bool {
if !self.alive() {
return false;
}
let configured = self.token
.with_role_data::<ShellSurfaceRole, _, _>(&self.wl_surface, |data| data.configured)
.expect("A shell surface object exists but the surface does not have the shell_surface role ?!");
if !configured {
if let SurfaceKind::XdgPopup(ref s) = self.shell_surface {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::xdg_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
zxdg_surface_v6::Error::NotConstructed as u32,
"Surface has not been confgured yet.".into(),
);
} else {
unreachable!();
}
}
configured
}
/// Send a 'popup_done' event to the popup surface
///
/// It means that the use has dismissed the popup surface, or that
/// the pointer has left the area of popup grab if there was a grab.
pub fn send_popup_done(&self) -> EventResult<()> {
if !self.alive() {
return EventResult::Destroyed;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => s.popup_done(),
SurfaceKind::XdgPopup(ref s) => s.popup_done(),
SurfaceKind::XdgToplevel(_) => unreachable!(),
}
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Retrieve a copy of the pending state of this popup surface
///
/// Returns `None` of the popup surface actually no longer exists.
pub fn get_pending_state(&self) -> Option<PopupState> {
if !self.alive() {
return None;
}
self.token
.with_role_data::<ShellSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
ShellSurfacePendingState::Popup(ref state) => state.clone(),
_ => None,
})
.ok()
.and_then(|x| x)
}
}
/// A configure message for toplevel surfaces
pub struct ToplevelConfigure {
/// A suggestion for a new size for the surface
pub size: Option<(i32, i32)>,
/// A notification of what are the current states of this surface
///
/// A surface can be any combination of these possible states
/// at the same time.
pub states: Vec<zxdg_toplevel_v6::State>,
/// A serial number to track ACK from the client
///
/// This should be an ever increasing number, as the ACK-ing
/// from a client for a serial will validate all pending lower
/// serials.
pub serial: u32,
}
/// A configure message for popup surface
pub struct PopupConfigure {
/// The position chosen for this popup relative to
/// its parent
pub position: (i32, i32),
/// A suggested size for the popup
pub size: (i32, i32),
/// A serial number to track ACK from the client
///
/// This should be an ever increasing number, as the ACK-ing
/// from a client for a serial will validate all pending lower
/// serials.
pub serial: u32,
}
/// A sub-implementation for the shell
///
/// You need to provide this to handle events that the provided implementation
/// cannot process for you directly.
///
/// Depending on what you want to do, you might implement some of these functions
/// as doing nothing.
pub struct ShellSurfaceUserImplementation<U, R, CID, SID, SD> {
/// A new shell client was instanciated
pub new_client: fn(evlh: &mut EventLoopHandle, idata: &mut SID, client: ShellClient<SD>),
/// The pong for a pending ping of this shell client was received
///
/// The ShellHandler already checked for you that the serial matches the one
/// from the pending ping.
pub client_pong: fn(evlh: &mut EventLoopHandle, idata: &mut SID, client: ShellClient<SD>),
/// A new toplevel surface was created
///
/// You need to return a `ToplevelConfigure` from this function, which will be sent
/// to the client to configure this surface
pub new_toplevel:
fn(evlh: &mut EventLoopHandle, idata: &mut SID, surface: ToplevelSurface<U, R, CID, SD>)
-> ToplevelConfigure,
/// A new popup surface was created
///
/// You need to return a `PopupConfigure` from this function, which will be sent
/// to the client to configure this surface
pub new_popup: fn(evlh: &mut EventLoopHandle, idata: &mut SID, surface: PopupSurface<U, R, CID, SD>)
-> PopupConfigure,
/// The client requested the start of an interactive move for this surface
pub move_: fn(
evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: ToplevelSurface<U, R, CID, SD>,
seat: &wl_seat::WlSeat,
serial: u32,
),
/// The client requested the start of an interactive resize for this surface
///
/// The `edges` argument specifies which part of the window's border is being dragged.
pub resize: fn(
evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: ToplevelSurface<U, R, CID, SD>,
seat: &wl_seat::WlSeat,
serial: u32,
edges: zxdg_toplevel_v6::ResizeEdge,
),
/// This popup requests a grab of the pointer
///
/// This means it requests to be sent a `popup_done` event when the pointer leaves
/// the grab area.
pub grab: fn(
evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: PopupSurface<U, R, CID, SD>,
seat: &wl_seat::WlSeat,
serial: u32,
),
/// A toplevel surface requested its display state to be changed
///
/// Each field represents the request of the client for a specific property:
///
/// - `None`: no request is made to change this property
/// - `Some(true)`: this property should be enabled
/// - `Some(false)`: this property should be disabled
///
/// For fullscreen/maximization, the client can also optionnaly request a specific
/// output.
///
/// You are to answer with a `ToplevelConfigure` that will be sent to the client in
/// response.
pub change_display_state: fn(
evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: ToplevelSurface<U, R, CID, SD>,
maximized: Option<bool>,
minimized: Option<bool>,
fullscreen: Option<bool>,
output: Option<&wl_output::WlOutput>,
) -> ToplevelConfigure,
/// The client requests the window menu to be displayed on this surface at this location
///
/// This menu belongs to the compositor. It is typically expected to contain options for
/// control of the window (maximize/minimize/close/move/etc...).
pub show_window_menu: fn(
evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: ToplevelSurface<U, R, CID, SD>,
seat: &wl_seat::WlSeat,
serial: u32,
x: i32,
y: i32,
),
}
impl<U, R, CID, SID, SD> Copy for ShellSurfaceUserImplementation<U, R, CID, SID, SD> {}
impl<U, R, CID, SID, SD> Clone for ShellSurfaceUserImplementation<U, R, CID, SID, SD> {
fn clone(&self) -> ShellSurfaceUserImplementation<U, R, CID, SID, SD> {
*self
}
}
pub mod legacy;
pub mod xdg;

View File

@ -1,446 +0,0 @@
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
ToplevelConfigure, ToplevelState};
use std::sync::Mutex;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::*;
use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_positioner_v6 as xdg_positioner,
zxdg_toplevel_v6};
use wayland_server::{Client, EventLoopHandle, Resource};
use wayland_server::protocol::{wl_output, wl_shell, wl_shell_surface, wl_surface};
pub(crate) fn wl_shell_bind<U, R, CID, SID, SD>(
evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData<U, R, CID, SID, SD>, _: &Client,
shell: wl_shell::WlShell,
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: Default + 'static,
{
shell.set_user_data(Box::into_raw(Box::new(Mutex::new((
make_shell_client_data::<SD>(),
Vec::<wl_shell_surface::WlShellSurface>::new(),
)))) as *mut _);
evlh.register(
&shell,
shell_implementation(),
idata.clone(),
Some(destroy_shell::<SD>),
);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.new_client)(evlh, &mut *user_idata, make_shell_client(&shell));
}
/*
* wl_shell
*/
pub(crate) type ShellUserData<SD> = Mutex<(ShellClientData<SD>, Vec<wl_shell_surface::WlShellSurface>)>;
fn destroy_shell<SD>(shell: &wl_shell::WlShell) {
let ptr = shell.get_user_data();
shell.set_user_data(::std::ptr::null_mut());
let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) };
// explicitly call drop to not forget what we're doing here
::std::mem::drop(data);
}
pub fn make_shell_client<SD>(resource: &wl_shell::WlShell) -> ShellClient<SD> {
ShellClient {
kind: super::ShellClientKind::Wl(unsafe { resource.clone_unchecked() }),
_data: ::std::marker::PhantomData,
}
}
fn shell_implementation<U, R, CID, SID, SD>(
) -> wl_shell::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
wl_shell::Implementation {
get_shell_surface: |evlh, idata, _, shell, shell_surface, surface| {
let role_data = ShellSurfaceRole {
pending_state: ShellSurfacePendingState::None,
window_geometry: None,
pending_configures: Vec::new(),
configured: true,
};
if idata
.compositor_token
.give_role_with(surface, role_data)
.is_err()
{
shell.post_error(
wl_shell::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
shell_surface.set_user_data(Box::into_raw(Box::new(unsafe {
(surface.clone_unchecked(), shell.clone_unchecked())
})) as *mut _);
evlh.register(
&shell_surface,
shell_surface_implementation(),
idata.clone(),
Some(destroy_shell_surface),
);
// register ourselves to the wl_shell for ping handling
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.1.is_empty() && guard.0.pending_ping != 0 {
// there is a pending ping that no surface could receive yet, send it
// note this is not possible that it was received and then a wl_shell_surface was
// destroyed, because wl_shell_surface has no destructor!
shell_surface.ping(guard.0.pending_ping);
}
guard.1.push(shell_surface);
},
}
}
/*
* wl_shell_surface
*/
pub type ShellSurfaceUserData = (wl_surface::WlSurface, wl_shell::WlShell);
fn destroy_shell_surface(shell_surface: &wl_shell_surface::WlShellSurface) {
let ptr = shell_surface.get_user_data();
shell_surface.set_user_data(::std::ptr::null_mut());
// drop the WlSurface object
let surface = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
// explicitly call drop to not forget what we're doing here
::std::mem::drop(surface);
}
fn make_toplevel_handle<U, R, H, SD>(
token: CompositorToken<U, R, H>, resource: &wl_shell_surface::WlShellSurface
) -> super::ToplevelSurface<U, R, H, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::ToplevelSurface {
wl_surface: unsafe { wl_surface.clone_unchecked() },
shell_surface: super::SurfaceKind::Wl(unsafe { resource.clone_unchecked() }),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
fn make_popup_handle<U, R, H, SD>(
token: CompositorToken<U, R, H>, resource: &wl_shell_surface::WlShellSurface
) -> super::PopupSurface<U, R, H, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::PopupSurface {
wl_surface: unsafe { wl_surface.clone_unchecked() },
shell_surface: super::SurfaceKind::Wl(unsafe { resource.clone_unchecked() }),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
pub fn send_toplevel_configure(resource: &wl_shell_surface::WlShellSurface, configure: ToplevelConfigure) {
let (w, h) = configure.size.unwrap_or((0, 0));
resource.configure(wl_shell_surface::Resize::empty(), w, h);
}
pub fn send_popup_configure(resource: &wl_shell_surface::WlShellSurface, configure: PopupConfigure) {
let (w, h) = configure.size;
resource.configure(wl_shell_surface::Resize::empty(), w, h);
}
fn wl_handle_display_state_change<U, R, CID, SID, SD>(
evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
shell_surface: &wl_shell_surface::WlShellSurface, maximized: Option<bool>, minimized: Option<bool>,
fullscreen: Option<bool>, output: Option<&wl_output::WlOutput>,
) {
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
// handler callback
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.change_display_state)(
evlh,
&mut *user_idata,
handle,
maximized,
minimized,
fullscreen,
output,
);
// send the configure response to client
let (w, h) = configure.size.unwrap_or((0, 0));
shell_surface.configure(wl_shell_surface::Resize::None, w, h);
}
fn wl_set_parent<U, R, CID, SID, SD>(
idata: &ShellSurfaceIData<U, R, CID, SID, SD>, shell_surface: &wl_shell_surface::WlShellSurface,
parent: Option<wl_surface::WlSurface>,
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
let ptr = shell_surface.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(wl_surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut state) => {
state.parent = parent;
}
_ => unreachable!(),
})
.unwrap();
}
fn wl_ensure_toplevel<U, R, CID, SID, SD>(
evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
shell_surface: &wl_shell_surface::WlShellSurface,
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
let ptr = shell_surface.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
// copy token to make borrow checker happy
let token = idata.compositor_token;
let need_send = token
.with_role_data::<ShellSurfaceRole, _, _>(wl_surface, |data| {
match data.pending_state {
ShellSurfacePendingState::Toplevel(_) => {
return false;
}
ShellSurfacePendingState::Popup(_) => {
// this is no longer a popup, deregister it
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(wl_surface))
.unwrap_or(false)
});
}
ShellSurfacePendingState::None => {}
}
// This was not previously toplevel, need to make it toplevel
data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState {
parent: None,
title: String::new(),
app_id: String::new(),
min_size: (0, 0),
max_size: (0, 0),
});
true
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
// we need to notify about this new toplevel surface
if need_send {
evlh.state()
.get_mut(&idata.state_token)
.known_toplevels
.push(make_toplevel_handle(idata.compositor_token, shell_surface));
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.new_toplevel)(evlh, &mut *user_idata, handle);
send_toplevel_configure(shell_surface, configure);
}
}
fn shell_surface_implementation<U, R, CID, SID, SD>(
) -> wl_shell_surface::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
wl_shell_surface::Implementation {
pong: |evlh, idata, _, shell_surface, serial| {
let &(_, ref shell) = unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) };
let valid = {
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.0.pending_ping == serial {
guard.0.pending_ping = 0;
true
} else {
false
}
};
if valid {
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.client_pong)(evlh, &mut *user_idata, make_shell_client(shell));
}
},
move_: |evlh, idata, _, shell_surface, seat, serial| {
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.move_)(evlh, &mut *user_idata, handle, seat, serial);
},
resize: |evlh, idata, _, shell_surface, seat, serial, edges| {
let edges = zxdg_toplevel_v6::ResizeEdge::from_raw(edges.bits())
.unwrap_or(zxdg_toplevel_v6::ResizeEdge::None);
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.resize)(evlh, &mut *user_idata, handle, seat, serial, edges);
},
set_toplevel: |evlh, idata, _, shell_surface| {
wl_ensure_toplevel(evlh, idata, shell_surface);
wl_set_parent(idata, shell_surface, None);
wl_handle_display_state_change(
evlh,
idata,
shell_surface,
Some(false),
Some(false),
Some(false),
None,
)
},
set_transient: |evlh, idata, _, shell_surface, parent, _, _, _| {
wl_ensure_toplevel(evlh, idata, shell_surface);
wl_set_parent(
idata,
shell_surface,
Some(unsafe { parent.clone_unchecked() }),
);
wl_handle_display_state_change(
evlh,
idata,
shell_surface,
Some(false),
Some(false),
Some(false),
None,
)
},
set_fullscreen: |evlh, idata, _, shell_surface, _, _, output| {
wl_ensure_toplevel(evlh, idata, shell_surface);
wl_set_parent(idata, shell_surface, None);
wl_handle_display_state_change(
evlh,
idata,
shell_surface,
Some(false),
Some(false),
Some(true),
output,
)
},
set_popup: |evlh, idata, _, shell_surface, seat, serial, parent, x, y, _| {
let ptr = shell_surface.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
// we are reseting the popup state, so remove this surface from everywhere
evlh.state()
.get_mut(&idata.state_token)
.known_toplevels
.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(wl_surface))
.unwrap_or(false)
});
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(wl_surface))
.unwrap_or(false)
});
idata
.compositor_token
.with_role_data(wl_surface, |data| {
data.pending_state = ShellSurfacePendingState::Popup(PopupState {
parent: unsafe { parent.clone_unchecked() },
positioner: PositionerState {
rect_size: (1, 1),
anchor_rect: Rectangle {
x,
y,
width: 1,
height: 1,
},
anchor_edges: xdg_positioner::Anchor::empty(),
gravity: xdg_positioner::Gravity::empty(),
constraint_adjustment: xdg_positioner::ConstraintAdjustment::empty(),
offset: (0, 0),
},
});
})
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
// notify the handler about this new popup
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.push(make_popup_handle(idata.compositor_token, shell_surface));
let handle = make_popup_handle(idata.compositor_token, shell_surface);
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.new_popup)(evlh, &mut *user_idata, handle);
send_popup_configure(shell_surface, configure);
(idata.implementation.grab)(
evlh,
&mut *user_idata,
make_popup_handle(idata.compositor_token, shell_surface),
seat,
serial,
);
},
set_maximized: |evlh, idata, _, shell_surface, output| {
wl_ensure_toplevel(evlh, idata, shell_surface);
wl_set_parent(idata, shell_surface, None);
wl_handle_display_state_change(
evlh,
idata,
shell_surface,
Some(true),
Some(false),
Some(false),
output,
)
},
set_title: |_, idata, _, shell_surface, title| {
let ptr = shell_surface.get_user_data();
let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
idata
.compositor_token
.with_role_data(surface, |data| {
if let ShellSurfacePendingState::Toplevel(ref mut state) = data.pending_state {
state.title = title;
}
})
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
},
set_class: |_, idata, _, shell_surface, class| {
let ptr = shell_surface.get_user_data();
let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
idata
.compositor_token
.with_role_data(surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut state) => {
state.app_id = class;
}
_ => {}
})
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
},
}
}

View File

@ -0,0 +1,916 @@
//! Utilities for handling shell surfaces with the `xdg_shell` protocol
//!
//! This module provides automatic handling of shell surfaces objects, by being registered
//! as a global handler for `xdg_shell`.
//!
//! ## Why use this implementation
//!
//! This implementation can track for you the various shell surfaces defined by the
//! clients by handling the `xdg_shell` protocol. It also contains a compatibility
//! layer handling its precursor, the unstable `zxdg_shell_v6` protocol, which is
//! mostly identical.
//!
//! It allows you to easily access a list of all shell surfaces defined by your clients
//! access their associated metadata and underlying `wl_surface`s.
//!
//! This handler only handles the protocol exchanges with the client to present you the
//! information in a coherent and relatively easy to use maneer. All the actual drawing
//! and positioning logic of windows is out of its scope.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize this handler, simple use the `xdg_shell_init` function provided in this
//! module. You will need to provide it the `CompositorToken` you retrieved from an
//! instanciation of the compositor global provided by smithay.
//!
//! ```no_run
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! # extern crate wayland_protocols;
//! #
//! use smithay::wayland::compositor::roles::*;
//! use smithay::wayland::compositor::CompositorToken;
//! use smithay::wayland::shell::xdg::{xdg_shell_init, XdgSurfaceRole, XdgRequest};
//! use wayland_protocols::unstable::xdg_shell::v6::server::zxdg_shell_v6::ZxdgShellV6;
//! use wayland_server::{EventLoop, LoopToken};
//! # use wayland_server::protocol::{wl_seat, wl_output};
//! # #[derive(Default)] struct MySurfaceData;
//!
//! // define the roles type. You need to integrate the XdgSurface role:
//! define_roles!(MyRoles =>
//! [XdgSurface, XdgSurfaceRole]
//! );
//!
//! // define the metadata you want associated with the shell clients
//! #[derive(Default)]
//! struct MyShellData {
//! /* ... */
//! }
//!
//! # fn main() {
//! # let (mut display, event_loop) = wayland_server::Display::new();
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<(), MyRoles, _, _>(
//! # &mut display,
//! # event_loop.token(),
//! # |_, _| {},
//! # None
//! # );
//! let (shell_state, _, _) = xdg_shell_init(
//! &mut display,
//! event_loop.token(),
//! // token from the compositor implementation
//! compositor_token,
//! // your implementation, can also be a strucy implementing the
//! // appropriate Implementation<(), XdgRequest<_, _, _>> trait
//! |event: XdgRequest<_, _, MyShellData>, ()| { /* ... */ },
//! None // put a logger if you want
//! );
//!
//! // You're now ready to go!
//! # }
//! ```
//!
//! ### Access to shell surface and clients data
//!
//! There are mainly 3 kind of objects that you'll manipulate from this implementation:
//!
//! - `ShellClient`: This is a handle representing an isntanciation of a shell global
//! you can associate client-wise metadata to it (this is the `MyShellData` type in
//! the example above).
//! - `ToplevelSurface`: This is a handle representing a toplevel surface, you can
//! retrive a list of all currently alive toplevel surface from the `ShellState`.
//! - `PopupSurface`: This is a handle representing a popup/tooltip surface. Similarly,
//! you can get a list of all currently alive popup surface from the `ShellState`.
//!
//! You'll obtain these objects though two means: either via the callback methods of
//! the subhandler you provided, or via methods on the `ShellState` that you are given
//! (in an `Arc<Mutex<_>>`) as return value of the init function.
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::Role;
use wayland_protocols::xdg_shell::server::{xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base};
use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_shell_v6, zxdg_surface_v6,
zxdg_toplevel_v6};
use wayland_server::{Display, Global, LoopToken, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_output, wl_seat, wl_surface};
// handlers for the xdg_shell protocol
mod xdg_handlers;
// compatibility handlers for the zxdg_shell_v6 protocol, its earlier version
mod zxdgv6_handlers;
/// Metadata associated with the `xdg_surface` role
pub struct XdgSurfaceRole {
/// Pending state as requested by the client
///
/// The data in this field are double-buffered, you should
/// apply them on a surface commit.
pub pending_state: XdgSurfacePendingState,
/// Geometry of the surface
///
/// Defines, in surface relative coordinates, what should
/// be considered as "the surface itself", regarding focus,
/// window alignment, etc...
///
/// By default, you should consider the full contents of the
/// buffers of this surface and its subsurfaces.
pub window_geometry: Option<Rectangle>,
/// List of non-acked configures pending
///
/// Whenever a configure is acked by the client, all configure
/// older than it are discarded as well. As such, this vec contains
/// the serials of all the configure send to this surface that are
/// newer than the last ack received.
pub pending_configures: Vec<u32>,
/// Has this surface acked at least one configure?
///
/// xdg_shell defines it as illegal to commit on a surface that has
/// not yet acked a configure.
pub configured: bool,
}
#[derive(Clone, Debug)]
/// The state of a positioner, as set by the client
pub struct PositionerState {
/// Size of the rectangle that needs to be positioned
pub rect_size: (i32, i32),
/// Anchor rectangle in the parent surface coordinates
/// relative to which the surface must be positioned
pub anchor_rect: Rectangle,
/// Edges defining the anchor point
pub anchor_edges: xdg_positioner::Anchor,
/// Gravity direction for positioning the child surface
/// relative to its anchor point
pub gravity: xdg_positioner::Gravity,
/// Adjustments to do if previous criterias constraint the
/// surface
pub constraint_adjustment: xdg_positioner::ConstraintAdjustment,
/// Offset placement relative to the anchor point
pub offset: (i32, i32),
}
impl PositionerState {
pub(crate) fn new() -> PositionerState {
PositionerState {
rect_size: (0, 0),
anchor_rect: Rectangle {
x: 0,
y: 0,
width: 0,
height: 0,
},
anchor_edges: xdg_positioner::Anchor::None,
gravity: xdg_positioner::Gravity::None,
constraint_adjustment: xdg_positioner::ConstraintAdjustment::None,
offset: (0, 0),
}
}
}
/// Contents of the pending state of a shell surface, depending on its role
pub enum XdgSurfacePendingState {
/// This a regular, toplevel surface
///
/// This corresponds to the `xdg_toplevel` role
///
/// This is what you'll generaly interpret as "a window".
Toplevel(ToplevelState),
/// This is a popup surface
///
/// This corresponds to the `xdg_popup` role
///
/// This are mostly for small tooltips and similar short-lived
/// surfaces.
Popup(PopupState),
/// This surface was not yet assigned a kind
None,
}
/// State of a regular toplevel surface
pub struct ToplevelState {
/// Parent of this surface
///
/// If this surface has a parent, it should be hidden
/// or displayed, brought up at the same time as it.
pub parent: Option<Resource<wl_surface::WlSurface>>,
/// Title of this shell surface
pub title: String,
/// App id for this shell surface
///
/// This identifier can be used to group surface together
/// as being several instance of the same app. This can
/// also be used as the D-Bus name for the app.
pub app_id: String,
/// Minimum size requested for this surface
///
/// A value of 0 on an axis means this axis is not constrained
pub min_size: (i32, i32),
/// Maximum size requested for this surface
///
/// A value of 0 on an axis means this axis is not constrained
pub max_size: (i32, i32),
}
impl Clone for ToplevelState {
fn clone(&self) -> ToplevelState {
ToplevelState {
parent: self.parent.as_ref().map(|p| p.clone()),
title: self.title.clone(),
app_id: self.app_id.clone(),
min_size: self.min_size,
max_size: self.max_size,
}
}
}
/// The pending state of a popup surface
pub struct PopupState {
/// Parent of this popup surface
pub parent: Option<Resource<wl_surface::WlSurface>>,
/// The positioner specifying how this tooltip should
/// be placed relative to its parent.
pub positioner: PositionerState,
}
impl Clone for PopupState {
fn clone(&self) -> PopupState {
PopupState {
parent: self.parent.as_ref().map(|p| p.clone()),
positioner: self.positioner.clone(),
}
}
}
impl Default for XdgSurfacePendingState {
fn default() -> XdgSurfacePendingState {
XdgSurfacePendingState::None
}
}
pub(crate) struct ShellImplementation<U, R, SD> {
log: ::slog::Logger,
compositor_token: CompositorToken<U, R>,
loop_token: LoopToken,
user_impl: Rc<RefCell<Implementation<(), XdgRequest<U, R, SD>>>>,
shell_state: Arc<Mutex<ShellState<U, R, SD>>>,
}
impl<U, R, SD> Clone for ShellImplementation<U, R, SD> {
fn clone(&self) -> Self {
ShellImplementation {
log: self.log.clone(),
compositor_token: self.compositor_token.clone(),
loop_token: self.loop_token.clone(),
user_impl: self.user_impl.clone(),
shell_state: self.shell_state.clone(),
}
}
}
/// Create a new `xdg_shell` globals
pub fn xdg_shell_init<U, R, SD, L, Impl>(
display: &mut Display,
ltoken: LoopToken,
ctoken: CompositorToken<U, R>,
implementation: Impl,
logger: L,
) -> (
Arc<Mutex<ShellState<U, R, SD>>>,
Global<xdg_wm_base::XdgWmBase>,
Global<zxdg_shell_v6::ZxdgShellV6>,
)
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: Default + 'static,
L: Into<Option<::slog::Logger>>,
Impl: Implementation<(), XdgRequest<U, R, SD>>,
{
let log = ::slog_or_stdlog(logger);
let shell_state = Arc::new(Mutex::new(ShellState {
known_toplevels: Vec::new(),
known_popups: Vec::new(),
}));
let shell_impl = ShellImplementation {
log: log.new(o!("smithay_module" => "xdg_shell_handler")),
loop_token: ltoken.clone(),
compositor_token: ctoken,
user_impl: Rc::new(RefCell::new(implementation)),
shell_state: shell_state.clone(),
};
let shell_impl_z = shell_impl.clone();
let xdg_shell_global = display.create_global(&ltoken, 1, move |_version, shell| {
self::xdg_handlers::implement_wm_base(shell, &shell_impl);
});
let zxdgv6_shell_global = display.create_global(&ltoken, 1, move |_version, shell| {
self::zxdgv6_handlers::implement_shell(shell, &shell_impl_z);
});
(shell_state, xdg_shell_global, zxdgv6_shell_global)
}
/// Shell global state
///
/// This state allows you to retrieve a list of surfaces
/// currently known to the shell global.
pub struct ShellState<U, R, SD> {
known_toplevels: Vec<ToplevelSurface<U, R, SD>>,
known_popups: Vec<PopupSurface<U, R, SD>>,
}
impl<U, R, SD> ShellState<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
/// Access all the shell surfaces known by this handler
pub fn toplevel_surfaces(&self) -> &[ToplevelSurface<U, R, SD>] {
&self.known_toplevels[..]
}
/// Access all the popup surfaces known by this handler
pub fn popup_surfaces(&self) -> &[PopupSurface<U, R, SD>] {
&self.known_popups[..]
}
}
/*
* User interaction
*/
enum ShellClientKind {
Xdg(Resource<xdg_wm_base::XdgWmBase>),
ZxdgV6(Resource<zxdg_shell_v6::ZxdgShellV6>),
}
pub(crate) struct ShellClientData<SD> {
pending_ping: u32,
data: SD,
}
fn make_shell_client_data<SD: Default>() -> ShellClientData<SD> {
ShellClientData {
pending_ping: 0,
data: Default::default(),
}
}
/// A shell client
///
/// This represents an instanciation of a shell
/// global (be it `wl_shell` or `xdg_shell`).
///
/// Most of the time, you can consider that a
/// wayland client will be a single shell client.
///
/// You can use this handle to access a storage for any
/// client-specific data you wish to associate with it.
pub struct ShellClient<SD> {
kind: ShellClientKind,
_data: ::std::marker::PhantomData<*mut SD>,
}
impl<SD> ShellClient<SD> {
/// Is the shell client represented by this handle still connected?
pub fn alive(&self) -> bool {
match self.kind {
ShellClientKind::Xdg(ref s) => s.is_alive(),
ShellClientKind::ZxdgV6(ref s) => s.is_alive(),
}
}
/// Checks if this handle and the other one actually refer to the
/// same shell client
pub fn equals(&self, other: &Self) -> bool {
match (&self.kind, &other.kind) {
(&ShellClientKind::Xdg(ref s1), &ShellClientKind::Xdg(ref s2)) => s1.equals(s2),
(&ShellClientKind::ZxdgV6(ref s1), &ShellClientKind::ZxdgV6(ref s2)) => s1.equals(s2),
_ => false,
}
}
/// Send a ping request to this shell client
///
/// You'll receive the reply as a `XdgRequest::ClientPong` request.
///
/// A typical use is to start a timer at the same time you send this ping
/// request, and cancel it when you receive the pong. If the timer runs
/// down to 0 before a pong is received, mark the client as unresponsive.
///
/// Fails if this shell client already has a pending ping or is already dead.
pub fn send_ping(&self, serial: u32) -> Result<(), ()> {
if !self.alive() {
return Err(());
}
match self.kind {
ShellClientKind::Xdg(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::xdg_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == 0 {
return Err(());
}
guard.pending_ping = serial;
shell.send(xdg_wm_base::Event::Ping { serial });
}
ShellClientKind::ZxdgV6(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::zxdgv6_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == 0 {
return Err(());
}
guard.pending_ping = serial;
shell.send(zxdg_shell_v6::Event::Ping { serial });
}
}
Ok(())
}
/// Access the user data associated with this shell client
pub fn with_data<F, T>(&self, f: F) -> Result<T, ()>
where
F: FnOnce(&mut SD) -> T,
{
if !self.alive() {
return Err(());
}
match self.kind {
ShellClientKind::Xdg(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::xdg_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
Ok(f(&mut guard.data))
}
ShellClientKind::ZxdgV6(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::zxdgv6_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
Ok(f(&mut guard.data))
}
}
}
}
pub(crate) enum ToplevelKind {
Xdg(Resource<xdg_toplevel::XdgToplevel>),
ZxdgV6(Resource<zxdg_toplevel_v6::ZxdgToplevelV6>),
}
/// A handle to a toplevel surface
pub struct ToplevelSurface<U, R, SD> {
wl_surface: Resource<wl_surface::WlSurface>,
shell_surface: ToplevelKind,
token: CompositorToken<U, R>,
_shell_data: ::std::marker::PhantomData<SD>,
}
impl<U, R, SD> ToplevelSurface<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
/// Is the toplevel surface refered by this handle still alive?
pub fn alive(&self) -> bool {
let shell_alive = match self.shell_surface {
ToplevelKind::Xdg(ref s) => s.is_alive(),
ToplevelKind::ZxdgV6(ref s) => s.is_alive(),
};
shell_alive && self.wl_surface.is_alive()
}
/// Do this handle and the other one actually refer to the same toplevel surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
/// Retrieve the shell client owning this toplevel surface
///
/// Returns `None` if the surface does actually no longer exist.
pub fn client(&self) -> Option<ShellClient<SD>> {
if !self.alive() {
return None;
}
let shell = match self.shell_surface {
ToplevelKind::Xdg(ref s) => {
let &(_, ref shell, _) =
unsafe { &*(s.get_user_data() as *mut self::xdg_handlers::ShellSurfaceUserData) };
ShellClientKind::Xdg(shell.clone())
}
ToplevelKind::ZxdgV6(ref s) => {
let &(_, ref shell, _) =
unsafe { &*(s.get_user_data() as *mut self::zxdgv6_handlers::ShellSurfaceUserData) };
ShellClientKind::ZxdgV6(shell.clone())
}
};
Some(ShellClient {
kind: shell,
_data: ::std::marker::PhantomData,
})
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
///
/// The serial of this configure will be tracked waiting for the client to ACK it.
pub fn send_configure(&self, cfg: ToplevelConfigure) {
if !self.alive() {
return;
}
match self.shell_surface {
ToplevelKind::Xdg(ref s) => self::xdg_handlers::send_toplevel_configure(self.token, s, cfg),
ToplevelKind::ZxdgV6(ref s) => self::zxdgv6_handlers::send_toplevel_configure(self.token, s, cfg),
}
}
/// Make sure this surface was configured
///
/// Returns `true` if it was, if not, returns `false` and raise
/// a protocol error to the associated client. Also returns `false`
/// if the surface is already destroyed.
///
/// xdg_shell mandates that a client acks a configure before commiting
/// anything.
pub fn ensure_configured(&self) -> bool {
if !self.alive() {
return false;
}
let configured = self.token
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| data.configured)
.expect("A shell surface object exists but the surface does not have the shell_surface role ?!");
if !configured {
match self.shell_surface {
ToplevelKind::Xdg(ref s) => {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::xdg_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
xdg_surface::Error::NotConstructed as u32,
"Surface has not been configured yet.".into(),
);
}
ToplevelKind::ZxdgV6(ref s) => {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::zxdgv6_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
zxdg_surface_v6::Error::NotConstructed as u32,
"Surface has not been configured yet.".into(),
);
}
}
}
configured
}
/// Send a "close" event to the client
pub fn send_close(&self) {
match self.shell_surface {
ToplevelKind::Xdg(ref s) => s.send(xdg_toplevel::Event::Close),
ToplevelKind::ZxdgV6(ref s) => s.send(zxdg_toplevel_v6::Event::Close),
}
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&Resource<wl_surface::WlSurface>> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Retrieve a copy of the pending state of this toplevel surface
///
/// Returns `None` of the toplevel surface actually no longer exists.
pub fn get_pending_state(&self) -> Option<ToplevelState> {
if !self.alive() {
return None;
}
self.token
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
XdgSurfacePendingState::Toplevel(ref state) => Some(state.clone()),
_ => None,
})
.ok()
.and_then(|x| x)
}
}
pub(crate) enum PopupKind {
Xdg(Resource<xdg_popup::XdgPopup>),
ZxdgV6(Resource<zxdg_popup_v6::ZxdgPopupV6>),
}
/// A handle to a popup surface
///
/// This is an unified abstraction over the popup surfaces
/// of both `wl_shell` and `xdg_shell`.
pub struct PopupSurface<U, R, SD> {
wl_surface: Resource<wl_surface::WlSurface>,
shell_surface: PopupKind,
token: CompositorToken<U, R>,
_shell_data: ::std::marker::PhantomData<SD>,
}
impl<U, R, SD> PopupSurface<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
/// Is the popup surface refered by this handle still alive?
pub fn alive(&self) -> bool {
let shell_alive = match self.shell_surface {
PopupKind::Xdg(ref p) => p.is_alive(),
PopupKind::ZxdgV6(ref p) => p.is_alive(),
};
shell_alive && self.wl_surface.is_alive()
}
/// Do this handle and the other one actually refer to the same popup surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
/// Retrieve the shell client owning this popup surface
///
/// Returns `None` if the surface does actually no longer exist.
pub fn client(&self) -> Option<ShellClient<SD>> {
if !self.alive() {
return None;
}
let shell = match self.shell_surface {
PopupKind::Xdg(ref p) => {
let &(_, ref shell, _) =
unsafe { &*(p.get_user_data() as *mut self::xdg_handlers::ShellSurfaceUserData) };
ShellClientKind::Xdg(shell.clone())
}
PopupKind::ZxdgV6(ref p) => {
let &(_, ref shell, _) =
unsafe { &*(p.get_user_data() as *mut self::zxdgv6_handlers::ShellSurfaceUserData) };
ShellClientKind::ZxdgV6(shell.clone())
}
};
Some(ShellClient {
kind: shell,
_data: ::std::marker::PhantomData,
})
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
///
/// The serial of this configure will be tracked waiting for the client to ACK it.
pub fn send_configure(&self, cfg: PopupConfigure) {
if !self.alive() {
return;
}
match self.shell_surface {
PopupKind::Xdg(ref p) => {
self::xdg_handlers::send_popup_configure(self.token, p, cfg);
}
PopupKind::ZxdgV6(ref p) => {
self::zxdgv6_handlers::send_popup_configure(self.token, p, cfg);
}
}
}
/// Make sure this surface was configured
///
/// Returns `true` if it was, if not, returns `false` and raise
/// a protocol error to the associated client. Also returns `false`
/// if the surface is already destroyed.
///
/// xdg_shell mandates that a client acks a configure before commiting
/// anything.
pub fn ensure_configured(&self) -> bool {
if !self.alive() {
return false;
}
let configured = self.token
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| data.configured)
.expect("A shell surface object exists but the surface does not have the shell_surface role ?!");
if !configured {
match self.shell_surface {
PopupKind::Xdg(ref s) => {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::xdg_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
xdg_surface::Error::NotConstructed as u32,
"Surface has not been confgured yet.".into(),
);
}
PopupKind::ZxdgV6(ref s) => {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::zxdgv6_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
zxdg_surface_v6::Error::NotConstructed as u32,
"Surface has not been confgured yet.".into(),
);
}
}
}
configured
}
/// Send a 'popup_done' event to the popup surface
///
/// It means that the use has dismissed the popup surface, or that
/// the pointer has left the area of popup grab if there was a grab.
pub fn send_popup_done(&self) {
match self.shell_surface {
PopupKind::Xdg(ref p) => p.send(xdg_popup::Event::PopupDone),
PopupKind::ZxdgV6(ref p) => p.send(zxdg_popup_v6::Event::PopupDone),
}
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&Resource<wl_surface::WlSurface>> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Retrieve a copy of the pending state of this popup surface
///
/// Returns `None` of the popup surface actually no longer exists.
pub fn get_pending_state(&self) -> Option<PopupState> {
if !self.alive() {
return None;
}
self.token
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
XdgSurfacePendingState::Popup(ref state) => Some(state.clone()),
_ => None,
})
.ok()
.and_then(|x| x)
}
}
/// A configure message for toplevel surfaces
pub struct ToplevelConfigure {
/// A suggestion for a new size for the surface
pub size: Option<(i32, i32)>,
/// A notification of what are the current states of this surface
///
/// A surface can be any combination of these possible states
/// at the same time.
pub states: Vec<xdg_toplevel::State>,
/// A serial number to track ACK from the client
///
/// This should be an ever increasing number, as the ACK-ing
/// from a client for a serial will validate all pending lower
/// serials.
pub serial: u32,
}
/// A configure message for popup surface
pub struct PopupConfigure {
/// The position chosen for this popup relative to
/// its parent
pub position: (i32, i32),
/// A suggested size for the popup
pub size: (i32, i32),
/// A serial number to track ACK from the client
///
/// This should be an ever increasing number, as the ACK-ing
/// from a client for a serial will validate all pending lower
/// serials.
pub serial: u32,
}
/// Events generated by xdg shell surfaces
///
/// These are events that the provided implementation cannot process
/// for you directly.
///
/// Depending on what you want to do, you might ignore some of them
pub enum XdgRequest<U, R, SD> {
/// A new shell client was instanciated
NewClient {
/// the client
client: ShellClient<SD>,
},
/// The pong for a pending ping of this shell client was received
///
/// The ShellHandler already checked for you that the serial matches the one
/// from the pending ping.
ClientPong {
/// the client
client: ShellClient<SD>,
},
/// A new toplevel surface was created
///
/// You likely need to send a `ToplevelConfigure` to the surface, to hint the
/// client as to how its window should be
NewToplevel {
/// the surface
surface: ToplevelSurface<U, R, SD>,
},
/// A new popup surface was created
///
/// You likely need to send a `PopupConfigure` to the surface, to hint the
/// client as to how its popup should be
NewPopup {
/// the surface
surface: PopupSurface<U, R, SD>,
},
/// The client requested the start of an interactive move for this surface
Move {
/// the surface
surface: ToplevelSurface<U, R, SD>,
/// the seat associated to this move
seat: Resource<wl_seat::WlSeat>,
/// the grab serial
serial: u32,
},
/// The client requested the start of an interactive resize for this surface
Resize {
/// The surface
surface: ToplevelSurface<U, R, SD>,
/// The seat associated with this resize
seat: Resource<wl_seat::WlSeat>,
/// The grab serial
serial: u32,
/// Specification of which part of the window's border is being dragged
edges: xdg_toplevel::ResizeEdge,
},
/// This popup requests a grab of the pointer
///
/// This means it requests to be sent a `popup_done` event when the pointer leaves
/// the grab area.
Grab {
/// The surface
surface: PopupSurface<U, R, SD>,
/// The seat to grab
seat: Resource<wl_seat::WlSeat>,
/// The grab serial
serial: u32,
},
/// A toplevel surface requested to be maximized
Maximize {
/// The surface
surface: ToplevelSurface<U, R, SD>,
},
/// A toplevel surface requested to stop being maximized
UnMaximize {
/// The surface
surface: ToplevelSurface<U, R, SD>,
},
/// A toplevel surface requested to be set fullscreen
Fullscreen {
/// The surface
surface: ToplevelSurface<U, R, SD>,
/// The output (if any) on which the fullscreen is requested
output: Option<Resource<wl_output::WlOutput>>,
},
/// A toplevel surface request to stop being fullscreen
UnFullscreen {
/// The surface
surface: ToplevelSurface<U, R, SD>,
},
/// A toplevel surface requested to be minimized
Minimize {
/// The surface
surface: ToplevelSurface<U, R, SD>,
},
/// The client requests the window menu to be displayed on this surface at this location
///
/// This menu belongs to the compositor. It is typically expected to contain options for
/// control of the window (maximize/minimize/close/move/etc...).
ShowWindowMenu {
/// The surface
surface: ToplevelSurface<U, R, SD>,
/// The seat associated with this input grab
seat: Resource<wl_seat::WlSeat>,
/// the grab serial
serial: u32,
/// location of the menu request
location: (i32, i32),
},
}

View File

@ -0,0 +1,720 @@
use super::{make_shell_client_data, PopupConfigure, PopupKind, PopupState, PositionerState, ShellClient,
ShellClientData, ShellImplementation, ToplevelConfigure, ToplevelKind, ToplevelState,
XdgRequest, XdgSurfacePendingState, XdgSurfaceRole};
use std::sync::Mutex;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::*;
use wayland_protocols::xdg_shell::server::{xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base};
use wayland_server::{LoopToken, NewResource, Resource};
use wayland_server::commons::{downcast_impl, Implementation};
use wayland_server::protocol::wl_surface;
pub(crate) fn implement_wm_base<U, R, SD>(
shell: NewResource<xdg_wm_base::XdgWmBase>,
implem: &ShellImplementation<U, R, SD>,
) -> Resource<xdg_wm_base::XdgWmBase>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: Default + 'static,
{
let shell = shell.implement_nonsend(
implem.clone(),
Some(|shell, _| destroy_shell::<SD>(&shell)),
&implem.loop_token,
);
shell.set_user_data(Box::into_raw(Box::new(Mutex::new(make_shell_client_data::<SD>()))) as *mut _);
let mut user_impl = implem.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::NewClient {
client: make_shell_client(&shell),
},
(),
);
shell
}
/*
* xdg_shell
*/
pub(crate) type ShellUserData<SD> = Mutex<ShellClientData<SD>>;
fn destroy_shell<SD>(shell: &Resource<xdg_wm_base::XdgWmBase>) {
let ptr = shell.get_user_data();
shell.set_user_data(::std::ptr::null_mut());
let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(data);
}
pub(crate) fn make_shell_client<SD>(resource: &Resource<xdg_wm_base::XdgWmBase>) -> ShellClient<SD> {
ShellClient {
kind: super::ShellClientKind::Xdg(resource.clone()),
_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<xdg_wm_base::XdgWmBase>, xdg_wm_base::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: xdg_wm_base::Request, shell: Resource<xdg_wm_base::XdgWmBase>) {
match request {
xdg_wm_base::Request::Destroy => {
// all is handled by destructor
}
xdg_wm_base::Request::CreatePositioner { id } => {
implement_positioner(id, &self.loop_token);
}
xdg_wm_base::Request::GetXdgSurface { id, surface } => {
let role_data = XdgSurfaceRole {
pending_state: XdgSurfacePendingState::None,
window_geometry: None,
pending_configures: Vec::new(),
configured: false,
};
if self.compositor_token
.give_role_with(&surface, role_data)
.is_err()
{
shell.post_error(
xdg_wm_base::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
let xdg_surface = id.implement_nonsend(
self.clone(),
Some(destroy_surface::<U, R, SD>),
&self.loop_token,
);
xdg_surface
.set_user_data(Box::into_raw(Box::new((surface.clone(), shell.clone()))) as *mut _);
}
xdg_wm_base::Request::Pong { serial } => {
let valid = {
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == serial {
guard.pending_ping = 0;
true
} else {
false
}
};
if valid {
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::ClientPong {
client: make_shell_client(&shell),
},
(),
);
}
}
}
}
}
/*
* xdg_positioner
*/
fn destroy_positioner(positioner: &Resource<xdg_positioner::XdgPositioner>) {
let ptr = positioner.get_user_data();
positioner.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let surface = unsafe { Box::from_raw(ptr as *mut PositionerState) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(surface);
}
fn implement_positioner(
positioner: NewResource<xdg_positioner::XdgPositioner>,
token: &LoopToken,
) -> Resource<xdg_positioner::XdgPositioner> {
let positioner = positioner.implement_nonsend(
|request, positioner: Resource<_>| {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
match request {
xdg_positioner::Request::Destroy => {
// handled by destructor
}
xdg_positioner::Request::SetSize { width, height } => {
if width < 1 || height < 1 {
positioner.post_error(
xdg_positioner::Error::InvalidInput as u32,
"Invalid size for positioner.".into(),
);
} else {
state.rect_size = (width, height);
}
}
xdg_positioner::Request::SetAnchorRect {
x,
y,
width,
height,
} => {
if width < 1 || height < 1 {
positioner.post_error(
xdg_positioner::Error::InvalidInput as u32,
"Invalid size for positioner's anchor rectangle.".into(),
);
} else {
state.anchor_rect = Rectangle {
x,
y,
width,
height,
};
}
}
xdg_positioner::Request::SetAnchor { anchor } => {
state.anchor_edges = anchor;
}
xdg_positioner::Request::SetGravity { gravity } => {
state.gravity = gravity;
}
xdg_positioner::Request::SetConstraintAdjustment {
constraint_adjustment,
} => {
let constraint_adjustment =
xdg_positioner::ConstraintAdjustment::from_bits_truncate(constraint_adjustment);
state.constraint_adjustment = constraint_adjustment;
}
xdg_positioner::Request::SetOffset { x, y } => {
state.offset = (x, y);
}
}
},
Some(|positioner, _| destroy_positioner(&positioner)),
token,
);
let data = PositionerState::new();
positioner.set_user_data(Box::into_raw(Box::new(data)) as *mut _);
positioner
}
/*
* xdg_surface
*/
type XdgSurfaceUserData = (
Resource<wl_surface::WlSurface>,
Resource<xdg_wm_base::XdgWmBase>,
);
fn destroy_surface<U, R, SD>(
surface: Resource<xdg_surface::XdgSurface>,
implem: Box<Implementation<Resource<xdg_surface::XdgSurface>, xdg_surface::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = unsafe { Box::from_raw(ptr as *mut XdgSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
}
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |rdata| {
if let XdgSurfacePendingState::None = rdata.pending_state {
// all is good
} else {
data.1.post_error(
xdg_wm_base::Error::Role as u32,
"xdg_surface was destroyed before its role object".into(),
);
}
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
impl<U, R, SD> Implementation<Resource<xdg_surface::XdgSurface>, xdg_surface::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: xdg_surface::Request, xdg_surface: Resource<xdg_surface::XdgSurface>) {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) = unsafe { &*(ptr as *mut XdgSurfaceUserData) };
match request {
xdg_surface::Request::Destroy => {
// all is handled by our destructor
}
xdg_surface::Request::GetToplevel { id } => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.pending_state = XdgSurfacePendingState::Toplevel(ToplevelState {
parent: None,
title: String::new(),
app_id: String::new(),
min_size: (0, 0),
max_size: (0, 0),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
let toplevel = id.implement_nonsend(
self.clone(),
Some(destroy_toplevel::<U, R, SD>),
&self.loop_token,
);
toplevel.set_user_data(Box::into_raw(Box::new((
surface.clone(),
shell.clone(),
xdg_surface.clone(),
))) as *mut _);
self.shell_state
.lock()
.unwrap()
.known_toplevels
.push(make_toplevel_handle(self.compositor_token, &toplevel));
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::NewToplevel { surface: handle }, ());
}
xdg_surface::Request::GetPopup {
id,
parent,
positioner,
} => {
let positioner_data = unsafe { &*(positioner.get_user_data() as *const PositionerState) };
let parent_surface = parent.map(|parent| {
let parent_ptr = parent.get_user_data();
let &(ref parent_surface, _) = unsafe { &*(parent_ptr as *mut XdgSurfaceUserData) };
parent_surface.clone()
});
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.pending_state = XdgSurfacePendingState::Popup(PopupState {
parent: parent_surface,
positioner: positioner_data.clone(),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
let popup = id.implement_nonsend(
self.clone(),
Some(destroy_popup::<U, R, SD>),
&self.loop_token,
);
popup.set_user_data(Box::into_raw(Box::new((
surface.clone(),
shell.clone(),
xdg_surface.clone(),
))) as *mut _);
self.shell_state
.lock()
.unwrap()
.known_popups
.push(make_popup_handle(self.compositor_token, &popup));
let handle = make_popup_handle(self.compositor_token, &popup);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::NewPopup { surface: handle }, ());
}
xdg_surface::Request::SetWindowGeometry {
x,
y,
width,
height,
} => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.window_geometry = Some(Rectangle {
x,
y,
width,
height,
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
xdg_surface::Request::AckConfigure { serial } => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
let mut found = false;
data.pending_configures.retain(|&s| {
if s == serial {
found = true;
}
s > serial
});
if !found {
// client responded to a non-existing configure
shell.post_error(
xdg_wm_base::Error::InvalidSurfaceState as u32,
format!("Wrong configure serial: {}", serial),
);
}
data.configured = true;
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
}
}
}
/*
* xdg_toplevel
*/
pub type ShellSurfaceUserData = (
Resource<wl_surface::WlSurface>,
Resource<xdg_wm_base::XdgWmBase>,
Resource<xdg_surface::XdgSurface>,
);
// Utility functions allowing to factor out a lot of the upcoming logic
fn with_surface_toplevel_data<U, R, SD, F>(
implem: &ShellImplementation<U, R, SD>,
toplevel: &Resource<xdg_toplevel::XdgToplevel>,
f: F,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
F: FnOnce(&mut ToplevelState),
{
let ptr = toplevel.get_user_data();
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| match data.pending_state {
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
_ => unreachable!(),
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
pub fn send_toplevel_configure<U, R>(
token: CompositorToken<U, R>,
resource: &Resource<xdg_toplevel::XdgToplevel>,
configure: ToplevelConfigure,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (width, height) = configure.size.unwrap_or((0, 0));
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
let states = {
let mut states = configure.states;
let ptr = states.as_mut_ptr();
let len = states.len();
let cap = states.capacity();
::std::mem::forget(states);
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
};
let serial = configure.serial;
resource.send(xdg_toplevel::Event::Configure {
width,
height,
states,
});
shell_surface.send(xdg_surface::Event::Configure { serial });
// Add the configure as pending
token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_toplevel_handle<U, R, SD>(
token: CompositorToken<U, R>,
resource: &Resource<xdg_toplevel::XdgToplevel>,
) -> super::ToplevelSurface<U, R, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::ToplevelSurface {
wl_surface: wl_surface.clone(),
shell_surface: ToplevelKind::Xdg(resource.clone()),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<xdg_toplevel::XdgToplevel>, xdg_toplevel::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: xdg_toplevel::Request, toplevel: Resource<xdg_toplevel::XdgToplevel>) {
match request {
xdg_toplevel::Request::Destroy => {
// all it done by the destructor
}
xdg_toplevel::Request::SetParent { parent } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
let parent_ptr = toplevel_surface_parent.get_user_data();
let &(ref parent_surface, _, _) =
unsafe { &*(parent_ptr as *mut ShellSurfaceUserData) };
parent_surface.clone()
})
});
}
xdg_toplevel::Request::SetTitle { title } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.title = title;
});
}
xdg_toplevel::Request::SetAppId { app_id } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.app_id = app_id;
});
}
xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::ShowWindowMenu {
surface: handle,
seat,
serial,
location: (x, y),
},
(),
);
}
xdg_toplevel::Request::Move { seat, serial } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Move {
surface: handle,
seat,
serial,
},
(),
);
}
xdg_toplevel::Request::Resize {
seat,
serial,
edges,
} => {
let edges =
xdg_toplevel::ResizeEdge::from_raw(edges).unwrap_or(xdg_toplevel::ResizeEdge::None);
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Resize {
surface: handle,
seat,
serial,
edges,
},
(),
);
}
xdg_toplevel::Request::SetMaxSize { width, height } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
});
}
xdg_toplevel::Request::SetMinSize { width, height } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
});
}
xdg_toplevel::Request::SetMaximized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::Maximize { surface: handle }, ());
}
xdg_toplevel::Request::UnsetMaximized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::UnMaximize { surface: handle }, ());
}
xdg_toplevel::Request::SetFullscreen { output } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Fullscreen {
surface: handle,
output,
},
(),
);
}
xdg_toplevel::Request::UnsetFullscreen => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::UnFullscreen { surface: handle }, ());
}
xdg_toplevel::Request::SetMinimized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::Minimize { surface: handle }, ());
}
}
}
}
fn destroy_toplevel<U, R, SD>(
toplevel: Resource<xdg_toplevel::XdgToplevel>,
implem: Box<Implementation<Resource<xdg_toplevel::XdgToplevel>, xdg_toplevel::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = toplevel.get_user_data();
toplevel.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = *unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
} else {
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |data| {
data.pending_state = XdgSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
// remove this surface from the known ones (as well as any leftover dead surface)
implem
.shell_state
.lock()
.unwrap()
.known_toplevels
.retain(|other| other.alive());
}
/*
* xdg_popup
*/
pub(crate) fn send_popup_configure<U, R>(
token: CompositorToken<U, R>,
resource: &Resource<xdg_popup::XdgPopup>,
configure: PopupConfigure,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (x, y) = configure.position;
let (width, height) = configure.size;
let serial = configure.serial;
resource.send(xdg_popup::Event::Configure {
x,
y,
width,
height,
});
shell_surface.send(xdg_surface::Event::Configure { serial });
// Add the configure as pending
token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_popup_handle<U, R, SD>(
token: CompositorToken<U, R>,
resource: &Resource<xdg_popup::XdgPopup>,
) -> super::PopupSurface<U, R, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::PopupSurface {
wl_surface: wl_surface.clone(),
shell_surface: PopupKind::Xdg(resource.clone()),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<xdg_popup::XdgPopup>, xdg_popup::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: xdg_popup::Request, popup: Resource<xdg_popup::XdgPopup>) {
match request {
xdg_popup::Request::Destroy => {
// all is handled by our destructor
}
xdg_popup::Request::Grab { seat, serial } => {
let handle = make_popup_handle(self.compositor_token, &popup);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Grab {
surface: handle,
seat,
serial,
},
(),
);
}
}
}
}
fn destroy_popup<U, R, SD>(
popup: Resource<xdg_popup::XdgPopup>,
implem: Box<Implementation<Resource<xdg_popup::XdgPopup>, xdg_popup::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = popup.get_user_data();
popup.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = *unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
} else {
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |data| {
data.pending_state = XdgSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_popup exists but surface has not shell_surface role?!");
}
// remove this surface from the known ones (as well as any leftover dead surface)
implem
.shell_state
.lock()
.unwrap()
.known_popups
.retain(|other| other.alive());
}

View File

@ -0,0 +1,791 @@
use super::{make_shell_client_data, PopupConfigure, PopupKind, PopupState, PositionerState, ShellClient,
ShellClientData, ShellImplementation, ToplevelConfigure, ToplevelKind, ToplevelState,
XdgRequest, XdgSurfacePendingState, XdgSurfaceRole};
use std::sync::Mutex;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::*;
use wayland_protocols::xdg_shell::server::{xdg_positioner, xdg_toplevel};
use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_positioner_v6, zxdg_shell_v6,
zxdg_surface_v6, zxdg_toplevel_v6};
use wayland_server::{LoopToken, NewResource, Resource};
use wayland_server::commons::{downcast_impl, Implementation};
use wayland_server::protocol::wl_surface;
pub(crate) fn implement_shell<U, R, SD>(
shell: NewResource<zxdg_shell_v6::ZxdgShellV6>,
implem: &ShellImplementation<U, R, SD>,
) -> Resource<zxdg_shell_v6::ZxdgShellV6>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: Default + 'static,
{
let shell = shell.implement_nonsend(
implem.clone(),
Some(|shell, _| destroy_shell::<SD>(&shell)),
&implem.loop_token,
);
shell.set_user_data(Box::into_raw(Box::new(Mutex::new(make_shell_client_data::<SD>()))) as *mut _);
let mut user_impl = implem.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::NewClient {
client: make_shell_client(&shell),
},
(),
);
shell
}
/*
* xdg_shell
*/
pub(crate) type ShellUserData<SD> = Mutex<ShellClientData<SD>>;
fn destroy_shell<SD>(shell: &Resource<zxdg_shell_v6::ZxdgShellV6>) {
let ptr = shell.get_user_data();
shell.set_user_data(::std::ptr::null_mut());
let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(data);
}
pub(crate) fn make_shell_client<SD>(resource: &Resource<zxdg_shell_v6::ZxdgShellV6>) -> ShellClient<SD> {
ShellClient {
kind: super::ShellClientKind::ZxdgV6(resource.clone()),
_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<zxdg_shell_v6::ZxdgShellV6>, zxdg_shell_v6::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: zxdg_shell_v6::Request, shell: Resource<zxdg_shell_v6::ZxdgShellV6>) {
match request {
zxdg_shell_v6::Request::Destroy => {
// all is handled by destructor
}
zxdg_shell_v6::Request::CreatePositioner { id } => {
implement_positioner(id, &self.loop_token);
}
zxdg_shell_v6::Request::GetXdgSurface { id, surface } => {
let role_data = XdgSurfaceRole {
pending_state: XdgSurfacePendingState::None,
window_geometry: None,
pending_configures: Vec::new(),
configured: false,
};
if self.compositor_token
.give_role_with(&surface, role_data)
.is_err()
{
shell.post_error(
zxdg_shell_v6::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
let xdg_surface = id.implement_nonsend(
self.clone(),
Some(destroy_surface::<U, R, SD>),
&self.loop_token,
);
xdg_surface
.set_user_data(Box::into_raw(Box::new((surface.clone(), shell.clone()))) as *mut _);
}
zxdg_shell_v6::Request::Pong { serial } => {
let valid = {
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == serial {
guard.pending_ping = 0;
true
} else {
false
}
};
if valid {
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::ClientPong {
client: make_shell_client(&shell),
},
(),
);
}
}
}
}
}
/*
* xdg_positioner
*/
fn destroy_positioner(positioner: &Resource<zxdg_positioner_v6::ZxdgPositionerV6>) {
let ptr = positioner.get_user_data();
positioner.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let surface = unsafe { Box::from_raw(ptr as *mut PositionerState) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(surface);
}
fn implement_positioner(
positioner: NewResource<zxdg_positioner_v6::ZxdgPositionerV6>,
token: &LoopToken,
) -> Resource<zxdg_positioner_v6::ZxdgPositionerV6> {
let positioner = positioner.implement_nonsend(
|request, positioner: Resource<_>| {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
match request {
zxdg_positioner_v6::Request::Destroy => {
// handled by destructor
}
zxdg_positioner_v6::Request::SetSize { width, height } => {
if width < 1 || height < 1 {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid size for positioner.".into(),
);
} else {
state.rect_size = (width, height);
}
}
zxdg_positioner_v6::Request::SetAnchorRect {
x,
y,
width,
height,
} => {
if width < 1 || height < 1 {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid size for positioner's anchor rectangle.".into(),
);
} else {
state.anchor_rect = Rectangle {
x,
y,
width,
height,
};
}
}
zxdg_positioner_v6::Request::SetAnchor { anchor } => {
if let Some(anchor) = zxdg_anchor_to_xdg(anchor) {
state.anchor_edges = anchor;
} else {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid anchor for positioner.".into(),
);
}
}
zxdg_positioner_v6::Request::SetGravity { gravity } => {
if let Some(gravity) = zxdg_gravity_to_xdg(gravity) {
state.gravity = gravity;
} else {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid gravity for positioner.".into(),
);
}
}
zxdg_positioner_v6::Request::SetConstraintAdjustment {
constraint_adjustment,
} => {
let constraint_adjustment =
zxdg_positioner_v6::ConstraintAdjustment::from_bits_truncate(constraint_adjustment);
state.constraint_adjustment = zxdg_constraints_adg_to_xdg(constraint_adjustment);
}
zxdg_positioner_v6::Request::SetOffset { x, y } => {
state.offset = (x, y);
}
}
},
Some(|positioner, _| destroy_positioner(&positioner)),
token,
);
let data = PositionerState::new();
positioner.set_user_data(Box::into_raw(Box::new(data)) as *mut _);
positioner
}
/*
* xdg_surface
*/
type XdgSurfaceUserData = (
Resource<wl_surface::WlSurface>,
Resource<zxdg_shell_v6::ZxdgShellV6>,
);
fn destroy_surface<U, R, SD>(
surface: Resource<zxdg_surface_v6::ZxdgSurfaceV6>,
implem: Box<Implementation<Resource<zxdg_surface_v6::ZxdgSurfaceV6>, zxdg_surface_v6::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = unsafe { Box::from_raw(ptr as *mut XdgSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
}
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |rdata| {
if let XdgSurfacePendingState::None = rdata.pending_state {
// all is good
} else {
data.1.post_error(
zxdg_shell_v6::Error::Role as u32,
"xdg_surface was destroyed before its role object".into(),
);
}
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
impl<U, R, SD> Implementation<Resource<zxdg_surface_v6::ZxdgSurfaceV6>, zxdg_surface_v6::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(
&mut self,
request: zxdg_surface_v6::Request,
xdg_surface: Resource<zxdg_surface_v6::ZxdgSurfaceV6>,
) {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) = unsafe { &*(ptr as *mut XdgSurfaceUserData) };
match request {
zxdg_surface_v6::Request::Destroy => {
// all is handled by our destructor
}
zxdg_surface_v6::Request::GetToplevel { id } => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.pending_state = XdgSurfacePendingState::Toplevel(ToplevelState {
parent: None,
title: String::new(),
app_id: String::new(),
min_size: (0, 0),
max_size: (0, 0),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
let toplevel = id.implement_nonsend(
self.clone(),
Some(destroy_toplevel::<U, R, SD>),
&self.loop_token,
);
toplevel.set_user_data(Box::into_raw(Box::new((
surface.clone(),
shell.clone(),
xdg_surface.clone(),
))) as *mut _);
self.shell_state
.lock()
.unwrap()
.known_toplevels
.push(make_toplevel_handle(self.compositor_token, &toplevel));
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::NewToplevel { surface: handle }, ());
}
zxdg_surface_v6::Request::GetPopup {
id,
parent,
positioner,
} => {
let positioner_data = unsafe { &*(positioner.get_user_data() as *const PositionerState) };
let parent_ptr = parent.get_user_data();
let &(ref parent_surface, _) = unsafe { &*(parent_ptr as *mut XdgSurfaceUserData) };
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.pending_state = XdgSurfacePendingState::Popup(PopupState {
parent: Some(parent_surface.clone()),
positioner: positioner_data.clone(),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
let popup = id.implement_nonsend(
self.clone(),
Some(destroy_popup::<U, R, SD>),
&self.loop_token,
);
popup.set_user_data(Box::into_raw(Box::new((
surface.clone(),
shell.clone(),
xdg_surface.clone(),
))) as *mut _);
self.shell_state
.lock()
.unwrap()
.known_popups
.push(make_popup_handle(self.compositor_token, &popup));
let handle = make_popup_handle(self.compositor_token, &popup);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::NewPopup { surface: handle }, ());
}
zxdg_surface_v6::Request::SetWindowGeometry {
x,
y,
width,
height,
} => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.window_geometry = Some(Rectangle {
x,
y,
width,
height,
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
zxdg_surface_v6::Request::AckConfigure { serial } => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
let mut found = false;
data.pending_configures.retain(|&s| {
if s == serial {
found = true;
}
s > serial
});
if !found {
// client responded to a non-existing configure
shell.post_error(
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
format!("Wrong configure serial: {}", serial),
);
}
data.configured = true;
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
}
}
}
/*
* xdg_toplevel
*/
pub type ShellSurfaceUserData = (
Resource<wl_surface::WlSurface>,
Resource<zxdg_shell_v6::ZxdgShellV6>,
Resource<zxdg_surface_v6::ZxdgSurfaceV6>,
);
// Utility functions allowing to factor out a lot of the upcoming logic
fn with_surface_toplevel_data<U, R, SD, F>(
implem: &ShellImplementation<U, R, SD>,
toplevel: &Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
f: F,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
F: FnOnce(&mut ToplevelState),
{
let ptr = toplevel.get_user_data();
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| match data.pending_state {
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
_ => unreachable!(),
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
pub fn send_toplevel_configure<U, R>(
token: CompositorToken<U, R>,
resource: &Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
configure: ToplevelConfigure,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (width, height) = configure.size.unwrap_or((0, 0));
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
let states = {
let mut states = configure.states;
let ptr = states.as_mut_ptr();
let len = states.len();
let cap = states.capacity();
::std::mem::forget(states);
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
};
let serial = configure.serial;
resource.send(zxdg_toplevel_v6::Event::Configure {
width,
height,
states,
});
shell_surface.send(zxdg_surface_v6::Event::Configure { serial });
// Add the configure as pending
token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_toplevel_handle<U, R, SD>(
token: CompositorToken<U, R>,
resource: &Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
) -> super::ToplevelSurface<U, R, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::ToplevelSurface {
wl_surface: wl_surface.clone(),
shell_surface: ToplevelKind::ZxdgV6(resource.clone()),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<zxdg_toplevel_v6::ZxdgToplevelV6>, zxdg_toplevel_v6::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(
&mut self,
request: zxdg_toplevel_v6::Request,
toplevel: Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
) {
match request {
zxdg_toplevel_v6::Request::Destroy => {
// all it done by the destructor
}
zxdg_toplevel_v6::Request::SetParent { parent } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
let parent_ptr = toplevel_surface_parent.get_user_data();
let &(ref parent_surface, _, _) =
unsafe { &*(parent_ptr as *mut ShellSurfaceUserData) };
parent_surface.clone()
})
});
}
zxdg_toplevel_v6::Request::SetTitle { title } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.title = title;
});
}
zxdg_toplevel_v6::Request::SetAppId { app_id } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.app_id = app_id;
});
}
zxdg_toplevel_v6::Request::ShowWindowMenu { seat, serial, x, y } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::ShowWindowMenu {
surface: handle,
seat,
serial,
location: (x, y),
},
(),
);
}
zxdg_toplevel_v6::Request::Move { seat, serial } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Move {
surface: handle,
seat,
serial,
},
(),
);
}
zxdg_toplevel_v6::Request::Resize {
seat,
serial,
edges,
} => {
let edges = zxdg_toplevel_v6::ResizeEdge::from_raw(edges)
.unwrap_or(zxdg_toplevel_v6::ResizeEdge::None);
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Resize {
surface: handle,
seat,
serial,
edges: zxdg_edges_to_xdg(edges),
},
(),
);
}
zxdg_toplevel_v6::Request::SetMaxSize { width, height } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
});
}
zxdg_toplevel_v6::Request::SetMinSize { width, height } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
});
}
zxdg_toplevel_v6::Request::SetMaximized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::Maximize { surface: handle }, ());
}
zxdg_toplevel_v6::Request::UnsetMaximized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::UnMaximize { surface: handle }, ());
}
zxdg_toplevel_v6::Request::SetFullscreen { output } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Fullscreen {
surface: handle,
output,
},
(),
);
}
zxdg_toplevel_v6::Request::UnsetFullscreen => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::UnFullscreen { surface: handle }, ());
}
zxdg_toplevel_v6::Request::SetMinimized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::Minimize { surface: handle }, ());
}
}
}
}
fn destroy_toplevel<U, R, SD>(
toplevel: Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
implem: Box<Implementation<Resource<zxdg_toplevel_v6::ZxdgToplevelV6>, zxdg_toplevel_v6::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = toplevel.get_user_data();
toplevel.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = *unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
} else {
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |data| {
data.pending_state = XdgSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
// remove this surface from the known ones (as well as any leftover dead surface)
implem
.shell_state
.lock()
.unwrap()
.known_toplevels
.retain(|other| other.alive());
}
/*
* xdg_popup
*/
pub(crate) fn send_popup_configure<U, R>(
token: CompositorToken<U, R>,
resource: &Resource<zxdg_popup_v6::ZxdgPopupV6>,
configure: PopupConfigure,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (x, y) = configure.position;
let (width, height) = configure.size;
let serial = configure.serial;
resource.send(zxdg_popup_v6::Event::Configure {
x,
y,
width,
height,
});
shell_surface.send(zxdg_surface_v6::Event::Configure { serial });
// Add the configure as pending
token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_popup_handle<U, R, SD>(
token: CompositorToken<U, R>,
resource: &Resource<zxdg_popup_v6::ZxdgPopupV6>,
) -> super::PopupSurface<U, R, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::PopupSurface {
wl_surface: wl_surface.clone(),
shell_surface: PopupKind::ZxdgV6(resource.clone()),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<zxdg_popup_v6::ZxdgPopupV6>, zxdg_popup_v6::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: zxdg_popup_v6::Request, popup: Resource<zxdg_popup_v6::ZxdgPopupV6>) {
match request {
zxdg_popup_v6::Request::Destroy => {
// all is handled by our destructor
}
zxdg_popup_v6::Request::Grab { seat, serial } => {
let handle = make_popup_handle(self.compositor_token, &popup);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Grab {
surface: handle,
seat,
serial,
},
(),
);
}
}
}
}
fn destroy_popup<U, R, SD>(
popup: Resource<zxdg_popup_v6::ZxdgPopupV6>,
implem: Box<Implementation<Resource<zxdg_popup_v6::ZxdgPopupV6>, zxdg_popup_v6::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = popup.get_user_data();
popup.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = *unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
} else {
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |data| {
data.pending_state = XdgSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_popup exists but surface has not shell_surface role?!");
}
// remove this surface from the known ones (as well as any leftover dead surface)
implem
.shell_state
.lock()
.unwrap()
.known_popups
.retain(|other| other.alive());
}
fn zxdg_edges_to_xdg(e: zxdg_toplevel_v6::ResizeEdge) -> xdg_toplevel::ResizeEdge {
match e {
zxdg_toplevel_v6::ResizeEdge::None => xdg_toplevel::ResizeEdge::None,
zxdg_toplevel_v6::ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
zxdg_toplevel_v6::ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom,
zxdg_toplevel_v6::ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left,
zxdg_toplevel_v6::ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right,
zxdg_toplevel_v6::ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft,
zxdg_toplevel_v6::ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight,
zxdg_toplevel_v6::ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft,
zxdg_toplevel_v6::ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight,
}
}
fn zxdg_constraints_adg_to_xdg(
c: zxdg_positioner_v6::ConstraintAdjustment,
) -> xdg_positioner::ConstraintAdjustment {
xdg_positioner::ConstraintAdjustment::from_bits_truncate(c.bits())
}
fn zxdg_gravity_to_xdg(c: zxdg_positioner_v6::Gravity) -> Option<xdg_positioner::Gravity> {
match c.bits() {
0b0000 => Some(xdg_positioner::Gravity::None),
0b0001 => Some(xdg_positioner::Gravity::Top),
0b0010 => Some(xdg_positioner::Gravity::Bottom),
0b0100 => Some(xdg_positioner::Gravity::Left),
0b0101 => Some(xdg_positioner::Gravity::TopLeft),
0b0110 => Some(xdg_positioner::Gravity::BottomLeft),
0b1000 => Some(xdg_positioner::Gravity::Right),
0b1001 => Some(xdg_positioner::Gravity::TopRight),
0b1010 => Some(xdg_positioner::Gravity::BottomRight),
_ => None,
}
}
fn zxdg_anchor_to_xdg(c: zxdg_positioner_v6::Anchor) -> Option<xdg_positioner::Anchor> {
match c.bits() {
0b0000 => Some(xdg_positioner::Anchor::None),
0b0001 => Some(xdg_positioner::Anchor::Top),
0b0010 => Some(xdg_positioner::Anchor::Bottom),
0b0100 => Some(xdg_positioner::Anchor::Left),
0b0101 => Some(xdg_positioner::Anchor::TopLeft),
0b0110 => Some(xdg_positioner::Anchor::BottomLeft),
0b1000 => Some(xdg_positioner::Anchor::Right),
0b1001 => Some(xdg_positioner::Anchor::TopRight),
0b1010 => Some(xdg_positioner::Anchor::BottomRight),
_ => None,
}
}

View File

@ -1,697 +0,0 @@
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
ToplevelConfigure, ToplevelState};
use std::sync::Mutex;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::*;
use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_positioner_v6, zxdg_shell_v6,
zxdg_surface_v6, zxdg_toplevel_v6};
use wayland_server::{Client, EventLoopHandle, Resource};
use wayland_server::protocol::{wl_output, wl_surface};
pub(crate) fn xdg_shell_bind<U, R, CID, SID, SD>(
evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData<U, R, CID, SID, SD>, _: &Client,
shell: zxdg_shell_v6::ZxdgShellV6,
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: Default + 'static,
{
shell.set_user_data(Box::into_raw(Box::new(Mutex::new(make_shell_client_data::<SD>()))) as *mut _);
evlh.register(
&shell,
shell_implementation(),
idata.clone(),
Some(destroy_shell::<SD>),
);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.new_client)(evlh, &mut *user_idata, make_shell_client(&shell));
}
/*
* xdg_shell
*/
pub(crate) type ShellUserData<SD> = Mutex<ShellClientData<SD>>;
fn destroy_shell<SD>(shell: &zxdg_shell_v6::ZxdgShellV6) {
let ptr = shell.get_user_data();
shell.set_user_data(::std::ptr::null_mut());
let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(data);
}
pub(crate) fn make_shell_client<SD>(resource: &zxdg_shell_v6::ZxdgShellV6) -> ShellClient<SD> {
ShellClient {
kind: super::ShellClientKind::Xdg(unsafe { resource.clone_unchecked() }),
_data: ::std::marker::PhantomData,
}
}
fn shell_implementation<U, R, CID, SID, SD>(
) -> zxdg_shell_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
zxdg_shell_v6::Implementation {
destroy: |_, _, _, _| {},
create_positioner: |evlh, _, _, _, positioner| {
let data = PositionerState {
rect_size: (0, 0),
anchor_rect: Rectangle {
x: 0,
y: 0,
width: 0,
height: 0,
},
anchor_edges: zxdg_positioner_v6::Anchor::empty(),
gravity: zxdg_positioner_v6::Gravity::empty(),
constraint_adjustment: zxdg_positioner_v6::ConstraintAdjustment::empty(),
offset: (0, 0),
};
positioner.set_user_data(Box::into_raw(Box::new(data)) as *mut _);
evlh.register(
&positioner,
positioner_implementation(),
(),
Some(destroy_positioner),
);
},
get_xdg_surface: |evlh, idata, _, shell, xdg_surface, wl_surface| {
let role_data = ShellSurfaceRole {
pending_state: ShellSurfacePendingState::None,
window_geometry: None,
pending_configures: Vec::new(),
configured: false,
};
if idata
.compositor_token
.give_role_with(wl_surface, role_data)
.is_err()
{
shell.post_error(
zxdg_shell_v6::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
xdg_surface.set_user_data(Box::into_raw(Box::new((
unsafe { wl_surface.clone_unchecked() },
unsafe { shell.clone_unchecked() },
))) as *mut _);
evlh.register(
&xdg_surface,
surface_implementation(),
idata.clone(),
Some(destroy_surface),
);
},
pong: |evlh, idata, _, shell, serial| {
let valid = {
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == serial {
guard.pending_ping = 0;
true
} else {
false
}
};
if valid {
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.client_pong)(evlh, &mut *user_idata, make_shell_client(shell));
}
},
}
}
/*
* xdg_positioner
*/
fn destroy_positioner(positioner: &zxdg_positioner_v6::ZxdgPositionerV6) {
let ptr = positioner.get_user_data();
positioner.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let surface = unsafe { Box::from_raw(ptr as *mut PositionerState) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(surface);
}
fn positioner_implementation() -> zxdg_positioner_v6::Implementation<()> {
zxdg_positioner_v6::Implementation {
destroy: |_, _, _, _| {},
set_size: |_, _, _, positioner, width, height| {
if width < 1 || height < 1 {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid size for positioner.".into(),
);
} else {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.rect_size = (width, height);
}
},
set_anchor_rect: |_, _, _, positioner, x, y, width, height| {
if width < 1 || height < 1 {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid size for positioner's anchor rectangle.".into(),
);
} else {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.anchor_rect = Rectangle {
x,
y,
width,
height,
};
}
},
set_anchor: |_, _, _, positioner, anchor| {
use self::zxdg_positioner_v6::Anchor;
if anchor.contains(Anchor::Left | Anchor::Right) || anchor.contains(Anchor::Top | Anchor::Bottom)
{
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid anchor for positioner.".into(),
);
} else {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.anchor_edges = anchor;
}
},
set_gravity: |_, _, _, positioner, gravity| {
use self::zxdg_positioner_v6::Gravity;
if gravity.contains(Gravity::Left | Gravity::Right)
|| gravity.contains(Gravity::Top | Gravity::Bottom)
{
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid gravity for positioner.".into(),
);
} else {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.gravity = gravity;
}
},
set_constraint_adjustment: |_, _, _, positioner, constraint_adjustment| {
let constraint_adjustment =
zxdg_positioner_v6::ConstraintAdjustment::from_bits_truncate(constraint_adjustment);
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.constraint_adjustment = constraint_adjustment;
},
set_offset: |_, _, _, positioner, x, y| {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.offset = (x, y);
},
}
}
/*
* xdg_surface
*/
fn destroy_surface(surface: &zxdg_surface_v6::ZxdgSurfaceV6) {
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// drop the state
let data =
unsafe { Box::from_raw(ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6)) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(data);
}
fn surface_implementation<U, R, CID, SID, SD>(
) -> zxdg_surface_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
zxdg_surface_v6::Implementation {
destroy: |_, idata, _, xdg_surface| {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
if let ShellSurfacePendingState::None = data.pending_state {
// all is good
} else {
shell.post_error(
zxdg_shell_v6::Error::Role as u32,
"xdg_surface was destroyed before its role object".into(),
);
}
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
},
get_toplevel: |evlh, idata, _, xdg_surface, toplevel| {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState {
parent: None,
title: String::new(),
app_id: String::new(),
min_size: (0, 0),
max_size: (0, 0),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
toplevel.set_user_data(Box::into_raw(Box::new(unsafe {
(
surface.clone_unchecked(),
shell.clone_unchecked(),
xdg_surface.clone_unchecked(),
)
})) as *mut _);
evlh.register(
&toplevel,
toplevel_implementation(),
idata.clone(),
Some(destroy_toplevel),
);
// register to self
evlh.state()
.get_mut(&idata.state_token)
.known_toplevels
.push(make_toplevel_handle(idata.compositor_token, &toplevel));
// intial configure event
let handle = make_toplevel_handle(idata.compositor_token, &toplevel);
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.new_toplevel)(evlh, &mut *user_idata, handle);
send_toplevel_configure(idata.compositor_token, &toplevel, configure);
},
get_popup: |evlh, idata, _, xdg_surface, popup, parent, positioner| {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
let positioner_data = unsafe { &*(positioner.get_user_data() as *const PositionerState) };
let parent_ptr = parent.get_user_data();
let &(ref parent_surface, _) =
unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.pending_state = ShellSurfacePendingState::Popup(PopupState {
parent: unsafe { parent_surface.clone_unchecked() },
positioner: *positioner_data,
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
popup.set_user_data(Box::into_raw(Box::new(unsafe {
(
surface.clone_unchecked(),
shell.clone_unchecked(),
xdg_surface.clone_unchecked(),
)
})) as *mut _);
evlh.register(
&popup,
popup_implementation(),
idata.clone(),
Some(destroy_popup),
);
// register to self
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.push(make_popup_handle(idata.compositor_token, &popup));
// intial configure event
let handle = make_popup_handle(idata.compositor_token, &popup);
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.new_popup)(evlh, &mut *user_idata, handle);
send_popup_configure(idata.compositor_token, &popup, configure);
},
set_window_geometry: |_, idata, _, surface, x, y, width, height| {
let ptr = surface.get_user_data();
let &(ref surface, _) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.window_geometry = Some(Rectangle {
x,
y,
width,
height,
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
},
ack_configure: |_, idata, _, surface, serial| {
let ptr = surface.get_user_data();
let &(ref surface, ref shell) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
let mut found = false;
data.pending_configures.retain(|&s| {
if s == serial {
found = true;
}
s > serial
});
if !found {
// client responded to a non-existing configure
shell.post_error(
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
format!("Wrong configure serial: {}", serial),
);
}
data.configured = true;
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
},
}
}
/*
* xdg_toplevel
*/
pub type ShellSurfaceUserData = (
wl_surface::WlSurface,
zxdg_shell_v6::ZxdgShellV6,
zxdg_surface_v6::ZxdgSurfaceV6,
);
fn destroy_toplevel(surface: &zxdg_toplevel_v6::ZxdgToplevelV6) {
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let data = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
// explicit call to drop to not forget what we're doing there
::std::mem::drop(data);
}
// Utility functions allowing to factor out a lot of the upcoming logic
fn with_surface_toplevel_data<U, R, CID, SID, SD, F>(
idata: &ShellSurfaceIData<U, R, CID, SID, SD>, toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
F: FnOnce(&mut ToplevelState),
{
let ptr = toplevel.get_user_data();
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
_ => unreachable!(),
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn xdg_handle_display_state_change<U, R, CID, SID, SD>(
evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, maximized: Option<bool>, minimized: Option<bool>,
fullscreen: Option<bool>, output: Option<&wl_output::WlOutput>,
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
let handle = make_toplevel_handle(idata.compositor_token, toplevel);
// handler callback
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.change_display_state)(
evlh,
&mut *user_idata,
handle,
maximized,
minimized,
fullscreen,
output,
);
// send the configure response to client
send_toplevel_configure(idata.compositor_token, toplevel, configure);
}
pub fn send_toplevel_configure<U, R, ID>(
token: CompositorToken<U, R, ID>, resource: &zxdg_toplevel_v6::ZxdgToplevelV6,
configure: ToplevelConfigure,
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
ID: 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (w, h) = configure.size.unwrap_or((0, 0));
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
let states = {
let mut states = configure.states;
let ptr = states.as_mut_ptr();
let len = states.len();
let cap = states.capacity();
::std::mem::forget(states);
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
};
let serial = configure.serial;
resource.configure(w, h, states);
shell_surface.configure(serial);
// Add the configure as pending
token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_toplevel_handle<U, R, H, SD>(
token: CompositorToken<U, R, H>, resource: &zxdg_toplevel_v6::ZxdgToplevelV6
) -> super::ToplevelSurface<U, R, H, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::ToplevelSurface {
wl_surface: unsafe { wl_surface.clone_unchecked() },
shell_surface: super::SurfaceKind::XdgToplevel(unsafe { resource.clone_unchecked() }),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
fn toplevel_implementation<U, R, CID, SID, SD>(
) -> zxdg_toplevel_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
zxdg_toplevel_v6::Implementation {
destroy: |evlh, idata, _, toplevel| {
let ptr = toplevel.get_user_data();
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.pending_state = ShellSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
// remove this surface from the known ones (as well as any leftover dead surface)
evlh.state()
.get_mut(&idata.state_token)
.known_toplevels
.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(surface))
.unwrap_or(false)
});
},
set_parent: |_, idata, _, toplevel, parent| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
let parent_ptr = toplevel_surface_parent.get_user_data();
let &(ref parent_surface, _) =
unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
unsafe { parent_surface.clone_unchecked() }
})
});
},
set_title: |_, idata, _, toplevel, title| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.title = title;
});
},
set_app_id: |_, idata, _, toplevel, app_id| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.app_id = app_id;
});
},
show_window_menu: |evlh, idata, _, toplevel, seat, serial, x, y| {
let handle = make_toplevel_handle(idata.compositor_token, toplevel);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.show_window_menu)(evlh, &mut *user_idata, handle, seat, serial, x, y)
},
move_: |evlh, idata, _, toplevel, seat, serial| {
let handle = make_toplevel_handle(idata.compositor_token, toplevel);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.move_)(evlh, &mut *user_idata, handle, seat, serial)
},
resize: |evlh, idata, _, toplevel, seat, serial, edges| {
let edges =
zxdg_toplevel_v6::ResizeEdge::from_raw(edges).unwrap_or(zxdg_toplevel_v6::ResizeEdge::None);
let handle = make_toplevel_handle(idata.compositor_token, toplevel);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.resize)(evlh, &mut *user_idata, handle, seat, serial, edges)
},
set_max_size: |_, idata, _, toplevel, width, height| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
})
},
set_min_size: |_, idata, _, toplevel, width, height| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.min_size = (width, height);
})
},
set_maximized: |evlh, idata, _, toplevel| {
xdg_handle_display_state_change(evlh, idata, toplevel, Some(true), None, None, None);
},
unset_maximized: |evlh, idata, _, toplevel| {
xdg_handle_display_state_change(evlh, idata, toplevel, Some(false), None, None, None);
},
set_fullscreen: |evlh, idata, _, toplevel, seat| {
xdg_handle_display_state_change(evlh, idata, toplevel, None, None, Some(true), seat);
},
unset_fullscreen: |evlh, idata, _, toplevel| {
xdg_handle_display_state_change(evlh, idata, toplevel, None, None, Some(false), None);
},
set_minimized: |evlh, idata, _, toplevel| {
xdg_handle_display_state_change(evlh, idata, toplevel, None, Some(true), None, None);
},
}
}
/*
* xdg_popup
*/
fn destroy_popup(surface: &zxdg_popup_v6::ZxdgPopupV6) {
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let data = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
// explicit call to drop to not forget what we're doing
::std::mem::drop(data);
}
pub(crate) fn send_popup_configure<U, R, ID>(
token: CompositorToken<U, R, ID>, resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
ID: 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (x, y) = configure.position;
let (w, h) = configure.size;
let serial = configure.serial;
resource.configure(x, y, w, h);
shell_surface.configure(serial);
// Add the configure as pending
token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_popup_handle<U, R, H, SD>(
token: CompositorToken<U, R, H>, resource: &zxdg_popup_v6::ZxdgPopupV6
) -> super::PopupSurface<U, R, H, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::PopupSurface {
wl_surface: unsafe { wl_surface.clone_unchecked() },
shell_surface: super::SurfaceKind::XdgPopup(unsafe { resource.clone_unchecked() }),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
fn popup_implementation<U, R, CID, SID, SD>(
) -> zxdg_popup_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
zxdg_popup_v6::Implementation {
destroy: |evlh, idata, _, popup| {
let ptr = popup.get_user_data();
let &(ref surface, _, _) = unsafe {
&*(ptr
as *mut (
wl_surface::WlSurface,
zxdg_shell_v6::ZxdgShellV6,
zxdg_surface_v6::ZxdgSurfaceV6,
))
};
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.pending_state = ShellSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
// remove this surface from the known ones (as well as any leftover dead surface)
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(surface))
.unwrap_or(false)
});
},
grab: |evlh, idata, _, popup, seat, serial| {
let handle = make_popup_handle(idata.compositor_token, popup);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.grab)(evlh, &mut *user_idata, handle, seat, serial)
},
}
}

View File

@ -11,7 +11,7 @@
//! [the similar helpers](https://cgit.freedesktop.org/wayland/wayland/tree/src/wayland-shm.c)
//! of the wayland C libraries.
//!
//! To use it, first add a `ShmGlobal` to your event loop, specifying the formats
//! To use it, first add a `ShmGlobal` to your display, specifying the formats
//! you want to support (ARGB8888 and XRGB8888 are always considered as supported,
//! as specified by the wayland protocol) and obtain its `ShmToken`.
//!
@ -23,12 +23,13 @@
//! use wayland_server::protocol::wl_shm::Format;
//!
//! # fn main() {
//! # let (display, mut event_loop) = wayland_server::create_display();
//! # let (mut display, mut event_loop) = wayland_server::Display::new();
//! // Insert the ShmGlobal into your event loop
//! // Here, we specify that Yuyv and C8 format are supported
//! // additionnaly to the standart Argb8888 and Xrgb8888.
//! let shm_global = init_shm_global(
//! &mut event_loop,
//! &mut display,
//! event_loop.token(),
//! vec![Format::Yuyv, Format::C8],
//! None // we don't provide a logger here
//! );
@ -42,7 +43,8 @@
//! # extern crate wayland_server;
//! # extern crate smithay;
//! # use wayland_server::protocol::wl_buffer::WlBuffer;
//! # fn wrap(buffer: &WlBuffer) {
//! # use wayland_server::Resource;
//! # fn wrap(buffer: &Resource<WlBuffer>) {
//! use smithay::wayland::shm::{with_buffer_contents, BufferData};
//!
//! with_buffer_contents(&buffer,
@ -64,7 +66,8 @@
use self::pool::{Pool, ResizeError};
use std::rc::Rc;
use std::sync::Arc;
use wayland_server::{resource_is_registered, Client, EventLoopHandle, Global, Resource};
use wayland_server::{Display, Global, LoopToken, NewResource, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_buffer, wl_shm, wl_shm_pool};
mod pool;
@ -77,6 +80,7 @@ mod pool;
pub struct ShmGlobalData {
formats: Rc<Vec<wl_shm::Format>>,
log: ::slog::Logger,
token: LoopToken,
}
/// Create a new SHM global advertizing given supported formats.
@ -85,12 +89,15 @@ pub struct ShmGlobalData {
/// as they are required by the protocol. Formats given as argument
/// as additionnaly advertized.
///
/// The global is directly registered into the eventloop, and this function
/// The global is directly created on the provided `Display`, and this function
/// returns the global handle, in case you whish to remove this global in
/// the future.
pub fn init_shm_global<L>(
evlh: &mut EventLoopHandle, mut formats: Vec<wl_shm::Format>, logger: L
) -> Global<wl_shm::WlShm, ShmGlobalData>
display: &mut Display,
token: LoopToken,
mut formats: Vec<wl_shm::Format>,
logger: L,
) -> Global<wl_shm::WlShm>
where
L: Into<Option<::slog::Logger>>,
{
@ -102,9 +109,16 @@ where
let data = ShmGlobalData {
formats: Rc::new(formats),
log: log.new(o!("smithay_module" => "shm_handler")),
token: token.clone(),
};
evlh.register_global::<wl_shm::WlShm, _>(1, shm_global_bind, data)
display.create_global::<wl_shm::WlShm, _>(&token, 1, move |_version, shm_new: NewResource<_>| {
let shm = shm_new.implement_nonsend(data.clone(), None::<fn(_, _)>, &data.token);
// send the formats
for f in &data.formats[..] {
shm.send(wl_shm::Event::Format { format: *f });
}
})
}
/// Error that can occur when accessing an SHM buffer
@ -131,11 +145,11 @@ pub enum BufferAccessError {
///
/// If the buffer is not managed by the provided `ShmGlobal`, the closure is not called
/// and this method will return `Err(())` (this will be the case for an EGL buffer for example).
pub fn with_buffer_contents<F>(buffer: &wl_buffer::WlBuffer, f: F) -> Result<(), BufferAccessError>
pub fn with_buffer_contents<F>(buffer: &Resource<wl_buffer::WlBuffer>, f: F) -> Result<(), BufferAccessError>
where
F: FnOnce(&[u8], BufferData),
{
if !resource_is_registered(buffer, &buffer_implementation()) {
if !buffer.is_implemented_with::<ShmGlobalData>() {
return Err(BufferAccessError::NotManaged);
}
let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) };
@ -151,52 +165,43 @@ where
Ok(())
}
fn shm_global_bind(evlh: &mut EventLoopHandle, data: &mut ShmGlobalData, _: &Client, global: wl_shm::WlShm) {
// register an handler for this shm
evlh.register(&global, shm_implementation(), data.clone(), None);
// and then the custom formats
for f in &data.formats[..] {
global.format(*f);
}
}
impl Implementation<Resource<wl_shm::WlShm>, wl_shm::Request> for ShmGlobalData {
fn receive(&mut self, request: wl_shm::Request, shm: Resource<wl_shm::WlShm>) {
use self::wl_shm::{Error, Request};
fn shm_implementation() -> wl_shm::Implementation<ShmGlobalData> {
wl_shm::Implementation {
create_pool: |evlh, data, _, shm, pool, fd, size| {
if size <= 0 {
shm.post_error(
wl_shm::Error::InvalidFd as u32,
"Invalid size for a new wl_shm_pool.".into(),
);
return;
}
let mmap_pool = match Pool::new(fd, size as usize, data.log.clone()) {
Ok(p) => p,
Err(()) => {
match request {
Request::CreatePool { id: pool, fd, size } => {
if size <= 0 {
shm.post_error(
wl_shm::Error::InvalidFd as u32,
format!("Failed mmap of fd {}.", fd),
Error::InvalidFd as u32,
"Invalid size for a new wl_shm_pool.".into(),
);
return;
}
};
let arc_pool = Box::new(Arc::new(mmap_pool));
evlh.register(
&pool,
shm_pool_implementation(),
data.clone(),
Some(destroy_shm_pool),
);
pool.set_user_data(Box::into_raw(arc_pool) as *mut ());
},
let mmap_pool = match Pool::new(fd, size as usize, self.log.clone()) {
Ok(p) => p,
Err(()) => {
shm.post_error(
wl_shm::Error::InvalidFd as u32,
format!("Failed mmap of fd {}.", fd),
);
return;
}
};
let arc_pool = Box::new(Arc::new(mmap_pool));
let pool = pool.implement_nonsend(
self.clone(),
Some(|pool: Resource<_>, _| {
drop(unsafe { Box::from_raw(pool.get_user_data() as *mut Arc<Pool>) })
}),
&self.token,
);
pool.set_user_data(Box::into_raw(arc_pool) as *mut ());
}
}
}
}
fn destroy_shm_pool(pool: &wl_shm_pool::WlShmPool) {
let arc_pool = unsafe { Box::from_raw(pool.get_user_data() as *mut Arc<Pool>) };
drop(arc_pool)
}
/// Details of the contents of a buffer relative to its pool
#[derive(Copy, Clone, Debug)]
pub struct BufferData {
@ -217,30 +222,48 @@ struct InternalBufferData {
data: BufferData,
}
fn shm_pool_implementation() -> wl_shm_pool::Implementation<ShmGlobalData> {
wl_shm_pool::Implementation {
create_buffer: |evlh, data, _, pool, buffer, offset, width, height, stride, format| {
if !data.formats.contains(&format) {
buffer.post_error(wl_shm::Error::InvalidFormat as u32, String::new());
return;
impl Implementation<Resource<wl_shm_pool::WlShmPool>, wl_shm_pool::Request> for ShmGlobalData {
fn receive(&mut self, request: wl_shm_pool::Request, pool: Resource<wl_shm_pool::WlShmPool>) {
use self::wl_shm_pool::Request;
let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc<Pool>) };
match request {
Request::CreateBuffer {
id: buffer,
offset,
width,
height,
stride,
format,
} => {
if !self.formats.contains(&format) {
pool.post_error(
wl_shm::Error::InvalidFormat as u32,
format!("SHM format {:?} is not supported.", format),
);
return;
}
let data = Box::into_raw(Box::new(InternalBufferData {
pool: arc_pool.clone(),
data: BufferData {
offset: offset,
width: width,
height: height,
stride: stride,
format: format,
},
}));
let buffer = buffer.implement_nonsend(
self.clone(),
Some(|buffer: Resource<_>, _| {
drop(unsafe { Box::from_raw(buffer.get_user_data() as *mut InternalBufferData) })
}),
&self.token,
);
buffer.set_user_data(data as *mut ());
}
let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc<Pool>) };
let data = Box::into_raw(Box::new(InternalBufferData {
pool: arc_pool.clone(),
data: BufferData {
offset: offset,
width: width,
height: height,
stride: stride,
format: format,
},
}));
evlh.register(&buffer, buffer_implementation(), (), Some(destroy_buffer));
buffer.set_user_data(data as *mut ());
},
resize: |_, _, _, pool, size| {
let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc<Pool>) };
match arc_pool.resize(size) {
Request::Resize { size } => match arc_pool.resize(size) {
Ok(()) => {}
Err(ResizeError::InvalidSize) => {
pool.post_error(
@ -251,19 +274,15 @@ fn shm_pool_implementation() -> wl_shm_pool::Implementation<ShmGlobalData> {
Err(ResizeError::MremapFailed) => {
pool.post_error(wl_shm::Error::InvalidFd as u32, "mremap failed.".into());
}
}
},
destroy: |_, _, _, _| {},
},
Request::Destroy => {}
}
}
}
fn destroy_buffer(buffer: &wl_buffer::WlBuffer) {
let buffer_data = unsafe { Box::from_raw(buffer.get_user_data() as *mut InternalBufferData) };
drop(buffer_data)
}
fn buffer_implementation() -> wl_buffer::Implementation<()> {
wl_buffer::Implementation {
destroy: |_, _, _, _| {},
impl Implementation<Resource<wl_buffer::WlBuffer>, wl_buffer::Request> for ShmGlobalData {
fn receive(&mut self, request: wl_buffer::Request, _pool: Resource<wl_buffer::WlBuffer>) {
// this will break if new requests are added to buffer =)
let wl_buffer::Request::Destroy = request;
}
}

View File

@ -161,8 +161,8 @@ unsafe fn map(fd: RawFd, size: usize) -> Result<*mut u8, ()> {
let ret = mman::mmap(
ptr::null_mut(),
size,
mman::PROT_READ,
mman::MAP_SHARED,
mman::ProtFlags::PROT_READ,
mman::MapFlags::MAP_SHARED,
fd,
0,
);
@ -179,8 +179,8 @@ unsafe fn nullify_map(ptr: *mut u8, size: usize) -> Result<(), ()> {
let ret = mman::mmap(
ptr as *mut _,
size,
mman::PROT_READ,
mman::MAP_ANONYMOUS | mman::MAP_PRIVATE | mman::MAP_FIXED,
mman::ProtFlags::PROT_READ,
mman::MapFlags::MAP_ANONYMOUS | mman::MapFlags::MAP_PRIVATE | mman::MapFlags::MAP_FIXED,
-1,
0,
);
@ -191,7 +191,7 @@ unsafe fn place_sigbus_handler() {
// create our sigbus handler
let action = SigAction::new(
SigHandler::SigAction(sigbus_handler),
signal::SA_NODEFER,
signal::SaFlags::SA_NODEFER,
signal::SigSet::empty(),
);
match signal::sigaction(Signal::SIGBUS, &action) {