shell: shell backend first draft

This commit is contained in:
Victor Berger 2017-09-05 19:50:22 +02:00
parent 70f7b19940
commit 3128585fc9
6 changed files with 1667 additions and 12 deletions

View File

@ -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"

View File

@ -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!()))
} }

54
src/shell/global.rs Normal file
View File

@ -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);
}
}

503
src/shell/mod.rs Normal file
View File

@ -0,0 +1,503 @@
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;
pub struct ShellSurfaceRole {
pub pending_state: ShellSurfacePendingState,
pub window_geometry: Option<Rectangle>,
pub pending_configures: Vec<u32>,
pub configured: bool,
}
#[derive(Copy, Clone, Debug)]
pub struct PositionerState {
pub rect_size: (i32, i32),
pub anchor_rect: Rectangle,
pub anchor_edges: xdg_positioner::Anchor,
pub gravity: xdg_positioner::Gravity,
pub constraint_adjustment: xdg_positioner::ConstraintAdjustment,
pub offset: (i32, i32),
}
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.
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` protocole
Popup(PopupState),
/// This surface was not yet assigned a kind
None,
}
pub struct ToplevelState {
pub parent: Option<wl_surface::WlSurface>,
pub title: String,
pub app_id: String,
pub min_size: (i32, i32),
pub max_size: (i32, i32),
}
impl ToplevelState {
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,
}
}
}
pub struct PopupState {
pub parent: wl_surface::WlSurface,
pub positioner: PositionerState,
}
impl PopupState {
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
}
}
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<F, 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,
}
pub struct ShellClient<SD> {
kind: ShellClientKind,
_data: ::std::marker::PhantomData<*mut SD>,
}
impl<SD> ShellClient<SD> {
pub fn alive(&self) -> bool {
match self.kind {
ShellClientKind::Wl(ref s) => s.status() == Liveness::Alive,
ShellClientKind::Xdg(ref s) => s.status() == Liveness::Alive,
}
}
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(())
}
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),
}
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,
{
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
}
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
pub fn client(&self) -> ShellClient<SD> {
match self.shell_surface {
SurfaceKind::Wl(ref s) => {
let &(_, ref shell) =
unsafe { &*(s.get_user_data() as *mut self::wl_handlers::ShellSurfaceUserData) };
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) };
ShellClient {
kind: ShellClientKind::Xdg(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
}
}
SurfaceKind::XdgPopup(_) => unreachable!(),
}
}
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
}
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
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)
}
}
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,
{
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
}
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
pub fn client(&self) -> ShellClient<SD> {
match self.shell_surface {
SurfaceKind::Wl(ref s) => {
let &(_, ref shell) =
unsafe { &*(s.get_user_data() as *mut self::wl_handlers::ShellSurfaceUserData) };
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) };
ShellClient {
kind: ShellClientKind::Xdg(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
}
}
SurfaceKind::XdgToplevel(_) => unreachable!(),
}
}
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(())
}
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!(),
}
}
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
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)
}
}
pub struct ToplevelConfigure {
pub size: Option<(i32, i32)>,
pub states: Vec<zxdg_toplevel_v6::State>,
pub serial: u32,
}
pub struct PopupConfigure {
pub position: (i32, i32),
pub size: (i32, i32),
pub serial: u32,
}
pub trait Handler<U, R, H, SD> {
fn new_client(&mut self, evlh: &mut EventLoopHandle, client: ShellClient<SD>);
fn client_pong(&mut self, evlh: &mut EventLoopHandle, client: ShellClient<SD>);
fn new_toplevel(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>)
-> ToplevelConfigure;
fn new_popup(&mut self, evlh: &mut EventLoopHandle, surface: PopupSurface<U, R, H, SD>)
-> PopupConfigure;
fn move_(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>,
seat: &wl_seat::WlSeat, serial: u32);
fn resize(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>,
seat: &wl_seat::WlSeat, serial: u32, edges: zxdg_toplevel_v6::ResizeEdge);
fn grab(&mut self, evlh: &mut EventLoopHandle, surface: PopupSurface<U, R, H, SD>,
seat: &wl_seat::WlSeat, serial: u32);
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;
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);
}

365
src/shell/wl_handlers.rs Normal file
View File

@ -0,0 +1,365 @@
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, Init, 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>) };
}
}
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, evqh: &mut EventLoopHandle, client: &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 _,
);
evqh.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) };
}
}
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, evqh: &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(evqh, 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, evqh: &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(evqh, 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, evqh: &mut EventLoopHandle, client: &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(evqh, make_shell_client(shell));
}
}
fn move_(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32) {
let handle = make_toplevel_handle(self.token, resource);
self.handler.move_(evqh, handle, seat, serial);
}
fn resize(&mut self, evqh: &mut EventLoopHandle, client: &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(evqh, handle, seat, serial, edges);
}
fn set_toplevel(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &wl_shell_surface::WlShellSurface) {
self.wl_ensure_toplevel(evqh, resource);
self.wl_handle_display_state_change(evqh, resource, Some(false), Some(false), Some(false), None)
}
fn set_transient(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &wl_shell_surface::WlShellSurface, parent: &wl_surface::WlSurface, x: i32,
y: i32, flags: wl_shell_surface::Transient) {
self.wl_ensure_toplevel(evqh, 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(evqh, resource, Some(false), Some(false), Some(false), None)
}
fn set_fullscreen(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &wl_shell_surface::WlShellSurface,
method: wl_shell_surface::FullscreenMethod, framerate: u32,
output: Option<&wl_output::WlOutput>) {
self.wl_ensure_toplevel(evqh, resource);
self.wl_handle_display_state_change(evqh, resource, Some(false), Some(false), Some(true), output)
}
fn set_popup(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32,
parent: &wl_surface::WlSurface, x: i32, y: i32, flags: 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(evqh, handle);
send_popup_configure(resource, configure);
}
fn set_maximized(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &wl_shell_surface::WlShellSurface, output: Option<&wl_output::WlOutput>) {
self.wl_ensure_toplevel(evqh, resource);
self.wl_handle_display_state_change(evqh, resource, Some(true), Some(false), Some(false), output)
}
fn set_title(&mut self, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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
);

731
src/shell/xdg_handlers.rs Normal file
View File

@ -0,0 +1,731 @@
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, Init, 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>) };
}
}
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, evqh: &mut EventLoopHandle, client: &Client,
resource: &zxdg_shell_v6::ZxdgShellV6) {
}
fn create_positioner(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &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 _);
evqh.register_with_destructor::<_, Self, XdgShellDestructor<SD>>(&id, self.my_id);
}
fn get_xdg_surface(&mut self, evqh: &mut EventLoopHandle, client: &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 _,
);
evqh.register_with_destructor::<_, Self, XdgShellDestructor<SD>>(&id, self.my_id);
}
fn pong(&mut self, evqh: &mut EventLoopHandle, client: &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(evqh, 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) };
}
}
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, evqh: &mut EventLoopHandle, client: &Client,
resource: &zxdg_positioner_v6::ZxdgPositionerV6) {
}
fn set_size(&mut self, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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),
)
};
}
}
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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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 _);
evqh.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(evqh, handle);
send_toplevel_configure(self.token, &id, configure);
}
fn get_popup(&mut self, evqh: &mut EventLoopHandle, client: &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 { &*(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 _);
evqh.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(evqh, handle);
send_popup_configure(self.token, &id, configure);
}
fn set_window_geometry(&mut self, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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) };
}
}
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, evqh: &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(evqh, 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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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(evqh, handle, seat, serial, x, y);
}
fn move_(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, seat: &wl_seat::WlSeat, serial: u32) {
let handle = make_toplevel_handle(self.token, resource);
self.handler.move_(evqh, handle, seat, serial);
}
fn resize(&mut self, evqh: &mut EventLoopHandle, client: &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(evqh, handle, seat, serial, edges);
}
fn set_max_size(&mut self, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &Client,
resource: &zxdg_toplevel_v6::ZxdgToplevelV6) {
self.xdg_handle_display_state_change(evqh, resource, Some(true), None, None, None);
}
fn unset_maximized(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &zxdg_toplevel_v6::ZxdgToplevelV6) {
self.xdg_handle_display_state_change(evqh, resource, Some(false), None, None, None);
}
fn set_fullscreen(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &zxdg_toplevel_v6::ZxdgToplevelV6, output: Option<&wl_output::WlOutput>) {
self.xdg_handle_display_state_change(evqh, resource, None, None, Some(true), output);
}
fn unset_fullscreen(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &zxdg_toplevel_v6::ZxdgToplevelV6) {
self.xdg_handle_display_state_change(evqh, resource, None, None, Some(false), None);
}
fn set_minimized(&mut self, evqh: &mut EventLoopHandle, client: &Client,
resource: &zxdg_toplevel_v6::ZxdgToplevelV6) {
self.xdg_handle_display_state_change(evqh, 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) };
}
}
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, evqh: &mut EventLoopHandle, client: &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, evqh: &mut EventLoopHandle, client: &Client, resource: &zxdg_popup_v6::ZxdgPopupV6,
seat: &wl_seat::WlSeat, serial: u32) {
let handle = make_popup_handle(self.token, resource);
self.handler.grab(evqh, 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
);