Merge pull request #88 from Smithay/wayland_rs_20
Upgrade to wayland-rs 0.20
This commit is contained in:
commit
60fc47917d
|
@ -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
|
||||
|
|
|
@ -8,6 +8,10 @@ rust:
|
|||
|
||||
sudo: required
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
dist: trusty
|
||||
|
||||
# We cannot cache .vagga, because we actually do not have read permissions
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -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"]
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,10 +28,11 @@ 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| {
|
||||
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() {
|
||||
|
@ -67,7 +71,7 @@ pub fn surface_implementation(
|
|||
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();
|
||||
buffer.send(wl_buffer::Event::Release);
|
||||
}
|
||||
Err(err) => panic!("EGL error: {}", err),
|
||||
}
|
||||
|
@ -80,55 +84,6 @@ pub fn surface_implementation(
|
|||
None => {}
|
||||
}
|
||||
});
|
||||
},
|
||||
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: |_, _, _, _, _, _, _| {},
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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)
|
||||
|
|
161
examples/udev.rs
161
examples/udev.rs
|
@ -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,10 +358,7 @@ fn main() {
|
|||
);
|
||||
|
||||
let (w, h) = (1920, 1080); // Hardcode full-hd res
|
||||
event_loop
|
||||
.state()
|
||||
.get_mut(&output_token)
|
||||
.change_current_state(
|
||||
output.change_current_state(
|
||||
Some(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
|
@ -375,10 +367,7 @@ fn main() {
|
|||
None,
|
||||
None,
|
||||
);
|
||||
event_loop
|
||||
.state()
|
||||
.get_mut(&output_token)
|
||||
.set_preferred(Mode {
|
||||
output.set_preferred(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
|
@ -392,9 +381,7 @@ 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 {
|
||||
libinput_backend.set_handler(LibinputInputHandler {
|
||||
log: log.clone(),
|
||||
pointer,
|
||||
keyboard,
|
||||
|
@ -404,16 +391,15 @@ fn main() {
|
|||
serial: 0,
|
||||
session: session,
|
||||
running: running.clone(),
|
||||
},
|
||||
);
|
||||
let libinput_event_source = libinput_bind(libinput_backend, &mut event_loop)
|
||||
});
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,10 +223,7 @@ fn main() {
|
|||
log.clone(),
|
||||
);
|
||||
|
||||
event_loop
|
||||
.state()
|
||||
.get_mut(&output_token)
|
||||
.change_current_state(
|
||||
output.change_current_state(
|
||||
Some(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
|
@ -245,26 +232,20 @@ fn main() {
|
|||
None,
|
||||
None,
|
||||
);
|
||||
event_loop
|
||||
.state()
|
||||
.get_mut(&output_token)
|
||||
.set_preferred(Mode {
|
||||
output.set_preferred(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
});
|
||||
|
||||
input.set_handler(
|
||||
&mut event_loop,
|
||||
WinitInputHandler {
|
||||
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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
Err(err) => self.handler.error(&mut device, err),
|
||||
},
|
||||
error: |evlh, &mut (ref mut device, ref mut handler), _, error| {
|
||||
FdEvent::Error { error, .. } => {
|
||||
warn!(device.logger, "DrmDevice errored: {}", error);
|
||||
handler.error(evlh, device, error.into());
|
||||
},
|
||||
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;
|
||||
|
|
|
@ -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<
|
||||
(
|
||||
|
|
|
@ -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::<
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, _, _| {
|
||||
impl Implementation<(), FdEvent> for LibinputInputBackend {
|
||||
fn receive(&mut self, event: FdEvent, (): ()) {
|
||||
match event {
|
||||
FdEvent::Ready { .. } => {
|
||||
use backend::input::InputBackend;
|
||||
if let Err(error) = backend.dispatch_new_events(evlh) {
|
||||
warn!(backend.logger, "Libinput errored: {}", error);
|
||||
}
|
||||
},
|
||||
error: |_evlh, ref backend, _, error| {
|
||||
warn!(backend.logger, "Libinput fd errored: {}", error);
|
||||
},
|
||||
if let Err(error) = self.dispatch_new_events() {
|
||||
warn!(self.logger, "Libinput errored: {}", error);
|
||||
}
|
||||
}
|
||||
FdEvent::Error { error, .. } => {
|
||||
warn!(self.logger, "Libinput fd errored: {}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,13 +495,14 @@ impl Drop for LogindSessionNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
fn fd_event_source_implementation() -> FdEventSourceImpl<Rc<LogindSessionImpl>> {
|
||||
FdEventSourceImpl {
|
||||
ready: |evlh, session, fd, interest| {
|
||||
let conn = session.conn.borrow();
|
||||
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 interest {
|
||||
match mask {
|
||||
x if x.contains(FdInterest::READ) && x.contains(FdInterest::WRITE) => {
|
||||
WatchEvent::Readable as u32 | WatchEvent::Writable as u32
|
||||
}
|
||||
|
@ -507,20 +511,24 @@ fn fd_event_source_implementation() -> FdEventSourceImpl<Rc<LogindSessionImpl>>
|
|||
_ => return,
|
||||
},
|
||||
);
|
||||
if let Err(err) = session.handle_signals(evlh, items) {
|
||||
error!(session.logger, "Error handling dbus signals: {}", err);
|
||||
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);
|
||||
}
|
||||
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 = session.conn.borrow();
|
||||
let conn = self.internal.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);
|
||||
if let Err(err) = self.internal.handle_signals(items) {
|
||||
error!(self.internal.logger, "Error handling dbus signals: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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| {
|
||||
for &mut (_, ref device) in devices.borrow_mut().values_mut() {
|
||||
info!(self.logger, "changed successful");
|
||||
device.observer().pause(evlh, devnum);
|
||||
})
|
||||
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| {
|
||||
for &mut (_, ref device) in devices.borrow_mut().values_mut() {
|
||||
info!(self.logger, "changed successful");
|
||||
device.observer().activate(evlh, devnum);
|
||||
})
|
||||
device.borrow_mut().observer().activate(devnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,41 +218,44 @@ 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>>();
|
||||
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!(udev.logger, "Device Added");
|
||||
info!(self.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(
|
||||
let logger = self.logger.clone();
|
||||
match self.session.open(
|
||||
path,
|
||||
fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY
|
||||
| fcntl::O_NONBLOCK,
|
||||
fcntl::OFlag::O_RDWR | fcntl::OFlag::O_CLOEXEC
|
||||
| fcntl::OFlag::O_NOCTTY
|
||||
| fcntl::OFlag::O_NONBLOCK,
|
||||
) {
|
||||
Ok(fd) => SessionFdDrmDevice(fd),
|
||||
Err(err) => {
|
||||
|
@ -258,12 +269,12 @@ where
|
|||
}
|
||||
}
|
||||
},
|
||||
udev.logger.clone(),
|
||||
self.logger.clone(),
|
||||
) {
|
||||
Ok(dev) => dev,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
udev.logger,
|
||||
self.logger,
|
||||
"Failed to initialize device {:?}. Error: {}. Skipping",
|
||||
path,
|
||||
err
|
||||
|
@ -273,29 +284,34 @@ where
|
|||
}
|
||||
};
|
||||
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) {
|
||||
match self.handler.device_added(&mut device) {
|
||||
Some(drm_handler) => {
|
||||
match drm_device_bind(&self.token, device, drm_handler) {
|
||||
Ok(fd_event_source) => {
|
||||
udev.devices.borrow_mut().insert(devnum, fd_event_source);
|
||||
self.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);
|
||||
drop(device);
|
||||
if let Err(err) = udev.session.close(fd) {
|
||||
warn!(
|
||||
udev.logger,
|
||||
self.logger,
|
||||
"Failed to bind device. Error: {:?}.", err
|
||||
);
|
||||
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
|
||||
);
|
||||
};
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
None => {
|
||||
udev.handler.device_removed(evlh, &mut device);
|
||||
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,
|
||||
self.logger,
|
||||
"Failed to close unused device. Error: {:?}", err
|
||||
);
|
||||
}
|
||||
|
@ -305,16 +321,21 @@ where
|
|||
}
|
||||
// Device removed
|
||||
EventType::Remove => {
|
||||
info!(udev.logger, "Device Remove");
|
||||
info!(self.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);
|
||||
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) = udev.session.close(fd) {
|
||||
if let Err(err) = self.session.close(fd) {
|
||||
warn!(
|
||||
udev.logger,
|
||||
self.logger,
|
||||
"Failed to close device {:?}. Error: {:?}. Ignoring",
|
||||
event.sysname(),
|
||||
err
|
||||
|
@ -325,28 +346,25 @@ where
|
|||
}
|
||||
// New connector
|
||||
EventType::Change => {
|
||||
info!(udev.logger, "Device Changed");
|
||||
info!(self.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);
|
||||
})
|
||||
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!(udev.logger, "changed, but device not tracked by backend");
|
||||
info!(self.logger, "changed, but device not tracked by backend");
|
||||
};
|
||||
} else {
|
||||
info!(udev.logger, "changed, but no devnum");
|
||||
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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -1,102 +1,87 @@
|
|||
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>),
|
||||
);
|
||||
},
|
||||
create_region: |evlh, _, _, _, region| {
|
||||
unsafe { RegionData::init(®ion) };
|
||||
evlh.register(®ion, region_implementation(), (), Some(destroy_region));
|
||||
},
|
||||
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);
|
||||
}
|
||||
},
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))))
|
||||
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))))
|
||||
});
|
||||
},
|
||||
damage: |_, _, _, surface, x, y, width, height| unsafe {
|
||||
SurfaceData::<U, R>::with_data(surface, |d| {
|
||||
wl_surface::Request::Damage {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => unsafe {
|
||||
SurfaceData::<U, R>::with_data(&surface, |d| {
|
||||
d.damage = Damage::Surface(Rectangle {
|
||||
x,
|
||||
y,
|
||||
|
@ -105,38 +90,43 @@ pub(crate) fn surface_implementation<U: 'static, R: 'static, ID: 'static>(
|
|||
})
|
||||
});
|
||||
},
|
||||
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(),
|
||||
)
|
||||
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);
|
||||
},
|
||||
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);
|
||||
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);
|
||||
},
|
||||
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);
|
||||
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);
|
||||
},
|
||||
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())
|
||||
wl_surface::Request::SetBufferScale { scale } => unsafe {
|
||||
SurfaceData::<U, R>::with_data(&surface, |d| d.buffer_scale = scale);
|
||||
},
|
||||
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| {
|
||||
wl_surface::Request::DamageBuffer {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => unsafe {
|
||||
SurfaceData::<U, R>::with_data(&surface, |d| {
|
||||
d.damage = Damage::Buffer(Rectangle {
|
||||
x,
|
||||
y,
|
||||
|
@ -145,24 +135,54 @@ pub(crate) fn surface_implementation<U: 'static, R: 'static, ID: 'static>(
|
|||
})
|
||||
});
|
||||
},
|
||||
destroy: |_, _, _, _| {},
|
||||
wl_surface::Request::Destroy => {
|
||||
// All is already handled by our destructor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy_surface<U: 'static, R: 'static>(surface: &wl_surface::WlSurface) {
|
||||
unsafe { SurfaceData::<U, R>::cleanup(surface) }
|
||||
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);
|
||||
}
|
||||
surface
|
||||
}
|
||||
|
||||
/*
|
||||
* wl_region
|
||||
*/
|
||||
|
||||
pub(crate) fn region_implementation() -> wl_region::Implementation<()> {
|
||||
wl_region::Implementation {
|
||||
add: |_, _, _, region, x, y, width, height| {
|
||||
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 {
|
||||
RegionData::add_rectangle(
|
||||
region,
|
||||
match request {
|
||||
wl_region::Request::Add {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => RegionData::add_rectangle(
|
||||
®ion,
|
||||
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(
|
||||
®ion,
|
||||
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(®ion) }),
|
||||
token,
|
||||
);
|
||||
unsafe {
|
||||
RegionData::init(®ion);
|
||||
}
|
||||
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) } {
|
||||
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;
|
||||
}
|
||||
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>),
|
||||
);
|
||||
},
|
||||
destroy: |_, _, _, _| {},
|
||||
let subsurface = implement_subsurface::<U, R>(id, &token);
|
||||
subsurface.set_user_data(Box::into_raw(Box::new(surface.clone())) as *mut ());
|
||||
}
|
||||
wl_subcompositor::Request::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.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(),
|
||||
);
|
||||
)
|
||||
}
|
||||
},
|
||||
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) {
|
||||
}
|
||||
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(),
|
||||
);
|
||||
)
|
||||
}
|
||||
},
|
||||
set_sync: |_, _, _, subsurface| unsafe {
|
||||
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| {
|
||||
}
|
||||
wl_subsurface::Request::SetSync => {
|
||||
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: |_, _, _, _| {},
|
||||
})
|
||||
}
|
||||
wl_subsurface::Request::SetDesync => {
|
||||
with_subsurface_attributes::<U, R, _>(&subsurface, |attrs| {
|
||||
attrs.sync = false;
|
||||
})
|
||||
}
|
||||
wl_subsurface::Request::Destroy => {
|
||||
// Our destructor already handles it
|
||||
}
|
||||
}
|
||||
},
|
||||
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 {
|
||||
let surface = Box::from_raw(ptr as *mut Resource<wl_surface::WlSurface>);
|
||||
if surface.is_alive() {
|
||||
SurfaceData::<U, R>::unset_parent(&surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,20 +43,17 @@
|
|||
//! 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
|
||||
//! &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
|
||||
//! );
|
||||
//!
|
||||
|
@ -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>,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,9 +38,6 @@
|
|||
//! 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
|
||||
|
@ -50,20 +48,23 @@
|
|||
//! // 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)
|
||||
pub fn owns(&self, output: &Resource<WlOutput>) -> bool {
|
||||
self.inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.instances
|
||||
.retain(|o| !o.equals(output));
|
||||
},
|
||||
.iter()
|
||||
.any(|o| o.equals(output))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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(|kbd| kbd.status() != Liveness::Dead);
|
||||
.retain(|k| !k.equals(&keyboard))
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
new_keyboard.implement(
|
||||
|request, _keyboard| {
|
||||
match request {
|
||||
Request::Release => {
|
||||
// Our destructors already handle it
|
||||
}
|
||||
}
|
||||
},
|
||||
destructor,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
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 ¯\_(ツ)_/¯
|
||||
}
|
||||
},
|
||||
get_keyboard: |evlh, token, _, _, keyboard| {
|
||||
evlh.register(&keyboard, keyboard_implementation(), (), None);
|
||||
if let Some(ref kbd_handle) = evlh.state().get(token).keyboard {
|
||||
}
|
||||
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, should error but cant
|
||||
// same as pointer, should error but cannot
|
||||
}
|
||||
},
|
||||
get_touch: |_evlh, _token, _, _, _touch| {
|
||||
}
|
||||
wl_seat::Request::GetTouch { id: _ } => {
|
||||
// TODO
|
||||
}
|
||||
wl_seat::Request::Release => {
|
||||
// Our destructors already handle it
|
||||
}
|
||||
}
|
||||
},
|
||||
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));
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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(<oken2, 1, move |_version, shell| {
|
||||
self::wl_handlers::implement_shell(
|
||||
shell,
|
||||
ltoken.clone(),
|
||||
ctoken.clone(),
|
||||
implementation.clone(),
|
||||
state2.clone(),
|
||||
);
|
||||
});
|
||||
|
||||
(state, global)
|
||||
}
|
|
@ -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(_, _)>,
|
||||
<oken2,
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
}),
|
||||
<oken,
|
||||
);
|
||||
shell_surface.set_user_data(Box::into_raw(Box::new(ShellSurfaceUserData { surface, state })) as *mut ());
|
||||
shell_surface
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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?!");
|
||||
},
|
||||
}
|
||||
}
|
|
@ -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(<oken, 1, move |_version, shell| {
|
||||
self::xdg_handlers::implement_wm_base(shell, &shell_impl);
|
||||
});
|
||||
|
||||
let zxdgv6_shell_global = display.create_global(<oken, 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),
|
||||
},
|
||||
}
|
|
@ -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());
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
},
|
||||
}
|
||||
}
|
|
@ -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,26 +165,20 @@ 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| {
|
||||
match request {
|
||||
Request::CreatePool { id: pool, fd, size } => {
|
||||
if size <= 0 {
|
||||
shm.post_error(
|
||||
wl_shm::Error::InvalidFd as u32,
|
||||
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()) {
|
||||
let mmap_pool = match Pool::new(fd, size as usize, self.log.clone()) {
|
||||
Ok(p) => p,
|
||||
Err(()) => {
|
||||
shm.post_error(
|
||||
|
@ -181,20 +189,17 @@ fn shm_implementation() -> wl_shm::Implementation<ShmGlobalData> {
|
|||
}
|
||||
};
|
||||
let arc_pool = Box::new(Arc::new(mmap_pool));
|
||||
evlh.register(
|
||||
&pool,
|
||||
shm_pool_implementation(),
|
||||
data.clone(),
|
||||
Some(destroy_shm_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
|
||||
|
@ -217,14 +222,28 @@ 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());
|
||||
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 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 {
|
||||
|
@ -235,12 +254,16 @@ fn shm_pool_implementation() -> wl_shm_pool::Implementation<ShmGlobalData> {
|
|||
format: format,
|
||||
},
|
||||
}));
|
||||
evlh.register(&buffer, buffer_implementation(), (), Some(destroy_buffer));
|
||||
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 ());
|
||||
},
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue