commit
f5ce5caa97
|
@ -30,15 +30,15 @@ branches:
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH
|
- export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH
|
||||||
- which rustfmt || cargo install rustfmt
|
- pip install 'travis-cargo<0.2' --user
|
||||||
|
- which rustfmt || travis-cargo --only nightly install rustfmt-nightly
|
||||||
- which cargo-install-update || cargo install cargo-update
|
- which cargo-install-update || cargo install cargo-update
|
||||||
- cargo install-update -a
|
- cargo install-update -a
|
||||||
- pip install 'travis-cargo<0.2' --user
|
|
||||||
- mkdir $(pwd)/socket
|
- mkdir $(pwd)/socket
|
||||||
- export XDG_RUNTIME_DIR="$(pwd)/socket"
|
- export XDG_RUNTIME_DIR="$(pwd)/socket"
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cargo fmt -- --write-mode=diff
|
- travis-cargo --only nightly fmt -- -- --write-mode=diff
|
||||||
- travis-cargo --skip nightly build
|
- travis-cargo --skip nightly build
|
||||||
- travis-cargo --only nightly build -- --features "clippy"
|
- travis-cargo --only nightly build -- --features "clippy"
|
||||||
- travis-cargo --only stable doc -- --no-deps
|
- travis-cargo --only stable doc -- --no-deps
|
||||||
|
|
|
@ -18,6 +18,7 @@ glium = { version = "0.16.0", optional = true, default-features = false }
|
||||||
input = { version = "0.2.0", optional = true }
|
input = { version = "0.2.0", optional = true }
|
||||||
clippy = { version = "*", optional = true }
|
clippy = { version = "*", optional = true }
|
||||||
rental = "0.4.11"
|
rental = "0.4.11"
|
||||||
|
wayland-protocols = { version = "0.9.9", features = ["unstable_protocols", "server"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gl_generator = "0.5"
|
gl_generator = "0.5"
|
||||||
|
@ -25,6 +26,7 @@ gl_generator = "0.5"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
slog-term = "2.0"
|
slog-term = "2.0"
|
||||||
slog-async = "2.0"
|
slog-async = "2.0"
|
||||||
|
rand = "0.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["backend_winit", "backend_libinput", "renderer_glium"]
|
default = ["backend_winit", "backend_libinput", "renderer_glium"]
|
||||||
|
|
|
@ -19,7 +19,6 @@ pub struct GliumDrawer<'a, F: 'a> {
|
||||||
|
|
||||||
impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
||||||
pub fn new(display: &'a F) -> GliumDrawer<'a, F> {
|
pub fn new(display: &'a F) -> GliumDrawer<'a, F> {
|
||||||
|
|
||||||
// building the vertex buffer, which contains all the vertices that we will draw
|
// building the vertex buffer, which contains all the vertices that we will draw
|
||||||
let vertex_buffer = glium::VertexBuffer::new(
|
let vertex_buffer = glium::VertexBuffer::new(
|
||||||
display,
|
display,
|
||||||
|
@ -87,7 +86,6 @@ impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
||||||
|
|
||||||
pub fn draw(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32),
|
pub fn draw(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32),
|
||||||
surface_location: (i32, i32), screen_size: (u32, u32)) {
|
surface_location: (i32, i32), screen_size: (u32, u32)) {
|
||||||
|
|
||||||
let image = glium::texture::RawImage2d {
|
let image = glium::texture::RawImage2d {
|
||||||
data: contents.into(),
|
data: contents.into(),
|
||||||
width: surface_dimensions.0,
|
width: surface_dimensions.0,
|
||||||
|
@ -102,8 +100,7 @@ impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
||||||
let x = 2.0 * (surface_location.0 as f32) / (screen_size.0 as f32) - 1.0;
|
let x = 2.0 * (surface_location.0 as f32) / (screen_size.0 as f32) - 1.0;
|
||||||
let y = 1.0 - 2.0 * (surface_location.1 as f32) / (screen_size.1 as f32);
|
let y = 1.0 - 2.0 * (surface_location.1 as f32) / (screen_size.1 as f32);
|
||||||
|
|
||||||
let uniforms =
|
let uniforms = uniform! {
|
||||||
uniform! {
|
|
||||||
matrix: [
|
matrix: [
|
||||||
[xscale, 0.0 , 0.0, 0.0],
|
[xscale, 0.0 , 0.0, 0.0],
|
||||||
[ 0.0 , yscale , 0.0, 0.0],
|
[ 0.0 , yscale , 0.0, 0.0],
|
||||||
|
@ -122,6 +119,5 @@ impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
mod shell;
|
|
||||||
mod glium;
|
mod glium;
|
||||||
|
|
||||||
pub use self::glium::GliumDrawer;
|
pub use self::glium::GliumDrawer;
|
||||||
pub use self::shell::{ShellSurfaceRole, WlShellStubHandler};
|
|
||||||
|
|
|
@ -1,104 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
use smithay::compositor::{CompositorToken, Handler as CompositorHandler};
|
|
||||||
use smithay::compositor::roles::{Role, RoleType};
|
|
||||||
use wayland_server::{Client, EventLoopHandle, GlobalHandler, Init, Resource};
|
|
||||||
use wayland_server::protocol::{wl_shell, wl_shell_surface, wl_surface};
|
|
||||||
|
|
||||||
/// A very basic handler for wl_shell
|
|
||||||
///
|
|
||||||
/// All it does is track which wl_shell_surface exist and which do not,
|
|
||||||
/// as well as the roles associated to them.
|
|
||||||
///
|
|
||||||
/// That's it.
|
|
||||||
pub struct WlShellStubHandler<U, R, H> {
|
|
||||||
my_id: Option<usize>,
|
|
||||||
token: CompositorToken<U, R, H>,
|
|
||||||
surfaces: Vec<(wl_shell_surface::WlShellSurface, wl_surface::WlSurface)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ShellSurfaceRole;
|
|
||||||
|
|
||||||
impl<U, R, H> WlShellStubHandler<U, R, H> {
|
|
||||||
pub fn new(compositor_token: CompositorToken<U, R, H>) -> WlShellStubHandler<U, R, H> {
|
|
||||||
WlShellStubHandler {
|
|
||||||
my_id: None,
|
|
||||||
token: compositor_token,
|
|
||||||
surfaces: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn surfaces(&self) -> &[(wl_shell_surface::WlShellSurface, wl_surface::WlSurface)] {
|
|
||||||
&self.surfaces
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<U, R, H> Init for WlShellStubHandler<U, R, H> {
|
|
||||||
fn init(&mut self, evqh: &mut EventLoopHandle, index: usize) {
|
|
||||||
self.my_id = Some(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl<U, R, H> GlobalHandler<wl_shell::WlShell> for WlShellStubHandler<U, R, H>
|
|
||||||
where
|
|
||||||
U: Send + 'static,
|
|
||||||
R: RoleType
|
|
||||||
+ Role<ShellSurfaceRole>
|
|
||||||
+ Send
|
|
||||||
+ 'static,
|
|
||||||
H: CompositorHandler<U, R>
|
|
||||||
+ Send
|
|
||||||
+ 'static,
|
|
||||||
{
|
|
||||||
fn bind(&mut self, evqh: &mut EventLoopHandle, client: &Client, global: wl_shell::WlShell) {
|
|
||||||
evqh.register::<_, Self>(
|
|
||||||
&global,
|
|
||||||
self.my_id.expect(
|
|
||||||
"WlShellStubHandler was not properly initialized.",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<U, R, H> wl_shell::Handler for WlShellStubHandler<U, R, H>
|
|
||||||
where
|
|
||||||
U: Send + 'static,
|
|
||||||
R: RoleType
|
|
||||||
+ Role<ShellSurfaceRole>
|
|
||||||
+ Send
|
|
||||||
+ 'static,
|
|
||||||
H: CompositorHandler<U, R> + Send + 'static,
|
|
||||||
{
|
|
||||||
fn get_shell_surface(&mut self, evqh: &mut EventLoopHandle, client: &Client,
|
|
||||||
resource: &wl_shell::WlShell, id: wl_shell_surface::WlShellSurface,
|
|
||||||
surface: &wl_surface::WlSurface) {
|
|
||||||
let surface = surface.clone().expect(
|
|
||||||
"WlShellStubHandler can only manage surfaces managed by Smithay's CompositorHandler.",
|
|
||||||
);
|
|
||||||
if self.token.give_role::<ShellSurfaceRole>(&surface).is_err() {
|
|
||||||
// This surface already has a role, and thus cannot be given one!
|
|
||||||
resource.post_error(
|
|
||||||
wl_shell::Error::Role as u32,
|
|
||||||
"Surface already has a role.".into(),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
evqh.register::<_, Self>(&id, self.my_id.unwrap());
|
|
||||||
self.surfaces.push((id, surface))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server_declare_handler!(WlShellStubHandler<U: [Send], R: [RoleType, Role<ShellSurfaceRole>, Send], H: [CompositorHandler<U, R>, Send]>, wl_shell::Handler, wl_shell::WlShell);
|
|
||||||
|
|
||||||
impl<U, R, H> wl_shell_surface::Handler for WlShellStubHandler<U, R, H>
|
|
||||||
where
|
|
||||||
U: Send + 'static,
|
|
||||||
H: CompositorHandler<U, R>
|
|
||||||
+ Send
|
|
||||||
+ 'static,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
server_declare_handler!(WlShellStubHandler<U: [Send], R: [Send], H: [CompositorHandler<U, R>, Send]>, wl_shell_surface::Handler, wl_shell_surface::WlShellSurface);
|
|
|
@ -1,20 +1,20 @@
|
||||||
#[macro_use(server_declare_handler)]
|
|
||||||
extern crate wayland_server;
|
|
||||||
#[macro_use(define_roles)]
|
|
||||||
extern crate smithay;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate glium;
|
extern crate glium;
|
||||||
|
extern crate rand;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate slog;
|
extern crate slog;
|
||||||
extern crate slog_async;
|
extern crate slog_async;
|
||||||
extern crate slog_term;
|
extern crate slog_term;
|
||||||
|
#[macro_use(define_roles)]
|
||||||
|
extern crate smithay;
|
||||||
|
extern crate wayland_protocols;
|
||||||
|
extern crate wayland_server;
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
use glium::Surface;
|
use glium::Surface;
|
||||||
|
|
||||||
use helpers::{GliumDrawer, ShellSurfaceRole, WlShellStubHandler};
|
use helpers::GliumDrawer;
|
||||||
use slog::{Drain, Logger};
|
use slog::{Drain, Logger};
|
||||||
|
|
||||||
use smithay::backend::graphics::glium::IntoGlium;
|
use smithay::backend::graphics::glium::IntoGlium;
|
||||||
|
@ -22,10 +22,15 @@ use smithay::backend::input::InputBackend;
|
||||||
use smithay::backend::winit;
|
use smithay::backend::winit;
|
||||||
use smithay::compositor::{self, CompositorHandler, CompositorToken, SubsurfaceRole, TraversalAction};
|
use smithay::compositor::{self, CompositorHandler, CompositorToken, SubsurfaceRole, TraversalAction};
|
||||||
use smithay::compositor::roles::Role;
|
use smithay::compositor::roles::Role;
|
||||||
use smithay::shm::{BufferData, ShmGlobal, ShmToken};
|
use smithay::shell::{self, PopupConfigure, PopupSurface, ShellClient, ShellHandler, ShellSurfaceRole,
|
||||||
use wayland_server::{Client, EventLoopHandle, Liveness, Resource};
|
ToplevelConfigure, ToplevelSurface};
|
||||||
|
use smithay::shm::{ShmGlobal, ShmToken};
|
||||||
|
|
||||||
use wayland_server::protocol::{wl_compositor, wl_shell, wl_shm, wl_subcompositor, wl_surface};
|
use wayland_protocols::unstable::xdg_shell::server::{zxdg_shell_v6, zxdg_toplevel_v6};
|
||||||
|
|
||||||
|
use wayland_server::{Client, EventLoopHandle};
|
||||||
|
use wayland_server::protocol::{wl_compositor, wl_output, wl_seat, wl_shell, wl_shm, wl_subcompositor,
|
||||||
|
wl_surface};
|
||||||
|
|
||||||
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
|
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
|
||||||
|
|
||||||
|
@ -36,16 +41,19 @@ struct SurfaceHandler {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct SurfaceData {
|
struct SurfaceData {
|
||||||
buffer: Option<(Vec<u8>, (u32, u32))>,
|
buffer: Option<(Vec<u8>, (u32, u32))>,
|
||||||
|
location: Option<(i32, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl compositor::Handler<SurfaceData, Roles> for SurfaceHandler {
|
impl compositor::Handler<SurfaceData, Roles> for SurfaceHandler {
|
||||||
fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface,
|
fn commit(&mut self, _evlh: &mut EventLoopHandle, _client: &Client, surface: &wl_surface::WlSurface,
|
||||||
token: CompositorToken<SurfaceData, Roles, SurfaceHandler>) {
|
token: CompositorToken<SurfaceData, Roles, SurfaceHandler>) {
|
||||||
// we retrieve the contents of the associated buffer and copy it
|
// we retrieve the contents of the associated buffer and copy it
|
||||||
token.with_surface_data(surface, |attributes| {
|
token.with_surface_data(surface, |attributes| {
|
||||||
match attributes.buffer.take() {
|
match attributes.buffer.take() {
|
||||||
Some(Some((buffer, (x, y)))) => {
|
Some(Some((buffer, (_x, _y)))) => {
|
||||||
self.shm_token.with_buffer_contents(&buffer, |slice, data| {
|
// we ignore hotspot coordinates in this simple example
|
||||||
|
self.shm_token
|
||||||
|
.with_buffer_contents(&buffer, |slice, data| {
|
||||||
let offset = data.offset as usize;
|
let offset = data.offset as usize;
|
||||||
let stride = data.stride as usize;
|
let stride = data.stride as usize;
|
||||||
let width = data.width as usize;
|
let width = data.width as usize;
|
||||||
|
@ -58,8 +66,8 @@ impl compositor::Handler<SurfaceData, Roles> for SurfaceHandler {
|
||||||
}
|
}
|
||||||
attributes.user_data.buffer =
|
attributes.user_data.buffer =
|
||||||
Some((new_vec, (data.width as u32, data.height as u32)));
|
Some((new_vec, (data.width as u32, data.height as u32)));
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
Some(None) => {
|
Some(None) => {
|
||||||
// erase the contents
|
// erase the contents
|
||||||
|
@ -71,7 +79,79 @@ impl compositor::Handler<SurfaceData, Roles> for SurfaceHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ShellSurfaceHandler {
|
||||||
|
token: CompositorToken<SurfaceData, Roles, SurfaceHandler>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShellSurfaceHandler {
|
||||||
|
fn new(token: CompositorToken<SurfaceData, Roles, SurfaceHandler>) -> ShellSurfaceHandler {
|
||||||
|
ShellSurfaceHandler { token }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl shell::Handler<SurfaceData, Roles, SurfaceHandler, ()> for ShellSurfaceHandler {
|
||||||
|
fn new_client(&mut self, _evlh: &mut EventLoopHandle, _client: ShellClient<()>) {}
|
||||||
|
fn client_pong(&mut self, _evlh: &mut EventLoopHandle, _client: ShellClient<()>) {}
|
||||||
|
fn new_toplevel(&mut self, _evlh: &mut EventLoopHandle,
|
||||||
|
surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>)
|
||||||
|
-> ToplevelConfigure {
|
||||||
|
let wl_surface = surface.get_surface().unwrap();
|
||||||
|
self.token.with_surface_data(wl_surface, |data| {
|
||||||
|
// 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);
|
||||||
|
data.user_data.location = Some((x, y))
|
||||||
|
});
|
||||||
|
ToplevelConfigure {
|
||||||
|
size: None,
|
||||||
|
states: vec![],
|
||||||
|
serial: 42,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn new_popup(&mut self, _evlh: &mut EventLoopHandle,
|
||||||
|
_surface: PopupSurface<SurfaceData, Roles, SurfaceHandler, ()>)
|
||||||
|
-> PopupConfigure {
|
||||||
|
PopupConfigure {
|
||||||
|
size: (10, 10),
|
||||||
|
position: (10, 10),
|
||||||
|
serial: 42,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn move_(&mut self, _evlh: &mut EventLoopHandle,
|
||||||
|
_surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>, _seat: &wl_seat::WlSeat,
|
||||||
|
_serial: u32) {
|
||||||
|
}
|
||||||
|
fn resize(&mut self, _evlh: &mut EventLoopHandle,
|
||||||
|
_surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>, _seat: &wl_seat::WlSeat,
|
||||||
|
_serial: u32, _edges: zxdg_toplevel_v6::ResizeEdge) {
|
||||||
|
}
|
||||||
|
fn grab(&mut self, _evlh: &mut EventLoopHandle,
|
||||||
|
_surface: PopupSurface<SurfaceData, Roles, SurfaceHandler, ()>, _seat: &wl_seat::WlSeat,
|
||||||
|
_serial: u32) {
|
||||||
|
}
|
||||||
|
fn change_display_state(&mut self, _evlh: &mut EventLoopHandle,
|
||||||
|
_surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>,
|
||||||
|
_maximized: Option<bool>, _minimized: Option<bool>, _fullscreen: Option<bool>,
|
||||||
|
_output: Option<&wl_output::WlOutput>)
|
||||||
|
-> ToplevelConfigure {
|
||||||
|
ToplevelConfigure {
|
||||||
|
size: None,
|
||||||
|
states: vec![],
|
||||||
|
serial: 42,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn show_window_menu(&mut self, _evlh: &mut EventLoopHandle,
|
||||||
|
_surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>,
|
||||||
|
_seat: &wl_seat::WlSeat, _serial: u32, _x: i32, _y: i32) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type MyCompositorHandler = CompositorHandler<SurfaceData, Roles, SurfaceHandler>;
|
type MyCompositorHandler = CompositorHandler<SurfaceData, Roles, SurfaceHandler>;
|
||||||
|
type MyShellHandler = ShellHandler<SurfaceData, Roles, SurfaceHandler, ShellSurfaceHandler, ()>;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// A logger facility, here we use the terminal for this example
|
// A logger facility, here we use the terminal for this example
|
||||||
|
@ -104,7 +184,9 @@ fn main() {
|
||||||
* Initialize the compositor global
|
* Initialize the compositor global
|
||||||
*/
|
*/
|
||||||
let compositor_handler_id = event_loop.add_handler_with_init(MyCompositorHandler::new(
|
let compositor_handler_id = event_loop.add_handler_with_init(MyCompositorHandler::new(
|
||||||
SurfaceHandler { shm_token: shm_token.clone() },
|
SurfaceHandler {
|
||||||
|
shm_token: shm_token.clone(),
|
||||||
|
},
|
||||||
log.clone(),
|
log.clone(),
|
||||||
));
|
));
|
||||||
// register it to handle wl_compositor and wl_subcompositor
|
// register it to handle wl_compositor and wl_subcompositor
|
||||||
|
@ -120,14 +202,15 @@ fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the shell stub global
|
* Initialize the shell global
|
||||||
*/
|
*/
|
||||||
let shell_handler_id =
|
let shell_handler_id = event_loop.add_handler_with_init(MyShellHandler::new(
|
||||||
event_loop.add_handler_with_init(WlShellStubHandler::new(compositor_token.clone()));
|
ShellSurfaceHandler::new(compositor_token),
|
||||||
event_loop.register_global::<wl_shell::WlShell, WlShellStubHandler<SurfaceData, Roles, SurfaceHandler>>(
|
compositor_token,
|
||||||
shell_handler_id,
|
log.clone(),
|
||||||
1,
|
));
|
||||||
);
|
event_loop.register_global::<wl_shell::WlShell, MyShellHandler>(shell_handler_id, 1);
|
||||||
|
event_loop.register_global::<zxdg_shell_v6::ZxdgShellV6, MyShellHandler>(shell_handler_id, 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize glium
|
* Initialize glium
|
||||||
|
@ -151,19 +234,19 @@ fn main() {
|
||||||
{
|
{
|
||||||
let screen_dimensions = context.get_framebuffer_dimensions();
|
let screen_dimensions = context.get_framebuffer_dimensions();
|
||||||
let state = event_loop.state();
|
let state = event_loop.state();
|
||||||
for &(_, ref surface) in
|
for toplevel_surface in state
|
||||||
state
|
.get_handler::<MyShellHandler>(shell_handler_id)
|
||||||
.get_handler::<WlShellStubHandler<SurfaceData, Roles, SurfaceHandler>>(shell_handler_id)
|
.toplevel_surfaces()
|
||||||
.surfaces()
|
|
||||||
{
|
{
|
||||||
if surface.status() != Liveness::Alive {
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// this surface is a root of a subsurface tree that needs to be drawn
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
compositor_token.with_surface_tree(surface, (100, 100), |surface,
|
let initial_place = compositor_token
|
||||||
attributes,
|
.with_surface_data(wl_surface, |data| data.user_data.location.unwrap_or((0, 0)));
|
||||||
role,
|
compositor_token
|
||||||
&(mut x, mut y)| {
|
.with_surface_tree(
|
||||||
|
wl_surface,
|
||||||
|
initial_place,
|
||||||
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
||||||
// there is actually something to draw !
|
// there is actually something to draw !
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
|
@ -176,7 +259,10 @@ fn main() {
|
||||||
// we are not display, so our children are neither
|
// we are not display, so our children are neither
|
||||||
TraversalAction::SkipChildren
|
TraversalAction::SkipChildren
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
|
|
|
@ -244,31 +244,36 @@ impl EGLContext {
|
||||||
|
|
||||||
let display = match native {
|
let display = match native {
|
||||||
NativeDisplay::X11(display)
|
NativeDisplay::X11(display)
|
||||||
if has_dp_extension("EGL_KHR_platform_x11") && egl.GetPlatformDisplay.is_loaded() => {
|
if has_dp_extension("EGL_KHR_platform_x11") && egl.GetPlatformDisplay.is_loaded() =>
|
||||||
|
{
|
||||||
trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
|
trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
|
||||||
egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null())
|
egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::X11(display)
|
NativeDisplay::X11(display)
|
||||||
if has_dp_extension("EGL_EXT_platform_x11") && egl.GetPlatformDisplayEXT.is_loaded() => {
|
if has_dp_extension("EGL_EXT_platform_x11") && egl.GetPlatformDisplayEXT.is_loaded() =>
|
||||||
|
{
|
||||||
trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
|
trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
|
||||||
egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null())
|
egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::Gbm(display)
|
NativeDisplay::Gbm(display)
|
||||||
if has_dp_extension("EGL_KHR_platform_gbm") && egl.GetPlatformDisplay.is_loaded() => {
|
if has_dp_extension("EGL_KHR_platform_gbm") && egl.GetPlatformDisplay.is_loaded() =>
|
||||||
|
{
|
||||||
trace!(log, "EGL Display Initialization via EGL_KHR_platform_gbm");
|
trace!(log, "EGL Display Initialization via EGL_KHR_platform_gbm");
|
||||||
egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
|
egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::Gbm(display)
|
NativeDisplay::Gbm(display)
|
||||||
if has_dp_extension("EGL_MESA_platform_gbm") && egl.GetPlatformDisplayEXT.is_loaded() => {
|
if has_dp_extension("EGL_MESA_platform_gbm") && egl.GetPlatformDisplayEXT.is_loaded() =>
|
||||||
|
{
|
||||||
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
|
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
|
||||||
egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
|
egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::Wayland(display)
|
NativeDisplay::Wayland(display)
|
||||||
if has_dp_extension("EGL_KHR_platform_wayland") && egl.GetPlatformDisplay.is_loaded() => {
|
if has_dp_extension("EGL_KHR_platform_wayland") && egl.GetPlatformDisplay.is_loaded() =>
|
||||||
|
{
|
||||||
trace!(
|
trace!(
|
||||||
log,
|
log,
|
||||||
"EGL Display Initialization via EGL_KHR_platform_wayland"
|
"EGL Display Initialization via EGL_KHR_platform_wayland"
|
||||||
|
@ -281,7 +286,8 @@ impl EGLContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::Wayland(display)
|
NativeDisplay::Wayland(display)
|
||||||
if has_dp_extension("EGL_EXT_platform_wayland") && egl.GetPlatformDisplayEXT.is_loaded() => {
|
if has_dp_extension("EGL_EXT_platform_wayland") && egl.GetPlatformDisplayEXT.is_loaded() =>
|
||||||
|
{
|
||||||
trace!(
|
trace!(
|
||||||
log,
|
log,
|
||||||
"EGL Display Initialization via EGL_EXT_platform_wayland"
|
"EGL Display Initialization via EGL_EXT_platform_wayland"
|
||||||
|
@ -293,9 +299,7 @@ impl EGLContext {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::X11(display) |
|
NativeDisplay::X11(display) | NativeDisplay::Gbm(display) | NativeDisplay::Wayland(display) => {
|
||||||
NativeDisplay::Gbm(display) |
|
|
||||||
NativeDisplay::Wayland(display) => {
|
|
||||||
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
||||||
egl.GetDisplay(display as *mut _)
|
egl.GetDisplay(display as *mut _)
|
||||||
}
|
}
|
||||||
|
@ -522,7 +526,6 @@ impl EGLContext {
|
||||||
|
|
||||||
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
|
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
|
||||||
context_attributes.push(0);
|
context_attributes.push(0);
|
||||||
|
|
||||||
} else if egl_version >= (1, 3) {
|
} else if egl_version >= (1, 3) {
|
||||||
trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
|
trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
|
||||||
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
|
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
|
||||||
|
@ -602,14 +605,12 @@ impl EGLContext {
|
||||||
let surface = match (native, self.backend_type) {
|
let surface = match (native, self.backend_type) {
|
||||||
(NativeSurface::X11(window), NativeType::X11) |
|
(NativeSurface::X11(window), NativeType::X11) |
|
||||||
(NativeSurface::Wayland(window), NativeType::Wayland) |
|
(NativeSurface::Wayland(window), NativeType::Wayland) |
|
||||||
(NativeSurface::Gbm(window), NativeType::Gbm) => {
|
(NativeSurface::Gbm(window), NativeType::Gbm) => self.egl.CreateWindowSurface(
|
||||||
self.egl.CreateWindowSurface(
|
|
||||||
self.display,
|
self.display,
|
||||||
self.config_id,
|
self.config_id,
|
||||||
window,
|
window,
|
||||||
self.surface_attributes.as_ptr(),
|
self.surface_attributes.as_ptr(),
|
||||||
)
|
),
|
||||||
}
|
|
||||||
_ => return Err(CreationError::NonMatchingSurfaceType),
|
_ => return Err(CreationError::NonMatchingSurfaceType),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -654,10 +655,9 @@ impl<'a> EGLSurface<'a> {
|
||||||
/// Swaps buffers at the end of a frame.
|
/// Swaps buffers at the end of a frame.
|
||||||
pub fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
|
pub fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
self.context.egl.SwapBuffers(
|
self.context
|
||||||
self.context.display as *const _,
|
.egl
|
||||||
self.surface as *const _,
|
.SwapBuffers(self.context.display as *const _, self.surface as *const _)
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
|
@ -705,10 +705,8 @@ impl Drop for EGLContext {
|
||||||
unsafe {
|
unsafe {
|
||||||
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
||||||
// is still the current one
|
// is still the current one
|
||||||
self.egl.DestroyContext(
|
self.egl
|
||||||
self.display as *const _,
|
.DestroyContext(self.display as *const _, self.context as *const _);
|
||||||
self.context as *const _,
|
|
||||||
);
|
|
||||||
self.egl.Terminate(self.display as *const _);
|
self.egl.Terminate(self.display as *const _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -717,10 +715,9 @@ impl Drop for EGLContext {
|
||||||
impl<'a> Drop for EGLSurface<'a> {
|
impl<'a> Drop for EGLSurface<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.context.egl.DestroySurface(
|
self.context
|
||||||
self.context.display as *const _,
|
.egl
|
||||||
self.surface as *const _,
|
.DestroySurface(self.context.display as *const _, self.surface as *const _);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,10 +87,10 @@ impl<'a> backend::PointerAxisEvent for PointerAxisEvent {
|
||||||
|
|
||||||
fn amount(&self) -> f64 {
|
fn amount(&self) -> f64 {
|
||||||
match self.source() {
|
match self.source() {
|
||||||
backend::AxisSource::Finger |
|
backend::AxisSource::Finger | backend::AxisSource::Continuous => self.event.axis_value(self.axis),
|
||||||
backend::AxisSource::Continuous => self.event.axis_value(self.axis),
|
backend::AxisSource::Wheel | backend::AxisSource::WheelTilt => {
|
||||||
backend::AxisSource::Wheel |
|
self.event.axis_value_discrete(self.axis).unwrap()
|
||||||
backend::AxisSource::WheelTilt => self.event.axis_value_discrete(self.axis).unwrap(),
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,9 +271,9 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_handler(&mut self) -> Option<&mut backend::InputHandler<Self>> {
|
fn get_handler(&mut self) -> Option<&mut backend::InputHandler<Self>> {
|
||||||
self.handler.as_mut().map(|handler| {
|
self.handler
|
||||||
handler as &mut backend::InputHandler<Self>
|
.as_mut()
|
||||||
})
|
.map(|handler| handler as &mut backend::InputHandler<Self>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_handler(&mut self) {
|
fn clear_handler(&mut self) {
|
||||||
|
@ -349,18 +349,15 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
// update capabilities, so they appear correctly on `on_seat_changed` and `on_seat_destroyed`.
|
// update capabilities, so they appear correctly on `on_seat_changed` and `on_seat_destroyed`.
|
||||||
if let Some(seat) = self.seats.get_mut(&device_seat) {
|
if let Some(seat) = self.seats.get_mut(&device_seat) {
|
||||||
let caps = seat.capabilities_mut();
|
let caps = seat.capabilities_mut();
|
||||||
caps.pointer =
|
caps.pointer = self.devices
|
||||||
self.devices
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| x.seat() == device_seat)
|
.filter(|x| x.seat() == device_seat)
|
||||||
.any(|x| x.has_capability(libinput::DeviceCapability::Pointer));
|
.any(|x| x.has_capability(libinput::DeviceCapability::Pointer));
|
||||||
caps.keyboard =
|
caps.keyboard = self.devices
|
||||||
self.devices
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| x.seat() == device_seat)
|
.filter(|x| x.seat() == device_seat)
|
||||||
.any(|x| x.has_capability(libinput::DeviceCapability::Keyboard));
|
.any(|x| x.has_capability(libinput::DeviceCapability::Keyboard));
|
||||||
caps.touch =
|
caps.touch = self.devices
|
||||||
self.devices
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| x.seat() == device_seat)
|
.filter(|x| x.seat() == device_seat)
|
||||||
.any(|x| x.has_capability(libinput::DeviceCapability::Touch));
|
.any(|x| x.has_capability(libinput::DeviceCapability::Touch));
|
||||||
|
@ -395,9 +392,9 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
use input::event::touch::*;
|
use input::event::touch::*;
|
||||||
if let Some(ref mut handler) = self.handler {
|
if let Some(ref mut handler) = self.handler {
|
||||||
let device_seat = touch_event.device().seat();
|
let device_seat = touch_event.device().seat();
|
||||||
let seat = &self.seats.get(&device_seat).expect(
|
let seat = &self.seats
|
||||||
"Recieved touch event of non existing Seat",
|
.get(&device_seat)
|
||||||
);
|
.expect("Recieved touch event of non existing Seat");
|
||||||
match touch_event {
|
match touch_event {
|
||||||
TouchEvent::Down(down_event) => {
|
TouchEvent::Down(down_event) => {
|
||||||
trace!(self.logger, "Calling on_touch_down with {:?}", down_event);
|
trace!(self.logger, "Calling on_touch_down with {:?}", down_event);
|
||||||
|
@ -433,25 +430,23 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
libinput::Event::Keyboard(keyboard_event) => {
|
libinput::Event::Keyboard(keyboard_event) => {
|
||||||
use input::event::keyboard::*;
|
use input::event::keyboard::*;
|
||||||
match keyboard_event {
|
match keyboard_event {
|
||||||
KeyboardEvent::Key(key_event) => {
|
KeyboardEvent::Key(key_event) => if let Some(ref mut handler) = self.handler {
|
||||||
if let Some(ref mut handler) = self.handler {
|
|
||||||
let device_seat = key_event.device().seat();
|
let device_seat = key_event.device().seat();
|
||||||
let seat = &self.seats.get(&device_seat).expect(
|
let seat = &self.seats
|
||||||
"Recieved key event of non existing Seat",
|
.get(&device_seat)
|
||||||
);
|
.expect("Recieved key event of non existing Seat");
|
||||||
trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event);
|
trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event);
|
||||||
handler.on_keyboard_key(seat, key_event);
|
handler.on_keyboard_key(seat, key_event);
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
libinput::Event::Pointer(pointer_event) => {
|
libinput::Event::Pointer(pointer_event) => {
|
||||||
use input::event::pointer::*;
|
use input::event::pointer::*;
|
||||||
if let Some(ref mut handler) = self.handler {
|
if let Some(ref mut handler) = self.handler {
|
||||||
let device_seat = pointer_event.device().seat();
|
let device_seat = pointer_event.device().seat();
|
||||||
let seat = &self.seats.get(&device_seat).expect(
|
let seat = &self.seats
|
||||||
"Recieved pointer event of non existing Seat",
|
.get(&device_seat)
|
||||||
);
|
.expect("Recieved pointer event of non existing Seat");
|
||||||
match pointer_event {
|
match pointer_event {
|
||||||
PointerEvent::Motion(motion_event) => {
|
PointerEvent::Motion(motion_event) => {
|
||||||
trace!(
|
trace!(
|
||||||
|
|
|
@ -214,9 +214,9 @@ impl EGLGraphicsBackend for WinitGraphicsBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
||||||
self.window.get_inner_size_pixels().expect(
|
self.window
|
||||||
"Window does not exist anymore",
|
.get_inner_size_pixels()
|
||||||
)
|
.expect("Window does not exist anymore")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_current(&self) -> bool {
|
fn is_current(&self) -> bool {
|
||||||
|
@ -310,14 +310,16 @@ impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent {
|
||||||
|
|
||||||
fn x_transformed(&self, width: u32) -> u32 {
|
fn x_transformed(&self, width: u32) -> u32 {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
(self.x * width as f64 / self.window.get_inner_size_points().unwrap_or((width, 0)).0 as f64) as u32,
|
(self.x * width as f64 / self.window.get_inner_size_points().unwrap_or((width, 0)).0 as f64) as
|
||||||
|
u32,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn y_transformed(&self, height: u32) -> u32 {
|
fn y_transformed(&self, height: u32) -> u32 {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
(self.y * height as f64 / self.window.get_inner_size_points().unwrap_or((0, height)).1 as f64) as u32,
|
(self.y * height as f64 / self.window.get_inner_size_points().unwrap_or((0, height)).1 as f64) as
|
||||||
|
u32,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -529,9 +531,9 @@ impl InputBackend for WinitInputBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_handler(&mut self) -> Option<&mut InputHandler<Self>> {
|
fn get_handler(&mut self) -> Option<&mut InputHandler<Self>> {
|
||||||
self.handler.as_mut().map(|handler| {
|
self.handler
|
||||||
handler as &mut InputHandler<Self>
|
.as_mut()
|
||||||
})
|
.map(|handler| handler as &mut InputHandler<Self>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_handler(&mut self) {
|
fn clear_handler(&mut self) {
|
||||||
|
@ -590,10 +592,15 @@ impl InputBackend for WinitInputBackend {
|
||||||
wl_egl_surface.resize(x as i32, y as i32, 0, 0);
|
wl_egl_surface.resize(x as i32, y as i32, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(WindowEvent::KeyboardInput {
|
(
|
||||||
input: KeyboardInput { scancode, state, .. }, ..
|
WindowEvent::KeyboardInput {
|
||||||
|
input: KeyboardInput {
|
||||||
|
scancode, state, ..
|
||||||
},
|
},
|
||||||
Some(handler)) => {
|
..
|
||||||
|
},
|
||||||
|
Some(handler),
|
||||||
|
) => {
|
||||||
match state {
|
match state {
|
||||||
ElementState::Pressed => *key_counter += 1,
|
ElementState::Pressed => *key_counter += 1,
|
||||||
ElementState::Released => {
|
ElementState::Released => {
|
||||||
|
@ -615,7 +622,12 @@ impl InputBackend for WinitInputBackend {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(WindowEvent::MouseMoved { position: (x, y), .. }, Some(handler)) => {
|
(
|
||||||
|
WindowEvent::MouseMoved {
|
||||||
|
position: (x, y), ..
|
||||||
|
},
|
||||||
|
Some(handler),
|
||||||
|
) => {
|
||||||
trace!(logger, "Calling on_pointer_move_absolute with {:?}", (x, y));
|
trace!(logger, "Calling on_pointer_move_absolute with {:?}", (x, y));
|
||||||
handler.on_pointer_move_absolute(
|
handler.on_pointer_move_absolute(
|
||||||
seat,
|
seat,
|
||||||
|
@ -627,10 +639,8 @@ impl InputBackend for WinitInputBackend {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(WindowEvent::MouseWheel { delta, .. }, Some(handler)) => {
|
(WindowEvent::MouseWheel { delta, .. }, Some(handler)) => match delta {
|
||||||
match delta {
|
MouseScrollDelta::LineDelta(x, y) | MouseScrollDelta::PixelDelta(x, y) => {
|
||||||
MouseScrollDelta::LineDelta(x, y) |
|
|
||||||
MouseScrollDelta::PixelDelta(x, y) => {
|
|
||||||
if x != 0.0 {
|
if x != 0.0 {
|
||||||
let event = WinitMouseWheelEvent {
|
let event = WinitMouseWheelEvent {
|
||||||
axis: Axis::Horizontal,
|
axis: Axis::Horizontal,
|
||||||
|
@ -658,8 +668,7 @@ impl InputBackend for WinitInputBackend {
|
||||||
handler.on_pointer_axis(seat, event);
|
handler.on_pointer_axis(seat, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
(WindowEvent::MouseInput { state, button, .. }, Some(handler)) => {
|
(WindowEvent::MouseInput { state, button, .. }, Some(handler)) => {
|
||||||
trace!(
|
trace!(
|
||||||
logger,
|
logger,
|
||||||
|
@ -675,13 +684,15 @@ impl InputBackend for WinitInputBackend {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(WindowEvent::Touch(Touch {
|
(
|
||||||
|
WindowEvent::Touch(Touch {
|
||||||
phase: TouchPhase::Started,
|
phase: TouchPhase::Started,
|
||||||
location: (x, y),
|
location: (x, y),
|
||||||
id,
|
id,
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
Some(handler)) => {
|
Some(handler),
|
||||||
|
) => {
|
||||||
trace!(logger, "Calling on_touch_down at {:?}", (x, y));
|
trace!(logger, "Calling on_touch_down at {:?}", (x, y));
|
||||||
handler.on_touch_down(
|
handler.on_touch_down(
|
||||||
seat,
|
seat,
|
||||||
|
@ -693,13 +704,15 @@ impl InputBackend for WinitInputBackend {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(WindowEvent::Touch(Touch {
|
(
|
||||||
|
WindowEvent::Touch(Touch {
|
||||||
phase: TouchPhase::Moved,
|
phase: TouchPhase::Moved,
|
||||||
location: (x, y),
|
location: (x, y),
|
||||||
id,
|
id,
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
Some(handler)) => {
|
Some(handler),
|
||||||
|
) => {
|
||||||
trace!(logger, "Calling on_touch_motion at {:?}", (x, y));
|
trace!(logger, "Calling on_touch_motion at {:?}", (x, y));
|
||||||
handler.on_touch_motion(
|
handler.on_touch_motion(
|
||||||
seat,
|
seat,
|
||||||
|
@ -711,13 +724,15 @@ impl InputBackend for WinitInputBackend {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(WindowEvent::Touch(Touch {
|
(
|
||||||
|
WindowEvent::Touch(Touch {
|
||||||
phase: TouchPhase::Ended,
|
phase: TouchPhase::Ended,
|
||||||
location: (x, y),
|
location: (x, y),
|
||||||
id,
|
id,
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
Some(handler)) => {
|
Some(handler),
|
||||||
|
) => {
|
||||||
trace!(logger, "Calling on_touch_motion at {:?}", (x, y));
|
trace!(logger, "Calling on_touch_motion at {:?}", (x, y));
|
||||||
handler.on_touch_motion(
|
handler.on_touch_motion(
|
||||||
seat,
|
seat,
|
||||||
|
@ -737,12 +752,14 @@ impl InputBackend for WinitInputBackend {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(WindowEvent::Touch(Touch {
|
(
|
||||||
|
WindowEvent::Touch(Touch {
|
||||||
phase: TouchPhase::Cancelled,
|
phase: TouchPhase::Cancelled,
|
||||||
id,
|
id,
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
Some(handler)) => {
|
Some(handler),
|
||||||
|
) => {
|
||||||
trace!(logger, "Calling on_touch_cancel");
|
trace!(logger, "Calling on_touch_cancel");
|
||||||
handler.on_touch_cancel(
|
handler.on_touch_cancel(
|
||||||
seat,
|
seat,
|
||||||
|
|
|
@ -5,15 +5,9 @@ use wayland_server::protocol::{wl_compositor, wl_subcompositor};
|
||||||
|
|
||||||
impl<U, R, H> GlobalHandler<wl_compositor::WlCompositor> for CompositorHandler<U, R, H>
|
impl<U, R, H> GlobalHandler<wl_compositor::WlCompositor> for CompositorHandler<U, R, H>
|
||||||
where
|
where
|
||||||
U: Default
|
U: Default + Send + 'static,
|
||||||
+ Send
|
R: Default + Send + 'static,
|
||||||
+ 'static,
|
H: UserHandler<U, R> + Send + 'static,
|
||||||
R: Default
|
|
||||||
+ Send
|
|
||||||
+ 'static,
|
|
||||||
H: UserHandler<U, R>
|
|
||||||
+ Send
|
|
||||||
+ 'static,
|
|
||||||
{
|
{
|
||||||
fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_compositor::WlCompositor) {
|
fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_compositor::WlCompositor) {
|
||||||
debug!(self.log, "New compositor global binded.");
|
debug!(self.log, "New compositor global binded.");
|
||||||
|
|
|
@ -193,10 +193,7 @@ impl<U, R> Destroy<wl_region::WlRegion> for CompositorDestructor<U, R> {
|
||||||
impl<U, R, H> wl_subcompositor::Handler for CompositorHandler<U, R, H>
|
impl<U, R, H> wl_subcompositor::Handler for CompositorHandler<U, R, H>
|
||||||
where
|
where
|
||||||
U: Send + 'static,
|
U: Send + 'static,
|
||||||
R: RoleType
|
R: RoleType + Role<SubsurfaceRole> + Send + 'static,
|
||||||
+ Role<SubsurfaceRole>
|
|
||||||
+ Send
|
|
||||||
+ 'static,
|
|
||||||
H: Send + 'static,
|
H: Send + 'static,
|
||||||
{
|
{
|
||||||
fn get_subsurface(&mut self, evqh: &mut EventLoopHandle, _: &Client,
|
fn get_subsurface(&mut self, evqh: &mut EventLoopHandle, _: &Client,
|
||||||
|
@ -210,9 +207,9 @@ where
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
id.set_user_data(Box::into_raw(
|
id.set_user_data(
|
||||||
Box::new(unsafe { surface.clone_unchecked() }),
|
Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _,
|
||||||
) as *mut _);
|
);
|
||||||
evqh.register_with_destructor::<_, CompositorHandler<U, R, H>, CompositorDestructor<U, R>>(
|
evqh.register_with_destructor::<_, CompositorHandler<U, R, H>, CompositorDestructor<U, R>>(
|
||||||
&id,
|
&id,
|
||||||
self.my_id,
|
self.my_id,
|
||||||
|
@ -259,7 +256,10 @@ where
|
||||||
let ptr = subsurface.get_user_data();
|
let ptr = subsurface.get_user_data();
|
||||||
let surface = &*(ptr as *mut wl_surface::WlSurface);
|
let surface = &*(ptr as *mut wl_surface::WlSurface);
|
||||||
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::After, sibling) {
|
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::After, sibling) {
|
||||||
subsurface.post_error(wl_subsurface::Error::BadSurface as u32, "Provided surface is not a sibling or parent.".into());
|
subsurface.post_error(
|
||||||
|
wl_subsurface::Error::BadSurface as u32,
|
||||||
|
"Provided surface is not a sibling or parent.".into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,10 @@ where
|
||||||
let ptr = subsurface.get_user_data();
|
let ptr = subsurface.get_user_data();
|
||||||
let surface = &*(ptr as *mut wl_surface::WlSurface);
|
let surface = &*(ptr as *mut wl_surface::WlSurface);
|
||||||
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::Before, sibling) {
|
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());
|
subsurface.post_error(
|
||||||
|
wl_subsurface::Error::BadSurface as u32,
|
||||||
|
"Provided surface is not a sibling or parent.".into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,7 +294,8 @@ where
|
||||||
server_declare_handler!(CompositorHandler<U: [], R: [RoleType, Role<SubsurfaceRole>], H: []>, wl_subsurface::Handler, wl_subsurface::WlSubsurface);
|
server_declare_handler!(CompositorHandler<U: [], R: [RoleType, Role<SubsurfaceRole>], H: []>, wl_subsurface::Handler, wl_subsurface::WlSubsurface);
|
||||||
|
|
||||||
impl<U, R> Destroy<wl_subsurface::WlSubsurface> for CompositorDestructor<U, R>
|
impl<U, R> Destroy<wl_subsurface::WlSubsurface> for CompositorDestructor<U, R>
|
||||||
where R: RoleType + Role<SubsurfaceRole>
|
where
|
||||||
|
R: RoleType + Role<SubsurfaceRole>,
|
||||||
{
|
{
|
||||||
fn destroy(subsurface: &wl_subsurface::WlSubsurface) {
|
fn destroy(subsurface: &wl_subsurface::WlSubsurface) {
|
||||||
let ptr = subsurface.get_user_data();
|
let ptr = subsurface.get_user_data();
|
||||||
|
|
|
@ -107,7 +107,7 @@ use self::region::RegionData;
|
||||||
use self::roles::{Role, RoleType, WrongRole};
|
use self::roles::{Role, RoleType, WrongRole};
|
||||||
use self::tree::SurfaceData;
|
use self::tree::SurfaceData;
|
||||||
pub use self::tree::TraversalAction;
|
pub use self::tree::TraversalAction;
|
||||||
use wayland_server::{Client, EventLoopHandle, Init, resource_is_registered};
|
use wayland_server::{resource_is_registered, Client, EventLoopHandle, Init};
|
||||||
|
|
||||||
use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_region, wl_surface};
|
use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_region, wl_surface};
|
||||||
|
|
||||||
|
@ -287,17 +287,15 @@ impl<U: Send + 'static, R: Send + 'static, H: Handler<U, R> + Send + 'static> Co
|
||||||
///
|
///
|
||||||
/// If the surface is not managed by the CompositorGlobal that provided this token, this
|
/// If the surface is not managed by the CompositorGlobal that provided this token, this
|
||||||
/// will panic (having more than one compositor is not supported).
|
/// will panic (having more than one compositor is not supported).
|
||||||
pub fn with_surface_data<F>(&self, surface: &wl_surface::WlSurface, f: F)
|
pub fn with_surface_data<F, T>(&self, surface: &wl_surface::WlSurface, f: F) -> T
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut SurfaceAttributes<U>),
|
F: FnOnce(&mut SurfaceAttributes<U>) -> T,
|
||||||
{
|
{
|
||||||
assert!(
|
assert!(
|
||||||
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid),
|
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid),
|
||||||
"Accessing the data of foreign surfaces is not supported."
|
"Accessing the data of foreign surfaces is not supported."
|
||||||
);
|
);
|
||||||
unsafe {
|
unsafe { SurfaceData::<U, R>::with_data(surface, f) }
|
||||||
SurfaceData::<U, R>::with_data(surface, f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,14 +319,11 @@ where
|
||||||
/// - a custom value that is passer in a fold-like maneer, but only from the output of a parent
|
/// - a custom value that is passer in a fold-like maneer, but only from the output of a parent
|
||||||
/// to its children. See `TraversalAction` for details.
|
/// to its children. See `TraversalAction` for details.
|
||||||
///
|
///
|
||||||
/// If the surface is not managed by the CompositorGlobal that provided this token, this
|
/// If the surface not managed by the CompositorGlobal that provided this token, this
|
||||||
/// will panic (having more than one compositor is not supported).
|
/// will panic (having more than one compositor is not supported).
|
||||||
pub fn with_surface_tree<F, T>(&self, surface: &wl_surface::WlSurface, initial: T, f: F) -> Result<(), ()>
|
pub fn with_surface_tree<F, T>(&self, surface: &wl_surface::WlSurface, initial: T, f: F) -> Result<(), ()>
|
||||||
where
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface,
|
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
||||||
&mut SurfaceAttributes<U>,
|
|
||||||
&mut R,
|
|
||||||
&T)
|
|
||||||
-> TraversalAction<T>,
|
-> TraversalAction<T>,
|
||||||
{
|
{
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -434,6 +429,24 @@ impl<U: Send + 'static, R: Send + RoleType + 'static, H: Handler<U, R> + Send +
|
||||||
unsafe { SurfaceData::<U, R>::give_role_with::<RoleData>(surface, data) }
|
unsafe { SurfaceData::<U, R>::give_role_with::<RoleData>(surface, data) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access the role data of a surface
|
||||||
|
///
|
||||||
|
/// Fails and don't call the closure if the surface doesn't have this role
|
||||||
|
///
|
||||||
|
/// 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>
|
||||||
|
where
|
||||||
|
R: Role<RoleData>,
|
||||||
|
F: FnOnce(&mut RoleData) -> T,
|
||||||
|
{
|
||||||
|
assert!(
|
||||||
|
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid),
|
||||||
|
"Accessing the data of foreign surfaces is not supported."
|
||||||
|
);
|
||||||
|
unsafe { SurfaceData::<U, R>::with_role_data::<RoleData, _, _>(surface, f) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Register that this surface does not have a role any longer and retrieve the data
|
/// Register that this surface does not have a role any longer and retrieve the data
|
||||||
///
|
///
|
||||||
|
|
|
@ -13,9 +13,9 @@ pub struct RegionData {
|
||||||
impl RegionData {
|
impl RegionData {
|
||||||
/// Initialize the user_data of a region, must be called right when the surface is created
|
/// 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: &wl_region::WlRegion) {
|
||||||
region.set_user_data(Box::into_raw(
|
region.set_user_data(
|
||||||
Box::new(Mutex::new(RegionData::default())),
|
Box::into_raw(Box::new(Mutex::new(RegionData::default()))) as *mut _,
|
||||||
) as *mut _)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cleans the user_data of that surface, must be called when it is destroyed
|
/// Cleans the user_data of that surface, must be called when it is destroyed
|
||||||
|
|
|
@ -58,9 +58,9 @@ impl<U: Default, R: Default> SurfaceData<U, R> {
|
||||||
|
|
||||||
/// Initialize the user_data of a surface, must be called right when the surface is created
|
/// 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: &wl_surface::WlSurface) {
|
||||||
surface.set_user_data(Box::into_raw(
|
surface.set_user_data(
|
||||||
Box::new(Mutex::new(SurfaceData::<U, R>::new())),
|
Box::into_raw(Box::new(Mutex::new(SurfaceData::<U, R>::new()))) as *mut _,
|
||||||
) as *mut _)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,21 +315,15 @@ impl<U, R> SurfaceData<U, R> {
|
||||||
/// false will cause an early-stopping.
|
/// false will cause an early-stopping.
|
||||||
pub unsafe fn map_tree<F, T>(root: &wl_surface::WlSurface, initial: T, mut f: F)
|
pub unsafe fn map_tree<F, T>(root: &wl_surface::WlSurface, initial: T, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface,
|
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
||||||
&mut SurfaceAttributes<U>,
|
|
||||||
&mut R,
|
|
||||||
&T)
|
|
||||||
-> TraversalAction<T>,
|
-> TraversalAction<T>,
|
||||||
{
|
{
|
||||||
// helper function for recursion
|
// helper function for recursion
|
||||||
unsafe fn map<U, R, F, T>(surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, initial: &T,
|
unsafe fn map<U, R, F, T>(surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface,
|
||||||
f: &mut F)
|
initial: &T, f: &mut F)
|
||||||
-> bool
|
-> bool
|
||||||
where
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface,
|
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
||||||
&mut SurfaceAttributes<U>,
|
|
||||||
&mut R,
|
|
||||||
&T)
|
|
||||||
-> TraversalAction<T>,
|
-> TraversalAction<T>,
|
||||||
{
|
{
|
||||||
// stop if we met the root, so to not deadlock/inifinte loop
|
// stop if we met the root, so to not deadlock/inifinte loop
|
||||||
|
|
|
@ -29,7 +29,7 @@ use wayland_server::protocol::{wl_keyboard, wl_surface};
|
||||||
|
|
||||||
use xkbcommon::xkb;
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
pub use xkbcommon::xkb::{Keysym, keysyms};
|
pub use xkbcommon::xkb::{keysyms, Keysym};
|
||||||
|
|
||||||
/// Represents the current state of the keyboard modifiers
|
/// Represents the current state of the keyboard modifiers
|
||||||
///
|
///
|
||||||
|
@ -178,8 +178,7 @@ where
|
||||||
"rules" => rules, "model" => model, "layout" => layout, "variant" => variant,
|
"rules" => rules, "model" => model, "layout" => layout, "variant" => variant,
|
||||||
"options" => &options
|
"options" => &options
|
||||||
);
|
);
|
||||||
let internal = KbdInternal::new(rules, model, layout, variant, options)
|
let internal = KbdInternal::new(rules, model, layout, variant, options).map_err(|_| {
|
||||||
.map_err(|_| {
|
|
||||||
debug!(log, "Loading keymap failed");
|
debug!(log, "Loading keymap failed");
|
||||||
Error::BadKeymap
|
Error::BadKeymap
|
||||||
})?;
|
})?;
|
||||||
|
@ -188,9 +187,9 @@ where
|
||||||
// prepare a tempfile with the keymap, to send it to clients
|
// prepare a tempfile with the keymap, to send it to clients
|
||||||
let mut keymap_file = tempfile().map_err(Error::IoError)?;
|
let mut keymap_file = tempfile().map_err(Error::IoError)?;
|
||||||
let keymap_data = internal.keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1);
|
let keymap_data = internal.keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1);
|
||||||
keymap_file.write_all(keymap_data.as_bytes()).map_err(
|
keymap_file
|
||||||
Error::IoError,
|
.write_all(keymap_data.as_bytes())
|
||||||
)?;
|
.map_err(Error::IoError)?;
|
||||||
keymap_file.flush().map_err(Error::IoError)?;
|
keymap_file.flush().map_err(Error::IoError)?;
|
||||||
|
|
||||||
trace!(log, "Keymap loaded and copied to tempfile.";
|
trace!(log, "Keymap loaded and copied to tempfile.";
|
||||||
|
|
25
src/lib.rs
25
src/lib.rs
|
@ -4,24 +4,24 @@
|
||||||
//! Most entry points in the modules can take an optionnal `slog::Logger` as argument
|
//! Most entry points in the modules can take an optionnal `slog::Logger` as argument
|
||||||
//! that will be used as a drain for logging. If `None` is provided, they'll log to `slog-stdlog`.
|
//! that will be used as a drain for logging. If `None` is provided, they'll log to `slog-stdlog`.
|
||||||
|
|
||||||
|
|
||||||
#![cfg_attr(feature = "clippy", feature(plugin))]
|
#![cfg_attr(feature = "clippy", feature(plugin))]
|
||||||
#![cfg_attr(feature = "clippy", plugin(clippy))]
|
#![cfg_attr(feature = "clippy", plugin(clippy))]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate wayland_server;
|
|
||||||
extern crate nix;
|
extern crate nix;
|
||||||
extern crate xkbcommon;
|
|
||||||
extern crate tempfile;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rental;
|
extern crate rental;
|
||||||
|
extern crate tempfile;
|
||||||
|
extern crate wayland_protocols;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate wayland_server;
|
||||||
|
extern crate xkbcommon;
|
||||||
|
|
||||||
#[cfg(feature = "backend_winit")]
|
|
||||||
extern crate winit;
|
|
||||||
#[cfg(feature = "backend_winit")]
|
|
||||||
extern crate wayland_client;
|
|
||||||
#[cfg(feature = "backend_libinput")]
|
#[cfg(feature = "backend_libinput")]
|
||||||
extern crate input;
|
extern crate input;
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
extern crate wayland_client;
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
extern crate winit;
|
||||||
|
|
||||||
extern crate libloading;
|
extern crate libloading;
|
||||||
|
|
||||||
|
@ -36,13 +36,14 @@ pub mod backend;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod shm;
|
pub mod shm;
|
||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
|
pub mod shell;
|
||||||
|
|
||||||
fn slog_or_stdlog<L>(logger: L) -> ::slog::Logger
|
fn slog_or_stdlog<L>(logger: L) -> ::slog::Logger
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
use slog::Drain;
|
use slog::Drain;
|
||||||
logger.into().unwrap_or_else(|| {
|
logger
|
||||||
::slog::Logger::root(::slog_stdlog::StdLog.fuse(), o!())
|
.into()
|
||||||
})
|
.unwrap_or_else(|| ::slog::Logger::root(::slog_stdlog::StdLog.fuse(), o!()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
use super::{Handler as UserHandler, ShellClientData, ShellHandler, ShellSurfaceRole};
|
||||||
|
use super::wl_handlers::WlShellDestructor;
|
||||||
|
use super::xdg_handlers::XdgShellDestructor;
|
||||||
|
|
||||||
|
use compositor::Handler as CompositorHandler;
|
||||||
|
use compositor::roles::*;
|
||||||
|
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use wayland_protocols::unstable::xdg_shell::server::zxdg_shell_v6;
|
||||||
|
use wayland_server::{Client, EventLoopHandle, GlobalHandler, Resource};
|
||||||
|
use wayland_server::protocol::{wl_shell, wl_shell_surface};
|
||||||
|
|
||||||
|
fn shell_client_data<SD: Default>() -> ShellClientData<SD> {
|
||||||
|
ShellClientData {
|
||||||
|
pending_ping: 0,
|
||||||
|
data: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> GlobalHandler<wl_shell::WlShell> for ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Default + Send + 'static,
|
||||||
|
{
|
||||||
|
fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_shell::WlShell) {
|
||||||
|
debug!(self.log, "New wl_shell global binded.");
|
||||||
|
global.set_user_data(Box::into_raw(Box::new(Mutex::new((
|
||||||
|
shell_client_data::<SD>(),
|
||||||
|
Vec::<wl_shell_surface::WlShellSurface>::new(),
|
||||||
|
)))) as *mut _);
|
||||||
|
evlh.register_with_destructor::<_, Self, WlShellDestructor<SD>>(&global, self.my_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> GlobalHandler<zxdg_shell_v6::ZxdgShellV6> for ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Default + Send + 'static,
|
||||||
|
{
|
||||||
|
fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: zxdg_shell_v6::ZxdgShellV6) {
|
||||||
|
debug!(self.log, "New xdg_shell global binded.");
|
||||||
|
global.set_user_data(
|
||||||
|
Box::into_raw(Box::new(Mutex::new(shell_client_data::<SD>()))) as *mut _,
|
||||||
|
);
|
||||||
|
evlh.register_with_destructor::<_, Self, XdgShellDestructor<SD>>(&global, self.my_id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,874 @@
|
||||||
|
//! Utilities for handling shell surfaces, toplevel and popups
|
||||||
|
//!
|
||||||
|
//! This module provides the `ShellHandler` type, which implements automatic handling of
|
||||||
|
//! shell surfaces objects, by being registered as a global handler for `wl_shell` and
|
||||||
|
//! `xdg_shell`.
|
||||||
|
//!
|
||||||
|
//! ## Why use this handler
|
||||||
|
//!
|
||||||
|
//! This handler 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, simply instanciate it and register it to the event loop
|
||||||
|
//! as a global handler for xdg_shell and wl_shell. You will need to provide it the
|
||||||
|
//! `CompositorToken` you retrieved from an instanciation of the `CompositorHandler`
|
||||||
|
//! provided by smithay.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # extern crate wayland_server;
|
||||||
|
//! # #[macro_use] extern crate smithay;
|
||||||
|
//! # extern crate wayland_protocols;
|
||||||
|
//! #
|
||||||
|
//! use smithay::compositor::roles::*;
|
||||||
|
//! use smithay::compositor::CompositorToken;
|
||||||
|
//! use smithay::shell::{ShellHandler, Handler as ShellHandlerTrait, ShellSurfaceRole};
|
||||||
|
//! use wayland_server::protocol::wl_shell::WlShell;
|
||||||
|
//! use wayland_protocols::unstable::xdg_shell::server::zxdg_shell_v6::ZxdgShellV6;
|
||||||
|
//! use wayland_server::{EventLoop, EventLoopHandle};
|
||||||
|
//! # use smithay::shell::*;
|
||||||
|
//! # use wayland_server::protocol::{wl_seat, wl_output};
|
||||||
|
//! # use wayland_protocols::unstable::xdg_shell::server::zxdg_toplevel_v6;
|
||||||
|
//! # #[derive(Default)] struct MySurfaceData;
|
||||||
|
//! # struct MyHandlerForCompositor;
|
||||||
|
//! # impl ::smithay::compositor::Handler<MySurfaceData, MyRoles> for MyHandlerForCompositor {}
|
||||||
|
//!
|
||||||
|
//! // 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 {
|
||||||
|
//! /* ... */
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // define a sub-handler for the shell::Handler trait
|
||||||
|
//! struct MyHandlerForShell {
|
||||||
|
//! /* ... */
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! # type MyToplevelSurface = ToplevelSurface<MySurfaceData, MyRoles, MyHandlerForCompositor,
|
||||||
|
//! # MyShellData>;
|
||||||
|
//! # type MyPopupSurface = PopupSurface<MySurfaceData, MyRoles, MyHandlerForCompositor,
|
||||||
|
//! # MyShellData>;
|
||||||
|
//!
|
||||||
|
//! impl ShellHandlerTrait<MySurfaceData, MyRoles, MyHandlerForCompositor, MyShellData> for MyHandlerForShell {
|
||||||
|
//! /* ... a few methods to implement, see shell::Handler
|
||||||
|
//! documentation for details ... */
|
||||||
|
//! # fn new_client(&mut self, evlh: &mut EventLoopHandle, client: ShellClient<MyShellData>) { unimplemented!() }
|
||||||
|
//! # fn client_pong(&mut self, evlh: &mut EventLoopHandle, client: ShellClient<MyShellData>) { unimplemented!() }
|
||||||
|
//! # fn new_toplevel(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface)
|
||||||
|
//! # -> ToplevelConfigure { unimplemented!() }
|
||||||
|
//! # fn new_popup(&mut self, evlh: &mut EventLoopHandle, surface: MyPopupSurface)
|
||||||
|
//! # -> PopupConfigure { unimplemented!() }
|
||||||
|
//! # fn move_(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface,
|
||||||
|
//! # seat: &wl_seat::WlSeat, serial: u32) { unimplemented!() }
|
||||||
|
//! # fn resize(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface,
|
||||||
|
//! # seat: &wl_seat::WlSeat, serial: u32, edges: zxdg_toplevel_v6::ResizeEdge) { unimplemented!() }
|
||||||
|
//! # fn grab(&mut self, evlh: &mut EventLoopHandle, surface: MyPopupSurface,
|
||||||
|
//! # seat: &wl_seat::WlSeat, serial: u32) { unimplemented!() }
|
||||||
|
//! # fn change_display_state(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface,
|
||||||
|
//! # maximized: Option<bool>, minimized: Option<bool>, fullscreen: Option<bool>,
|
||||||
|
//! # output: Option<&wl_output::WlOutput>)
|
||||||
|
//! # -> ToplevelConfigure { unimplemented!() }
|
||||||
|
//! # fn show_window_menu(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface,
|
||||||
|
//! # seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32) { unimplemented!() }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! # type MyCompositorHandler = smithay::compositor::CompositorHandler<MySurfaceData, MyRoles,
|
||||||
|
//! # MyHandlerForCompositor>;
|
||||||
|
//! // A type alias for brevety. ShellHandler has many type parameters:
|
||||||
|
//! type MyShellHandler = ShellHandler<
|
||||||
|
//! MySurfaceData, // the surface data you defined for the CompositorHandler
|
||||||
|
//! MyRoles, // the roles type
|
||||||
|
//! MyHandlerForCompositor, // the sub-handler type you defined for the CompositorHandler
|
||||||
|
//! MyHandlerForShell, // the sub-handler type you defined for this ShellHandler
|
||||||
|
//! MyShellData // the client data you defined for this ShellHandler
|
||||||
|
//! >;
|
||||||
|
//! # fn main() {
|
||||||
|
//! # let (_display, mut event_loop) = wayland_server::create_display();
|
||||||
|
//! # let compositor_hid = event_loop.add_handler_with_init(
|
||||||
|
//! # MyCompositorHandler::new(MyHandlerForCompositor{ /* ... */ }, None /* put a logger here */)
|
||||||
|
//! # );
|
||||||
|
//! # let compositor_token = {
|
||||||
|
//! # let state = event_loop.state();
|
||||||
|
//! # state.get_handler::<MyCompositorHandler>(compositor_hid).get_token()
|
||||||
|
//! # };
|
||||||
|
//!
|
||||||
|
//! let shell_hid = event_loop.add_handler_with_init(
|
||||||
|
//! MyShellHandler::new(
|
||||||
|
//! MyHandlerForShell{ /* ... */ },
|
||||||
|
//! compositor_token, // the composior token you retrieved from the CompositorHandler
|
||||||
|
//! None /* put a logger here */
|
||||||
|
//! )
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! event_loop.register_global::<WlShell, MyShellHandler>(shell_hid, 1);
|
||||||
|
//! event_loop.register_global::<ZxdgShellV6, MyShellHandler>(shell_hid, 1);
|
||||||
|
//!
|
||||||
|
//! // 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 handler:
|
||||||
|
//!
|
||||||
|
//! - `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 `Shellhandler`.
|
||||||
|
//! - `PopupSurface`: This is a handle representing a popup/tooltip surface. Similarly,
|
||||||
|
//! you can get a list of all currently alive popup surface from the `ShellHandler`.
|
||||||
|
//!
|
||||||
|
//! You'll obtain these objects though two means: either via the callback methods of
|
||||||
|
//! the subhandler you provided, or via methods on the `ShellHandler` that you can
|
||||||
|
//! access from the `state()` of the event loop.
|
||||||
|
|
||||||
|
use compositor::{CompositorToken, Handler as CompositorHandler, Rectangle};
|
||||||
|
use compositor::roles::Role;
|
||||||
|
|
||||||
|
use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6 as xdg_positioner,
|
||||||
|
zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6};
|
||||||
|
|
||||||
|
use wayland_server::{EventLoopHandle, EventResult, Init, Liveness, Resource};
|
||||||
|
use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface};
|
||||||
|
|
||||||
|
mod global;
|
||||||
|
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.clone(),
|
||||||
|
})
|
||||||
|
} 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The handler for the shell globals
|
||||||
|
///
|
||||||
|
/// See module-level documentation for its use.
|
||||||
|
pub struct ShellHandler<U, R, H, SH, SD> {
|
||||||
|
my_id: usize,
|
||||||
|
log: ::slog::Logger,
|
||||||
|
token: CompositorToken<U, R, H>,
|
||||||
|
handler: SH,
|
||||||
|
known_toplevels: Vec<ToplevelSurface<U, R, H, SD>>,
|
||||||
|
known_popups: Vec<PopupSurface<U, R, H, SD>>,
|
||||||
|
_shell_data: ::std::marker::PhantomData<SD>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> Init for ShellHandler<U, R, H, SH, SD> {
|
||||||
|
fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) {
|
||||||
|
self.my_id = index;
|
||||||
|
debug!(self.log, "Init finished")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
{
|
||||||
|
/// Create a new CompositorHandler
|
||||||
|
pub fn new<L>(handler: SH, token: CompositorToken<U, R, H>, logger: L) -> ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let log = ::slog_or_stdlog(logger);
|
||||||
|
ShellHandler {
|
||||||
|
my_id: ::std::usize::MAX,
|
||||||
|
log: log.new(o!("smithay_module" => "shell_handler")),
|
||||||
|
token: token,
|
||||||
|
handler: handler,
|
||||||
|
known_toplevels: Vec::new(),
|
||||||
|
known_popups: Vec::new(),
|
||||||
|
_shell_data: ::std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the inner handler of this CompositorHandler
|
||||||
|
pub fn get_handler(&mut self) -> &mut SH {
|
||||||
|
&mut self.handler
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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, H, SD>] {
|
||||||
|
&self.known_toplevels[..]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access all the popup surfaces known by this handler
|
||||||
|
pub fn popup_surfaces(&self) -> &[PopupSurface<U, R, H, SD>] {
|
||||||
|
&self.known_popups[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User interaction
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum ShellClientKind {
|
||||||
|
Wl(wl_shell::WlShell),
|
||||||
|
Xdg(zxdg_shell_v6::ZxdgShellV6),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ShellClientData<SD> {
|
||||||
|
pending_ping: u32,
|
||||||
|
data: SD,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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, H, SD> {
|
||||||
|
wl_surface: wl_surface::WlSurface,
|
||||||
|
shell_surface: SurfaceKind,
|
||||||
|
token: CompositorToken<U, R, H>,
|
||||||
|
_shell_data: ::std::marker::PhantomData<SD>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SD> ToplevelSurface<U, R, H, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + '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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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, H, SD> {
|
||||||
|
wl_surface: wl_surface::WlSurface,
|
||||||
|
shell_surface: SurfaceKind,
|
||||||
|
token: CompositorToken<U, R, H>,
|
||||||
|
_shell_data: ::std::marker::PhantomData<SD>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SD> PopupSurface<U, R, H, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + '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 trait for the sub-handler provided to the ShellHandler
|
||||||
|
///
|
||||||
|
/// You need to implement this trait to handle events that the ShellHandler
|
||||||
|
/// cannot process for you directly.
|
||||||
|
///
|
||||||
|
/// Depending on what you want to do, you might implement some of these methods
|
||||||
|
/// as doing nothing.
|
||||||
|
pub trait Handler<U, R, H, SD> {
|
||||||
|
/// A new shell client was instanciated
|
||||||
|
fn new_client(&mut self, evlh: &mut EventLoopHandle, 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.
|
||||||
|
fn client_pong(&mut self, evlh: &mut EventLoopHandle, client: ShellClient<SD>);
|
||||||
|
/// A new toplevel surface was created
|
||||||
|
///
|
||||||
|
/// You need to return a `ToplevelConfigure` from this method, which will be sent
|
||||||
|
/// to the client to configure this surface
|
||||||
|
fn new_toplevel(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>)
|
||||||
|
-> ToplevelConfigure;
|
||||||
|
/// A new popup surface was created
|
||||||
|
///
|
||||||
|
/// You need to return a `PopupConfigure` from this method, which will be sent
|
||||||
|
/// to the client to configure this surface
|
||||||
|
fn new_popup(&mut self, evlh: &mut EventLoopHandle, surface: PopupSurface<U, R, H, SD>)
|
||||||
|
-> PopupConfigure;
|
||||||
|
/// The client requested the start of an interactive move for this surface
|
||||||
|
fn move_(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, 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.
|
||||||
|
fn resize(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, 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.
|
||||||
|
fn grab(&mut self, evlh: &mut EventLoopHandle, surface: PopupSurface<U, R, H, 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.
|
||||||
|
fn change_display_state(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, 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...).
|
||||||
|
fn show_window_menu(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>,
|
||||||
|
seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32);
|
||||||
|
}
|
|
@ -0,0 +1,370 @@
|
||||||
|
use super::{Handler as UserHandler, PopupConfigure, PopupState, PositionerState, ShellClient,
|
||||||
|
ShellClientData, ShellHandler, ShellSurfacePendingState, ShellSurfaceRole, ToplevelConfigure,
|
||||||
|
ToplevelState};
|
||||||
|
|
||||||
|
use compositor::{CompositorToken, Handler as CompositorHandler, Rectangle};
|
||||||
|
use compositor::roles::*;
|
||||||
|
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use wayland_protocols::unstable::xdg_shell::server::{zxdg_positioner_v6 as xdg_positioner, zxdg_toplevel_v6};
|
||||||
|
|
||||||
|
use wayland_server::{Client, Destroy, EventLoopHandle, Resource};
|
||||||
|
use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface};
|
||||||
|
|
||||||
|
pub struct WlShellDestructor<SD> {
|
||||||
|
_data: ::std::marker::PhantomData<SD>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* wl_shell
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub type ShellUserData<SD> = Mutex<(ShellClientData<SD>, Vec<wl_shell_surface::WlShellSurface>)>;
|
||||||
|
|
||||||
|
impl<SD> Destroy<wl_shell::WlShell> for WlShellDestructor<SD> {
|
||||||
|
fn destroy(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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> wl_shell::Handler for ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Send + 'static,
|
||||||
|
{
|
||||||
|
fn get_shell_surface(&mut self, evlh: &mut EventLoopHandle, _: &Client, resource: &wl_shell::WlShell,
|
||||||
|
id: wl_shell_surface::WlShellSurface, surface: &wl_surface::WlSurface) {
|
||||||
|
trace!(self.log, "Creating new wl_shell_surface.");
|
||||||
|
let role_data = ShellSurfaceRole {
|
||||||
|
pending_state: ShellSurfacePendingState::None,
|
||||||
|
window_geometry: None,
|
||||||
|
pending_configures: Vec::new(),
|
||||||
|
configured: true,
|
||||||
|
};
|
||||||
|
if let Err(_) = self.token.give_role_with(surface, role_data) {
|
||||||
|
resource.post_error(
|
||||||
|
wl_shell::Error::Role as u32,
|
||||||
|
"Surface already has a role.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
id.set_user_data(
|
||||||
|
Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _,
|
||||||
|
);
|
||||||
|
evlh.register_with_destructor::<_, Self, WlShellDestructor<SD>>(&id, self.my_id);
|
||||||
|
|
||||||
|
// register ourselves to the wl_shell for ping handling
|
||||||
|
let mutex = unsafe { &*(resource.get_user_data() as *mut ShellUserData<SD>) };
|
||||||
|
let mut guard = mutex.lock().unwrap();
|
||||||
|
if guard.1.len() == 0 && 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!
|
||||||
|
id.ping(guard.0.pending_ping);
|
||||||
|
}
|
||||||
|
guard.1.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server_declare_handler!(
|
||||||
|
ShellHandler<U: [Send], R: [Role<ShellSurfaceRole>, Send], H:[CompositorHandler<U, R>, Send], SH:[UserHandler<U,R,H,SD>, Send], SD: [Send]>,
|
||||||
|
wl_shell::Handler,
|
||||||
|
wl_shell::WlShell
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* wl_shell_surface
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub type ShellSurfaceUserData = (wl_surface::WlSurface, wl_shell::WlShell);
|
||||||
|
|
||||||
|
impl<SD> Destroy<wl_shell_surface::WlShellSurface> for WlShellDestructor<SD> {
|
||||||
|
fn destroy(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Send + 'static,
|
||||||
|
{
|
||||||
|
fn wl_handle_display_state_change(&mut self, evlh: &mut EventLoopHandle,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface,
|
||||||
|
maximized: Option<bool>, minimized: Option<bool>,
|
||||||
|
fullscreen: Option<bool>, output: Option<&wl_output::WlOutput>) {
|
||||||
|
let handle = make_toplevel_handle(self.token, resource);
|
||||||
|
// handler callback
|
||||||
|
let configure =
|
||||||
|
self.handler
|
||||||
|
.change_display_state(evlh, handle, maximized, minimized, fullscreen, output);
|
||||||
|
// send the configure response to client
|
||||||
|
let (w, h) = configure.size.unwrap_or((0, 0));
|
||||||
|
resource.configure(wl_shell_surface::None, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wl_ensure_toplevel(&mut self, evlh: &mut EventLoopHandle,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
|
// copy token to make borrow checker happy
|
||||||
|
let token = self.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
|
||||||
|
self.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),
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.expect(
|
||||||
|
"xdg_surface exists but surface has not shell_surface role?!",
|
||||||
|
);
|
||||||
|
// we need to notify about this new toplevel surface
|
||||||
|
if need_send {
|
||||||
|
let handle = make_toplevel_handle(self.token, resource);
|
||||||
|
let configure = self.handler.new_toplevel(evlh, handle);
|
||||||
|
send_toplevel_configure(resource, configure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> wl_shell_surface::Handler for ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Send + 'static,
|
||||||
|
{
|
||||||
|
fn pong(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface, serial: u32) {
|
||||||
|
let &(_, ref shell) = unsafe { &*(resource.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 {
|
||||||
|
self.handler.client_pong(evlh, make_shell_client(shell));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32) {
|
||||||
|
let handle = make_toplevel_handle(self.token, resource);
|
||||||
|
self.handler.move_(evlh, handle, seat, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32,
|
||||||
|
edges: wl_shell_surface::Resize) {
|
||||||
|
let edges = zxdg_toplevel_v6::ResizeEdge::from_raw(edges.bits())
|
||||||
|
.unwrap_or(zxdg_toplevel_v6::ResizeEdge::None);
|
||||||
|
let handle = make_toplevel_handle(self.token, resource);
|
||||||
|
self.handler.resize(evlh, handle, seat, serial, edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_toplevel(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface) {
|
||||||
|
self.wl_ensure_toplevel(evlh, resource);
|
||||||
|
self.wl_handle_display_state_change(evlh, resource, Some(false), Some(false), Some(false), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_transient(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface, parent: &wl_surface::WlSurface, _x: i32,
|
||||||
|
_y: i32, _flags: wl_shell_surface::Transient) {
|
||||||
|
self.wl_ensure_toplevel(evlh, resource);
|
||||||
|
// set the parent
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
|
self.token
|
||||||
|
.with_role_data::<ShellSurfaceRole, _, _>(wl_surface, |data| match data.pending_state {
|
||||||
|
ShellSurfacePendingState::Toplevel(ref mut state) => {
|
||||||
|
state.parent = Some(unsafe { parent.clone_unchecked() });
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
// set as regular surface
|
||||||
|
self.wl_handle_display_state_change(evlh, resource, Some(false), Some(false), Some(false), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_fullscreen(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface,
|
||||||
|
_method: wl_shell_surface::FullscreenMethod, _framerate: u32,
|
||||||
|
output: Option<&wl_output::WlOutput>) {
|
||||||
|
self.wl_ensure_toplevel(evlh, resource);
|
||||||
|
self.wl_handle_display_state_change(evlh, resource, Some(false), Some(false), Some(true), output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_popup(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32,
|
||||||
|
parent: &wl_surface::WlSurface, x: i32, y: i32, _: wl_shell_surface::Transient) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
|
// we are reseting the popup state, so remove this surface from everywhere
|
||||||
|
self.known_toplevels.retain(|other| {
|
||||||
|
other
|
||||||
|
.get_surface()
|
||||||
|
.map(|s| !s.equals(wl_surface))
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
|
self.known_popups.retain(|other| {
|
||||||
|
other
|
||||||
|
.get_surface()
|
||||||
|
.map(|s| !s.equals(wl_surface))
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
|
self.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
|
||||||
|
let handle = make_popup_handle(self.token, resource);
|
||||||
|
let configure = self.handler.new_popup(evlh, handle);
|
||||||
|
send_popup_configure(resource, configure);
|
||||||
|
self.handler
|
||||||
|
.grab(evlh, make_popup_handle(self.token, resource), seat, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_maximized(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface, output: Option<&wl_output::WlOutput>) {
|
||||||
|
self.wl_ensure_toplevel(evlh, resource);
|
||||||
|
self.wl_handle_display_state_change(evlh, resource, Some(true), Some(false), Some(false), output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_title(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface, title: String) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
|
self.token
|
||||||
|
.with_role_data(surface, |data| match data.pending_state {
|
||||||
|
ShellSurfacePendingState::Toplevel(ref mut state) => {
|
||||||
|
state.title = title;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
})
|
||||||
|
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_class(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &wl_shell_surface::WlShellSurface, class_: String) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
|
self.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?!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server_declare_handler!(
|
||||||
|
ShellHandler<U: [Send], R: [Role<ShellSurfaceRole>, Send], H:[CompositorHandler<U, R>, Send], SH: [UserHandler<U,R,H,SD>, Send], SD: [Send]>,
|
||||||
|
wl_shell_surface::Handler,
|
||||||
|
wl_shell_surface::WlShellSurface
|
||||||
|
);
|
|
@ -0,0 +1,734 @@
|
||||||
|
use super::{Handler as UserHandler, PopupConfigure, PopupState, PositionerState, ShellClient,
|
||||||
|
ShellClientData, ShellHandler, ShellSurfacePendingState, ShellSurfaceRole, ToplevelConfigure,
|
||||||
|
ToplevelState};
|
||||||
|
|
||||||
|
use compositor::{CompositorToken, Handler as CompositorHandler, Rectangle};
|
||||||
|
use compositor::roles::*;
|
||||||
|
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6, zxdg_shell_v6,
|
||||||
|
zxdg_surface_v6, zxdg_toplevel_v6};
|
||||||
|
use wayland_server::{Client, Destroy, EventLoopHandle, Resource};
|
||||||
|
use wayland_server::protocol::{wl_output, wl_seat, wl_surface};
|
||||||
|
|
||||||
|
pub struct XdgShellDestructor<SD> {
|
||||||
|
_data: ::std::marker::PhantomData<SD>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xdg_shell
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub type ShellUserData<SD> = Mutex<ShellClientData<SD>>;
|
||||||
|
|
||||||
|
impl<SD> Destroy<zxdg_shell_v6::ZxdgShellV6> for XdgShellDestructor<SD> {
|
||||||
|
fn destroy(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 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> zxdg_shell_v6::Handler for ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Send + 'static,
|
||||||
|
{
|
||||||
|
fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, _: &zxdg_shell_v6::ZxdgShellV6) {}
|
||||||
|
fn create_positioner(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
_: &zxdg_shell_v6::ZxdgShellV6, id: zxdg_positioner_v6::ZxdgPositionerV6) {
|
||||||
|
trace!(self.log, "Creating new xdg_positioner.");
|
||||||
|
id.set_user_data(Box::into_raw(Box::new(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),
|
||||||
|
})) as *mut _);
|
||||||
|
evlh.register_with_destructor::<_, Self, XdgShellDestructor<SD>>(&id, self.my_id);
|
||||||
|
}
|
||||||
|
fn get_xdg_surface(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_shell_v6::ZxdgShellV6, id: zxdg_surface_v6::ZxdgSurfaceV6,
|
||||||
|
surface: &wl_surface::WlSurface) {
|
||||||
|
trace!(self.log, "Creating new wl_shell_surface.");
|
||||||
|
let role_data = ShellSurfaceRole {
|
||||||
|
pending_state: ShellSurfacePendingState::None,
|
||||||
|
window_geometry: None,
|
||||||
|
pending_configures: Vec::new(),
|
||||||
|
configured: false,
|
||||||
|
};
|
||||||
|
if let Err(_) = self.token.give_role_with(surface, role_data) {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_shell_v6::Error::Role as u32,
|
||||||
|
"Surface already has a role.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
id.set_user_data(
|
||||||
|
Box::into_raw(Box::new((unsafe { surface.clone_unchecked() }, unsafe {
|
||||||
|
resource.clone_unchecked()
|
||||||
|
}))) as *mut _,
|
||||||
|
);
|
||||||
|
evlh.register_with_destructor::<_, Self, XdgShellDestructor<SD>>(&id, self.my_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pong(&mut self, evlh: &mut EventLoopHandle, _: &Client, resource: &zxdg_shell_v6::ZxdgShellV6,
|
||||||
|
serial: u32) {
|
||||||
|
let valid = {
|
||||||
|
let mutex = unsafe { &*(resource.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 {
|
||||||
|
self.handler.client_pong(evlh, make_shell_client(resource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server_declare_handler!(
|
||||||
|
ShellHandler<U: [Send], R: [Role<ShellSurfaceRole>, Send], H:[CompositorHandler<U, R>, Send], SH: [UserHandler<U,R,H,SD>, Send], SD: [Send]>,
|
||||||
|
zxdg_shell_v6::Handler,
|
||||||
|
zxdg_shell_v6::ZxdgShellV6
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xdg_positioner
|
||||||
|
*/
|
||||||
|
|
||||||
|
impl<SD> Destroy<zxdg_positioner_v6::ZxdgPositionerV6> for XdgShellDestructor<SD> {
|
||||||
|
fn destroy(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> zxdg_positioner_v6::Handler for ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Send + 'static,
|
||||||
|
{
|
||||||
|
fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, _: &zxdg_positioner_v6::ZxdgPositionerV6) {}
|
||||||
|
|
||||||
|
fn set_size(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_positioner_v6::ZxdgPositionerV6, width: i32, height: i32) {
|
||||||
|
if width < 1 || height < 1 {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_positioner_v6::Error::InvalidInput as u32,
|
||||||
|
"Invalid size for positioner.".into(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let state = unsafe { &mut *(ptr as *mut PositionerState) };
|
||||||
|
state.rect_size = (width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_anchor_rect(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_positioner_v6::ZxdgPositionerV6, x: i32, y: i32, width: i32,
|
||||||
|
height: i32) {
|
||||||
|
if width < 1 || height < 1 {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_positioner_v6::Error::InvalidInput as u32,
|
||||||
|
"Invalid size for positioner's anchor rectangle.".into(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let state = unsafe { &mut *(ptr as *mut PositionerState) };
|
||||||
|
state.anchor_rect = Rectangle {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_anchor(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_positioner_v6::ZxdgPositionerV6, anchor: zxdg_positioner_v6::Anchor) {
|
||||||
|
use self::zxdg_positioner_v6::{AnchorBottom, AnchorLeft, AnchorRight, AnchorTop};
|
||||||
|
if anchor.contains(AnchorLeft | AnchorRight) || anchor.contains(AnchorTop | AnchorBottom) {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_positioner_v6::Error::InvalidInput as u32,
|
||||||
|
"Invalid anchor for positioner.".into(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let state = unsafe { &mut *(ptr as *mut PositionerState) };
|
||||||
|
state.anchor_edges = anchor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_gravity(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_positioner_v6::ZxdgPositionerV6, gravity: zxdg_positioner_v6::Gravity) {
|
||||||
|
use self::zxdg_positioner_v6::{GravityBottom, GravityLeft, GravityRight, GravityTop};
|
||||||
|
if gravity.contains(GravityLeft | GravityRight) || gravity.contains(GravityTop | GravityBottom) {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_positioner_v6::Error::InvalidInput as u32,
|
||||||
|
"Invalid gravity for positioner.".into(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let state = unsafe { &mut *(ptr as *mut PositionerState) };
|
||||||
|
state.gravity = gravity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_constraint_adjustment(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_positioner_v6::ZxdgPositionerV6,
|
||||||
|
constraint_adjustment: u32) {
|
||||||
|
let constraint_adjustment =
|
||||||
|
zxdg_positioner_v6::ConstraintAdjustment::from_bits_truncate(constraint_adjustment);
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let state = unsafe { &mut *(ptr as *mut PositionerState) };
|
||||||
|
state.constraint_adjustment = constraint_adjustment;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_offset(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_positioner_v6::ZxdgPositionerV6, x: i32, y: i32) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let state = unsafe { &mut *(ptr as *mut PositionerState) };
|
||||||
|
state.offset = (x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server_declare_handler!(
|
||||||
|
ShellHandler<U: [Send], R: [Role<ShellSurfaceRole>, Send], H:[CompositorHandler<U, R>, Send], SH: [UserHandler<U,R,H,SD>, Send], SD: [Send]>,
|
||||||
|
zxdg_positioner_v6::Handler,
|
||||||
|
zxdg_positioner_v6::ZxdgPositionerV6
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xdg_surface
|
||||||
|
*/
|
||||||
|
|
||||||
|
impl<SD> Destroy<zxdg_surface_v6::ZxdgSurfaceV6> for XdgShellDestructor<SD> {
|
||||||
|
fn destroy(surface: &zxdg_surface_v6::ZxdgSurfaceV6) {
|
||||||
|
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 (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
// explicit call to drop to not forget what we're doing here
|
||||||
|
::std::mem::drop(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> zxdg_surface_v6::Handler for ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Send + 'static,
|
||||||
|
{
|
||||||
|
fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, resource: &zxdg_surface_v6::ZxdgSurfaceV6) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref surface, ref shell) =
|
||||||
|
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
|
||||||
|
self.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?!",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_toplevel(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_surface_v6::ZxdgSurfaceV6, id: zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref surface, ref shell) =
|
||||||
|
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
|
||||||
|
self.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?!",
|
||||||
|
);
|
||||||
|
|
||||||
|
id.set_user_data(Box::into_raw(Box::new(unsafe {
|
||||||
|
(
|
||||||
|
surface.clone_unchecked(),
|
||||||
|
shell.clone_unchecked(),
|
||||||
|
resource.clone_unchecked(),
|
||||||
|
)
|
||||||
|
})) as *mut _);
|
||||||
|
evlh.register_with_destructor::<_, Self, XdgShellDestructor<SD>>(&id, self.my_id);
|
||||||
|
|
||||||
|
// register to self
|
||||||
|
self.known_toplevels
|
||||||
|
.push(make_toplevel_handle(self.token, &id));
|
||||||
|
|
||||||
|
// intial configure event
|
||||||
|
let handle = make_toplevel_handle(self.token, &id);
|
||||||
|
let configure = self.handler.new_toplevel(evlh, handle);
|
||||||
|
send_toplevel_configure(self.token, &id, configure);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_popup(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_surface_v6::ZxdgSurfaceV6, id: zxdg_popup_v6::ZxdgPopupV6,
|
||||||
|
parent: &zxdg_surface_v6::ZxdgSurfaceV6,
|
||||||
|
positioner: &zxdg_positioner_v6::ZxdgPositionerV6) {
|
||||||
|
let ptr = resource.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)) };
|
||||||
|
|
||||||
|
self.token
|
||||||
|
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
|
||||||
|
data.pending_state = ShellSurfacePendingState::Popup(PopupState {
|
||||||
|
parent: unsafe { parent_surface.clone_unchecked() },
|
||||||
|
positioner: positioner_data.clone(),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.expect(
|
||||||
|
"xdg_surface exists but surface has not shell_surface role?!",
|
||||||
|
);
|
||||||
|
|
||||||
|
id.set_user_data(Box::into_raw(Box::new(unsafe {
|
||||||
|
(
|
||||||
|
surface.clone_unchecked(),
|
||||||
|
shell.clone_unchecked(),
|
||||||
|
resource.clone_unchecked(),
|
||||||
|
)
|
||||||
|
})) as *mut _);
|
||||||
|
evlh.register_with_destructor::<_, Self, XdgShellDestructor<SD>>(&id, self.my_id);
|
||||||
|
|
||||||
|
// register to self
|
||||||
|
self.known_popups.push(make_popup_handle(self.token, &id));
|
||||||
|
|
||||||
|
// intial configure event
|
||||||
|
let handle = make_popup_handle(self.token, &id);
|
||||||
|
let configure = self.handler.new_popup(evlh, handle);
|
||||||
|
send_popup_configure(self.token, &id, configure);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_window_geometry(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_surface_v6::ZxdgSurfaceV6, x: i32, y: i32, width: i32,
|
||||||
|
height: i32) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref surface, _) =
|
||||||
|
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
|
||||||
|
self.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?!",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ack_configure(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_surface_v6::ZxdgSurfaceV6, serial: u32) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref surface, ref shell) =
|
||||||
|
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
|
||||||
|
self.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?!",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server_declare_handler!(
|
||||||
|
ShellHandler<U: [Send], R: [Role<ShellSurfaceRole>, Send], H:[CompositorHandler<U, R>, Send], SH: [UserHandler<U,R,H,SD>, Send], SD: [Send]>,
|
||||||
|
zxdg_surface_v6::Handler,
|
||||||
|
zxdg_surface_v6::ZxdgSurfaceV6
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xdg_toplevel
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub type ShellSurfaceUserData = (
|
||||||
|
wl_surface::WlSurface,
|
||||||
|
zxdg_shell_v6::ZxdgShellV6,
|
||||||
|
zxdg_surface_v6::ZxdgSurfaceV6,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<SD> Destroy<zxdg_toplevel_v6::ZxdgToplevelV6> for XdgShellDestructor<SD> {
|
||||||
|
fn destroy(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Send + 'static,
|
||||||
|
{
|
||||||
|
// Utility function allowing to factor out a lot of the upcoming logic
|
||||||
|
fn with_surface_toplevel_data<F>(&self, resource: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut ToplevelState),
|
||||||
|
{
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
|
self.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(&mut self, evlh: &mut EventLoopHandle,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||||
|
maximized: Option<bool>, minimized: Option<bool>,
|
||||||
|
fullscreen: Option<bool>, output: Option<&wl_output::WlOutput>) {
|
||||||
|
let handle = make_toplevel_handle(self.token, resource);
|
||||||
|
// handler callback
|
||||||
|
let configure =
|
||||||
|
self.handler
|
||||||
|
.change_display_state(evlh, handle, maximized, minimized, fullscreen, output);
|
||||||
|
// send the configure response to client
|
||||||
|
send_toplevel_configure(self.token, resource, configure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_toplevel_configure<U, R, H>(token: CompositorToken<U, R, H>,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||||
|
configure: ToplevelConfigure)
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + '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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> zxdg_toplevel_v6::Handler for ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Send + 'static,
|
||||||
|
{
|
||||||
|
fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, resource: &zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
|
self.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)
|
||||||
|
self.known_toplevels.retain(|other| {
|
||||||
|
other
|
||||||
|
.get_surface()
|
||||||
|
.map(|s| !s.equals(surface))
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_parent(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||||
|
parent: Option<&zxdg_toplevel_v6::ZxdgToplevelV6>) {
|
||||||
|
self.with_surface_toplevel_data(resource, |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() }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_title(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, title: String) {
|
||||||
|
self.with_surface_toplevel_data(resource, |toplevel_data| { toplevel_data.title = title; });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_app_id(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, app_id: String) {
|
||||||
|
self.with_surface_toplevel_data(resource, |toplevel_data| { toplevel_data.app_id = app_id; });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_window_menu(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, seat: &wl_seat::WlSeat, serial: u32,
|
||||||
|
x: i32, y: i32) {
|
||||||
|
let handle = make_toplevel_handle(self.token, resource);
|
||||||
|
self.handler
|
||||||
|
.show_window_menu(evlh, handle, seat, serial, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, seat: &wl_seat::WlSeat, serial: u32) {
|
||||||
|
let handle = make_toplevel_handle(self.token, resource);
|
||||||
|
self.handler.move_(evlh, handle, seat, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, seat: &wl_seat::WlSeat, serial: u32, edges: u32) {
|
||||||
|
let edges =
|
||||||
|
zxdg_toplevel_v6::ResizeEdge::from_raw(edges).unwrap_or(zxdg_toplevel_v6::ResizeEdge::None);
|
||||||
|
let handle = make_toplevel_handle(self.token, resource);
|
||||||
|
self.handler.resize(evlh, handle, seat, serial, edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_max_size(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, width: i32, height: i32) {
|
||||||
|
self.with_surface_toplevel_data(resource, |toplevel_data| {
|
||||||
|
toplevel_data.max_size = (width, height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_min_size(&mut self, _: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, width: i32, height: i32) {
|
||||||
|
self.with_surface_toplevel_data(resource, |toplevel_data| {
|
||||||
|
toplevel_data.min_size = (width, height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_maximized(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||||
|
self.xdg_handle_display_state_change(evlh, resource, Some(true), None, None, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unset_maximized(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||||
|
self.xdg_handle_display_state_change(evlh, resource, Some(false), None, None, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_fullscreen(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, output: Option<&wl_output::WlOutput>) {
|
||||||
|
self.xdg_handle_display_state_change(evlh, resource, None, None, Some(true), output);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unset_fullscreen(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||||
|
self.xdg_handle_display_state_change(evlh, resource, None, None, Some(false), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_minimized(&mut self, evlh: &mut EventLoopHandle, _: &Client,
|
||||||
|
resource: &zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||||
|
self.xdg_handle_display_state_change(evlh, resource, None, Some(true), None, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server_declare_handler!(
|
||||||
|
ShellHandler<U: [Send], R: [Role<ShellSurfaceRole>, Send], H:[CompositorHandler<U, R>, Send], SH: [UserHandler<U,R,H,SD>, Send], SD: [Send]>,
|
||||||
|
zxdg_toplevel_v6::Handler,
|
||||||
|
zxdg_toplevel_v6::ZxdgToplevelV6
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xdg_popup
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl<SD> Destroy<zxdg_popup_v6::ZxdgPopupV6> for XdgShellDestructor<SD> {
|
||||||
|
fn destroy(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 fn send_popup_configure<U, R, H>(token: CompositorToken<U, R, H>, resource: &zxdg_popup_v6::ZxdgPopupV6,
|
||||||
|
configure: PopupConfigure)
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + '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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U, R, H, SH, SD> zxdg_popup_v6::Handler for ShellHandler<U, R, H, SH, SD>
|
||||||
|
where
|
||||||
|
U: Send + 'static,
|
||||||
|
R: Role<ShellSurfaceRole> + Send + 'static,
|
||||||
|
H: CompositorHandler<U, R> + Send + 'static,
|
||||||
|
SH: UserHandler<U, R, H, SD> + Send + 'static,
|
||||||
|
SD: Send + 'static,
|
||||||
|
{
|
||||||
|
fn destroy(&mut self, _: &mut EventLoopHandle, _: &Client, resource: &zxdg_popup_v6::ZxdgPopupV6) {
|
||||||
|
let ptr = resource.get_user_data();
|
||||||
|
let &(ref surface, _, _) = unsafe {
|
||||||
|
&*(ptr as
|
||||||
|
*mut (
|
||||||
|
wl_surface::WlSurface,
|
||||||
|
zxdg_shell_v6::ZxdgShellV6,
|
||||||
|
zxdg_surface_v6::ZxdgSurfaceV6,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
self.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)
|
||||||
|
self.known_popups.retain(|other| {
|
||||||
|
other
|
||||||
|
.get_surface()
|
||||||
|
.map(|s| !s.equals(surface))
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grab(&mut self, evlh: &mut EventLoopHandle, _: &Client, resource: &zxdg_popup_v6::ZxdgPopupV6,
|
||||||
|
seat: &wl_seat::WlSeat, serial: u32) {
|
||||||
|
let handle = make_popup_handle(self.token, resource);
|
||||||
|
self.handler.grab(evlh, handle, seat, serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server_declare_handler!(
|
||||||
|
ShellHandler<U: [Send], R: [Role<ShellSurfaceRole>, Send], H:[CompositorHandler<U, R>, Send], SH: [UserHandler<U,R,H,SD>, Send], SD: [Send]>,
|
||||||
|
zxdg_popup_v6::Handler,
|
||||||
|
zxdg_popup_v6::ZxdgPopupV6
|
||||||
|
);
|
|
@ -67,7 +67,7 @@ use self::pool::{Pool, ResizeError};
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use wayland_server::{Client, Destroy, EventLoopHandle, GlobalHandler, Init, Resource, resource_is_registered};
|
use wayland_server::{resource_is_registered, Client, Destroy, EventLoopHandle, GlobalHandler, Init, Resource};
|
||||||
use wayland_server::protocol::{wl_buffer, wl_shm, wl_shm_pool};
|
use wayland_server::protocol::{wl_buffer, wl_shm, wl_shm_pool};
|
||||||
|
|
||||||
mod pool;
|
mod pool;
|
||||||
|
@ -111,7 +111,9 @@ impl ShmGlobal {
|
||||||
///
|
///
|
||||||
/// This is needed to retrieve the contents of the shm pools and buffers.
|
/// This is needed to retrieve the contents of the shm pools and buffers.
|
||||||
pub fn get_token(&self) -> ShmToken {
|
pub fn get_token(&self) -> ShmToken {
|
||||||
ShmToken { hid: self.handler_id.expect("ShmGlobal was not initialized.") }
|
ShmToken {
|
||||||
|
hid: self.handler_id.expect("ShmGlobal was not initialized."),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +127,7 @@ pub struct ShmToken {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error that can occur when accessing an SHM buffer
|
/// Error that can occur when accessing an SHM buffer
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum BufferAccessError {
|
pub enum BufferAccessError {
|
||||||
/// This buffer is not managed by the SHM handler
|
/// This buffer is not managed by the SHM handler
|
||||||
NotManaged,
|
NotManaged,
|
||||||
|
@ -218,8 +221,11 @@ impl wl_shm::Handler for ShmHandler {
|
||||||
let mmap_pool = match Pool::new(fd, size as usize, self.log.clone()) {
|
let mmap_pool = match Pool::new(fd, size as usize, self.log.clone()) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
shm.post_error(wl_shm::Error::InvalidFd as u32, format!("Failed mmap of fd {}.", fd));
|
shm.post_error(
|
||||||
return
|
wl_shm::Error::InvalidFd as u32,
|
||||||
|
format!("Failed mmap of fd {}.", fd),
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let arc_pool = Box::new(Arc::new(mmap_pool));
|
let arc_pool = Box::new(Arc::new(mmap_pool));
|
||||||
|
|
|
@ -6,7 +6,7 @@ use nix::sys::signal::{self, SigAction, SigHandler, Signal};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::{ONCE_INIT, Once, RwLock};
|
use std::sync::{Once, RwLock, ONCE_INIT};
|
||||||
|
|
||||||
thread_local!(static SIGBUS_GUARD: Cell<(*const MemMap, bool)> = Cell::new((ptr::null_mut(), false)));
|
thread_local!(static SIGBUS_GUARD: Cell<(*const MemMap, bool)> = Cell::new((ptr::null_mut(), false)));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue