use std::{ptr::NonNull, cell::Cell, rc::{Rc, Weak}};
use libc::{self, size_t, int16_t, uint16_t};
use wayland_sys::server::WAYLAND_SERVER_HANDLE;
use wlroots_sys::{pid_t, wl_event_source, wlr_xwayland_surface, xcb_atom_t, xcb_window_t,
wlr_xwayland_surface_configure, wlr_xwayland_surface_activate};
use {area::{Area, Size, Origin},
compositor,
surface::{self, InternalState},
xwayland,
utils::{self, HandleErr, HandleResult, Handleable, c_to_rust_string}};
pub use xwayland::hints::{Hints, SizeHints};
pub type Handle = utils::Handle<(), wlr_xwayland_surface, Surface>;
#[allow(unused_variables)]
pub trait Handler {
fn destroyed(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
fn on_configure(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle,
configure: &xwayland::event::Configure) {
}
fn on_move(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle,
event: &xwayland::event::Move) {}
fn on_resize(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle,
event: &xwayland::event::Resize) {}
fn on_maximize(&mut self, compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
fn on_fullscreen(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
fn on_map(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle)
-> Option<Box<surface::Handler>> { None }
fn on_unmap(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
fn title_set(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
fn class_set(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
fn parent_set(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
fn pid_set(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
fn window_type_set(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
fn ping_timeout(&mut self,
compositor_handle: compositor::Handle,
surface_handle: Option<surface::Handle>,
xwayland_surface_handle: Handle) {}
}
wayland_listener!(pub(crate) Shell, (Surface, Option<Box<Handler>>), [
destroy_listener => destroy_notify: |this: &mut Shell, data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.destroyed(compositor, surface, shell_surface.weak_reference());
let surface_ptr = data as *mut wlr_xwayland_surface;
let shell_state_ptr = (*surface_ptr).data as *mut State;
if let Some(shell_ptr) = (*shell_state_ptr).shell {
Box::from_raw(shell_ptr.as_ptr());
}
};
request_configure_listener => request_configure_notify: |this: &mut Shell,
data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
let event = xwayland::event::Configure::from_ptr(data as *mut _);
manager.on_configure(compositor,
surface,
shell_surface.weak_reference(),
&event);
};
request_move_listener => request_move_notify: |this: &mut Shell,
data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
let event = xwayland::event::Move::from_ptr(data as *mut _);
manager.on_move(compositor,
surface,
shell_surface.weak_reference(),
&event);
};
request_resize_listener => request_resize_notify: |this: &mut Shell,
data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
let event = xwayland::event::Resize::from_ptr(data as *mut _);
manager.on_resize(compositor,
surface,
shell_surface.weak_reference(),
&event);
};
request_maximize_listener => request_maximize_notify: |this: &mut Shell,
_data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.on_maximize(compositor,
surface,
shell_surface.weak_reference());
};
request_fullscreen_listener => request_fullscreen_notify: |this: &mut Shell,
_data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.on_fullscreen(compositor,
surface,
shell_surface.weak_reference());
};
map_listener => map_notify: |this: &mut Shell, _data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
let surface_handler = manager.on_map(compositor,
surface,
shell_surface.weak_reference());
if let Some(surface_handler) = surface_handler {
let surface_state = (*(*shell_surface.shell_surface.as_ptr()).surface).data as *mut InternalState;
(*(*surface_state).surface.unwrap().as_ptr()).data().1 = surface_handler;
}
};
unmap_listener => unmap_notify: |this: &mut Shell, _data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.on_unmap(compositor,
surface,
shell_surface.weak_reference());
};
set_title_listener => set_title_notify: |this: &mut Shell, _data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.title_set(compositor,
surface,
shell_surface.weak_reference());
};
set_class_listener => set_class_notify: |this: &mut Shell, _data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.class_set(compositor,
surface,
shell_surface.weak_reference());
};
set_parent_listener => set_parent_notify: |this: &mut Shell, _data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.parent_set(compositor,
surface,
shell_surface.weak_reference());
};
set_pid_listener => set_pid_notify: |this: &mut Shell, _data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.pid_set(compositor,
surface,
shell_surface.weak_reference());
};
set_window_type_listener => set_window_type_notify: |this: &mut Shell,
_data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.window_type_set(compositor,
surface,
shell_surface.weak_reference());
};
ping_timeout_listener => ping_timeout_notify: |this: &mut Shell,
_data: *mut libc::c_void,|
unsafe {
let (ref mut shell_surface, ref mut manager) = match &mut this.data {
(_, None) => return,
(ss, Some(manager)) => (ss, manager)
};
let surface = shell_surface.surface();
let compositor = match compositor::handle() {
Some(handle) => handle,
None => return
};
manager.ping_timeout(compositor,
surface,
shell_surface.weak_reference());
};
]);
pub(crate) struct State {
pub(crate) shell: Option<NonNull<Shell>>,
handle: Weak<Cell<bool>>
}
#[derive(Debug)]
pub struct Surface {
liveliness: Rc<Cell<bool>>,
shell_surface: NonNull<wlr_xwayland_surface>
}
impl Surface {
pub(crate) unsafe fn new(shell_surface: *mut wlr_xwayland_surface) -> Self {
let shell_surface = NonNull::new(shell_surface)
.expect("Shell Surface pointer was null");
if !(*shell_surface.as_ptr()).data.is_null() {
panic!("Shell surface has already been created");
}
let liveliness = Rc::new(Cell::new(false));
let state = Box::new(State { shell: None, handle: Rc::downgrade(&liveliness) });
(*shell_surface.as_ptr()).data = Box::into_raw(state) as *mut _;
Surface { liveliness,
shell_surface }
}
pub fn window_id(&self) -> xcb_window_t {
unsafe { (*self.shell_surface.as_ptr()).window_id }
}
pub fn surface_id(&self) -> u32 {
unsafe { (*self.shell_surface.as_ptr()).surface_id }
}
pub fn surface(&self) -> Option<surface::Handle> {
unsafe {
let surface = (*self.shell_surface.as_ptr()).surface;
if surface.is_null() {
None
} else {
Some(surface::Handle::from_ptr((*self.shell_surface.as_ptr()).surface))
}
}
}
pub fn coords(&self) -> (int16_t, int16_t) {
unsafe { ((*self.shell_surface.as_ptr()).x, (*self.shell_surface.as_ptr()).y) }
}
pub fn dimensions(&self) -> (uint16_t, uint16_t) {
unsafe { ((*self.shell_surface.as_ptr()).width, (*self.shell_surface.as_ptr()).height) }
}
pub fn saved_dimensions(&self) -> (uint16_t, uint16_t) {
unsafe { ((*self.shell_surface.as_ptr()).saved_width, (*self.shell_surface.as_ptr()).saved_height) }
}
pub fn override_redirect(&self) -> bool {
unsafe { (*self.shell_surface.as_ptr()).override_redirect }
}
pub fn mapped(&self) -> bool {
unsafe { (*self.shell_surface.as_ptr()).mapped }
}
pub fn title(&self) -> Option<String> {
unsafe { c_to_rust_string((*self.shell_surface.as_ptr()).title) }
}
pub fn class(&self) -> Option<String> {
unsafe { c_to_rust_string((*self.shell_surface.as_ptr()).class) }
}
pub fn instance(&self) -> Option<String> {
unsafe { c_to_rust_string((*self.shell_surface.as_ptr()).instance) }
}
pub fn pid(&self) -> pid_t {
unsafe { (*self.shell_surface.as_ptr()).pid }
}
pub fn parent(&self) -> Option<Handle> {
unsafe {
let parent_ptr = (*self.shell_surface.as_ptr()).parent;
if parent_ptr.is_null() {
None
} else {
Some(Handle::from_ptr(parent_ptr))
}
}
}
pub fn children(&self) -> Vec<Handle> {
unsafe {
let mut result = Vec::new();
wl_list_for_each!((*self.shell_surface.as_ptr()).children,
parent_link,
(child: wlr_xwayland_surface) => {
result.push(Handle::from_ptr(child))
});
result
}
}
pub unsafe fn window_type(&self) -> *mut xcb_atom_t {
(*self.shell_surface.as_ptr()).window_type
}
pub unsafe fn window_type_len(&self) -> size_t {
(*self.shell_surface.as_ptr()).window_type_len
}
pub unsafe fn protocols(&self) -> *mut xcb_atom_t {
(*self.shell_surface.as_ptr()).protocols
}
pub unsafe fn protocols_len(&self) -> size_t {
(*self.shell_surface.as_ptr()).protocols_len
}
pub fn decorations(&self) -> u32 {
unsafe { (*self.shell_surface.as_ptr()).decorations }
}
pub fn hints<'surface>(&'surface self) -> xwayland::surface::Hints<'surface> {
unsafe { xwayland::surface::Hints::from_ptr((*self.shell_surface.as_ptr()).hints) }
}
pub fn size_hints<'surface>(&'surface self) -> xwayland::surface::SizeHints<'surface> {
unsafe { xwayland::surface::SizeHints::from_ptr((*self.shell_surface.as_ptr()).size_hints) }
}
pub fn hints_urgency(&self) -> u32 {
unsafe { (*self.shell_surface.as_ptr()).hints_urgency }
}
pub fn pinging(&self) -> bool {
unsafe { (*self.shell_surface.as_ptr()).pinging }
}
pub unsafe fn ping_timer(&self) -> *mut wl_event_source {
(*self.shell_surface.as_ptr()).ping_timer
}
pub fn fullscreen(&self) -> bool {
unsafe { (*self.shell_surface.as_ptr()).fullscreen }
}
pub fn maximized_vert(&self) -> bool {
unsafe { (*self.shell_surface.as_ptr()).maximized_vert }
}
pub fn maximized_horz(&self) -> bool {
unsafe { (*self.shell_surface.as_ptr()).maximized_horz }
}
pub fn has_alpha(&self) -> bool {
unsafe { (*self.shell_surface.as_ptr()).has_alpha }
}
pub fn geometry(&self) -> Area {
let (x, y, width, height) = unsafe {
(
(*self.shell_surface.as_ptr()).x as i32,
(*self.shell_surface.as_ptr()).y as i32,
(*self.shell_surface.as_ptr()).width as i32,
(*self.shell_surface.as_ptr()).height as i32
)
};
Area {
origin: Origin { x, y },
size: Size { width, height }
}
}
pub fn configure(&self, x: i16, y: i16, width: u16, height: u16) {
unsafe {
wlr_xwayland_surface_configure(self.shell_surface.as_ptr(), x, y, width, height);
}
}
pub fn set_activated(&self, active: bool) {
unsafe { wlr_xwayland_surface_activate(self.shell_surface.as_ptr(), active); }
}
}
impl Drop for Surface {
fn drop(&mut self) {
if Rc::strong_count(&self.liveliness) > 1 {
return
}
unsafe {
Box::from_raw((*self.shell_surface.as_ptr()).data as *mut State);
}
}
}
impl Handleable<(), wlr_xwayland_surface> for Surface {
#[doc(hidden)]
unsafe fn from_ptr(shell_surface: *mut wlr_xwayland_surface) -> Option<Self> {
let shell_surface = NonNull::new(shell_surface)?;
let data = (*shell_surface.as_ptr()).data as *mut State;
let liveliness = (*data).handle.upgrade().unwrap();
Some(Surface { liveliness, shell_surface })
}
#[doc(hidden)]
unsafe fn as_ptr(&self) -> *mut wlr_xwayland_surface {
self.shell_surface.as_ptr()
}
#[doc(hidden)]
unsafe fn from_handle(handle: &Handle) -> HandleResult<Self> {
let liveliness = handle.handle
.upgrade()
.ok_or_else(|| HandleErr::AlreadyDropped)?;
Ok(Surface { liveliness,
shell_surface: handle.as_non_null() })
}
fn weak_reference(&self) -> Handle {
Handle { ptr: self.shell_surface,
handle: Rc::downgrade(&self.liveliness),
_marker: std::marker::PhantomData,
data: Some(()) }
}
}
impl Drop for Shell {
fn drop(&mut self) {
unsafe {
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.destroy_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.request_configure_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.request_move_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.request_resize_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.request_maximize_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.request_fullscreen_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.map_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.unmap_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.set_title_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.set_class_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.set_parent_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.set_pid_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.set_window_type_listener() as *mut _ as _);
ffi_dispatch!(WAYLAND_SERVER_HANDLE,
wl_list_remove,
self.ping_timeout_listener() as *mut _ as _);
}
}
}