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