Add Wayland layer-shell wrapper, behind layer-shell feature

Requires patched GTK4 from
https://github.com/pop-os/gtk4/tree/layer-shell_impish.
This commit is contained in:
Ian Douglas Scott 2022-01-05 13:28:49 -08:00
parent cfd1edcde3
commit 6195ab2db9
5 changed files with 753 additions and 1 deletions

33
src/deref_cell.rs Normal file
View file

@ -0,0 +1,33 @@
#![allow(dead_code)]
use once_cell::unsync::OnceCell;
/// Wrapper around `OnceCell` implementing `Deref`, and thus also panicking
/// when not set (or set twice).
///
/// To be used in place of `gtk::TemplateChild`, but without xml.
pub struct DerefCell<T>(OnceCell<T>);
impl<T> DerefCell<T> {
#[track_caller]
pub fn set(&self, value: T) {
if self.0.set(value).is_err() {
panic!("Initialized twice");
}
}
}
impl<T> Default for DerefCell<T> {
fn default() -> Self {
Self(OnceCell::default())
}
}
impl<T> std::ops::Deref for DerefCell<T> {
type Target = T;
#[track_caller]
fn deref(&self) -> &T {
self.0.get().unwrap()
}
}

View file

@ -1 +1,6 @@
mod deref_cell;
#[cfg(feature = "layer-shell")]
pub mod wayland;
#[cfg(feature = "layer-shell")]
mod wayland_custom_surface;
pub mod x;

563
src/wayland.rs Normal file
View file

@ -0,0 +1,563 @@
// TODO: scale-factor?
use derivative::Derivative;
use gdk4_wayland::prelude::*;
use gtk4::{
cairo, gdk,
glib::{self, clone, subclass::prelude::*, translate::*},
gsk::{self, traits::RendererExt},
prelude::*,
subclass::prelude::*,
};
use std::{
cell::{Cell, RefCell},
os::raw::c_int,
ptr,
rc::Rc,
};
use wayland_client::{event_enum, sys::client::wl_proxy, Filter, GlobalManager, Main, Proxy};
use wayland_protocols::{
wlr::unstable::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1},
xdg_shell::client::xdg_popup,
};
use crate::{deref_cell::DerefCell, wayland_custom_surface::WaylandCustomSurface};
pub use wayland_protocols::wlr::unstable::layer_shell::v1::client::{
zwlr_layer_shell_v1::Layer,
zwlr_layer_surface_v1::{Anchor, KeyboardInteractivity},
};
event_enum!(
Events |
LayerSurface => zwlr_layer_surface_v1::ZwlrLayerSurfaceV1
);
struct CosmicWaylandDisplay {
event_queue: RefCell<wayland_client::EventQueue>,
wayland_display: wayland_client::Display,
wlr_layer_shell: Option<Main<zwlr_layer_shell_v1::ZwlrLayerShellV1>>,
}
impl CosmicWaylandDisplay {
fn for_display(display: &gdk4_wayland::WaylandDisplay) -> Rc<Self> {
const DATA_KEY: &str = "cosmic-wayland-display";
// `GdkWaylandDisplay` already associated with a `CosmicWaylandDisplay`
if let Some(data) = unsafe { display.data::<Rc<Self>>(DATA_KEY) } {
return unsafe { data.as_ref() }.clone();
}
let wayland_display = unsafe {
wayland_client::Display::from_external_display(
display.wl_display().as_ref().c_ptr() as *mut _
)
}; // XXX is this sound?
let mut event_queue = wayland_display.create_event_queue();
let attached_display = wayland_display.attach(event_queue.token());
let globals = GlobalManager::new(&attached_display);
event_queue.sync_roundtrip(&mut (), |_, _, _| {}).unwrap();
let wlr_layer_shell = globals
.instantiate_exact::<zwlr_layer_shell_v1::ZwlrLayerShellV1>(1)
.ok();
let cosmic_wayland_display = Rc::new(Self {
event_queue: RefCell::new(event_queue),
wayland_display,
wlr_layer_shell,
});
unsafe { display.set_data(DATA_KEY, cosmic_wayland_display.clone()) };
// XXX Efficient way to poll?
// XXX unwrap?
glib::idle_add_local(
clone!(@weak cosmic_wayland_display => @default-return Continue(false), move || {
cosmic_wayland_display.wayland_display.flush().unwrap();
let mut event_queue = cosmic_wayland_display.event_queue.borrow_mut();
if let Some(guard) = event_queue.prepare_read() {
guard.read_events().unwrap();
}
event_queue.dispatch_pending(&mut (), |_, _, _| {}).unwrap();
Continue(true)
}),
);
cosmic_wayland_display
}
}
#[derive(Derivative)]
#[derivative(Default)]
pub struct LayerShellWindowInner {
display: DerefCell<gdk4_wayland::WaylandDisplay>,
surface: RefCell<Option<WaylandCustomSurface>>,
renderer: RefCell<Option<gsk::Renderer>>,
wlr_layer_surface: RefCell<Option<Main<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1>>>,
constraint_solver: DerefCell<ConstraintSolver>,
child: RefCell<Option<gtk4::Widget>>,
monitor: DerefCell<Option<gdk4_wayland::WaylandMonitor>>,
#[derivative(Default(value = "Cell::new(Layer::Background)"))]
layer: Cell<Layer>,
#[derivative(Default(value = "Cell::new(Anchor::empty())"))]
anchor: Cell<Anchor>,
exclusive_zone: Cell<i32>,
margin: Cell<(i32, i32, i32, i32)>,
#[derivative(Default(value = "Cell::new(KeyboardInteractivity::None)"))]
keyboard_interactivity: Cell<KeyboardInteractivity>,
namespace: DerefCell<String>,
focus_widget: RefCell<Option<gtk4::Widget>>,
}
#[glib::object_subclass]
impl ObjectSubclass for LayerShellWindowInner {
const NAME: &'static str = "S76CosmicLayerShellWindow";
type ParentType = gtk4::Widget;
type Interfaces = (gtk4::Native, gtk4::Root);
type Type = LayerShellWindow;
}
impl ObjectImpl for LayerShellWindowInner {
fn constructed(&self, obj: &Self::Type) {
self.display
.set(gdk::Display::default().unwrap().downcast().unwrap()); // XXX any issue unwrapping?
self.constraint_solver.set(glib::Object::new(&[]).unwrap());
obj.add_css_class("background");
}
}
impl WidgetImpl for LayerShellWindowInner {
fn realize(&self, widget: &Self::Type) {
let surface = WaylandCustomSurface::new(&*self.display);
surface.set_get_popup_func(Some(Box::new(clone!(@strong widget => move |_surface, popup| {
if let Some(wlr_layer_surface) = widget.inner().wlr_layer_surface.borrow().as_ref() {
let xdg_popup: xdg_popup::XdgPopup =
unsafe { Proxy::from_c_ptr(gdk_wayland_popup_get_xdg_popup(popup.to_glib_none().0)).into() };
wlr_layer_surface.get_popup(&xdg_popup);
}
true
}))));
widget.layer_shell_init(&surface);
let widget_ptr: *mut _ = widget.to_glib_none().0;
let surface_ptr: *mut _ = surface.to_glib_none().0;
unsafe { gdk_surface_set_widget(surface_ptr as *mut _, widget_ptr as *mut _) };
*self.surface.borrow_mut() = Some(surface.clone());
surface.connect_render(move |_surface, region| {
unsafe {
gtk_widget_render(
widget_ptr as *mut _,
surface_ptr as *mut _,
region.to_glib_none().0,
)
};
true
});
surface.connect_event(|_, event| {
unsafe { gtk_main_do_event(event.to_glib_none().0) };
true
});
self.parent_realize(widget);
*self.renderer.borrow_mut() =
Some(gsk::Renderer::for_surface(surface.upcast_ref()).unwrap()); // XXX unwrap?
unsafe { gtk4::ffi::gtk_native_realize(widget_ptr as *mut _) };
}
fn unrealize(&self, widget: &Self::Type) {
let widget_ptr: *mut Self::Instance = widget.to_glib_none().0;
unsafe { gtk4::ffi::gtk_native_unrealize(widget_ptr as *mut _) };
self.parent_unrealize(widget);
if let Some(renderer) = self.renderer.borrow_mut().take() {
renderer.unrealize();
}
if let Some(surface) = self.surface.borrow().as_ref() {
let surface_ptr: *mut _ = surface.to_glib_none().0;
unsafe { gdk_surface_set_widget(surface_ptr as *mut _, ptr::null_mut()) };
}
}
fn map(&self, widget: &Self::Type) {
if let Some(surface) = self.surface.borrow().as_ref() {
let width = widget.measure(gtk4::Orientation::Horizontal, -1).1;
let height = widget.measure(gtk4::Orientation::Vertical, width).1;
widget.set_size(width as u32, height as u32);
surface.present(width, height);
}
self.parent_map(widget);
if let Some(child) = self.child.borrow().as_ref() {
child.map();
}
}
fn unmap(&self, widget: &Self::Type) {
self.parent_unmap(widget);
if let Some(surface) = self.surface.borrow().as_ref() {
surface.hide();
}
if let Some(child) = self.child.borrow().as_ref() {
child.unmap();
}
}
fn measure(
&self,
_widget: &Self::Type,
orientation: gtk4::Orientation,
for_size: i32,
) -> (i32, i32, i32, i32) {
if let Some(child) = self.child.borrow().as_ref() {
child.measure(orientation, for_size)
} else {
(0, 0, 0, 0)
}
}
fn size_allocate(&self, _widget: &Self::Type, width: i32, height: i32, baseline: i32) {
if let Some(child) = self.child.borrow().as_ref() {
child.allocate(width, height, baseline, None)
}
}
fn show(&self, widget: &Self::Type) {
widget.realize();
self.parent_show(widget);
widget.map();
}
fn hide(&self, widget: &Self::Type) {
self.parent_hide(widget);
widget.unmap();
}
}
// TODO: Move into gtk4-rs when support merged/released in gtk
unsafe impl IsImplementable<LayerShellWindowInner> for gtk4::Native {
fn interface_init(iface: &mut glib::Interface<Self>) {
let iface = unsafe { &mut *(iface as *mut _ as *mut GtkNativeInterface) };
iface.get_surface = Some(get_surface);
iface.get_renderer = Some(get_renderer);
iface.get_surface_transform = Some(get_surface_transform);
iface.layout = Some(layout);
}
fn instance_init(_instance: &mut glib::subclass::InitializingObject<LayerShellWindowInner>) {}
}
// TODO: Move into gtk4-rs when support merged/released in gtk
unsafe impl IsImplementable<LayerShellWindowInner> for gtk4::Root {
fn interface_init(iface: &mut glib::Interface<Self>) {
let iface = unsafe { &mut *(iface as *mut _ as *mut GtkRootInterface) };
iface.get_display = Some(get_display);
iface.get_constraint_solver = Some(get_constraint_solver);
iface.get_focus = Some(get_focus);
iface.set_focus = Some(set_focus);
}
fn instance_init(_instance: &mut glib::subclass::InitializingObject<LayerShellWindowInner>) {}
}
glib::wrapper! {
pub struct LayerShellWindow(ObjectSubclass<LayerShellWindowInner>)
@extends gtk4::Widget, @implements gtk4::Accessible, gtk4::Buildable, gtk4::ConstraintTarget, gtk4::Native, gtk4::Root;
}
// TODO handle configure/destroy
// TODO presumably call destroy() when appropriate?
// What do wayland-client types do when associated connection is gone? Panic? UB?
impl LayerShellWindow {
pub fn new(
monitor: Option<&gdk4_wayland::WaylandMonitor>,
layer: Layer,
namespace: &str,
) -> Self {
let obj: Self = glib::Object::new(&[]).unwrap();
obj.inner().monitor.set(monitor.cloned());
obj.inner().layer.set(layer);
obj.inner().namespace.set(namespace.to_string());
obj
}
fn inner(&self) -> &LayerShellWindowInner {
LayerShellWindowInner::from_instance(self)
}
pub fn set_child<T: IsA<gtk4::Widget>>(&self, w: Option<&T>) {
let mut child = self.inner().child.borrow_mut();
if let Some(child) = child.take() {
child.unparent();
}
if let Some(w) = w {
w.set_parent(self);
}
*child = w.map(|x| x.clone().upcast());
}
fn layer_shell_init(&self, surface: &WaylandCustomSurface) {
let width = self.measure(gtk4::Orientation::Horizontal, -1).1;
let height = self.measure(gtk4::Orientation::Vertical, width).1;
// XXX needed for wl_surface to exist
surface.present(width, height);
let wl_surface = surface.wl_surface();
let cosmic_wayland_display = CosmicWaylandDisplay::for_display(&*self.inner().display);
let wlr_layer_shell = match cosmic_wayland_display.wlr_layer_shell.as_ref() {
Some(wlr_layer_shell) => wlr_layer_shell,
None => {
eprintln!("Error: Layer shell not supported by compositor");
return;
}
};
let output = self.inner().monitor.as_ref().map(|x| x.wl_output());
let layer = self.layer();
let namespace = self.namespace().to_string();
let wlr_layer_surface =
wlr_layer_shell.get_layer_surface(&wl_surface, output.as_ref(), layer, namespace);
wlr_layer_surface.set_anchor(self.anchor());
wlr_layer_surface.set_exclusive_zone(self.exclusive_zone());
let margin = self.margin();
wlr_layer_surface.set_margin(margin.0, margin.1, margin.2, margin.3);
wlr_layer_surface.set_keyboard_interactivity(self.keyboard_interactivity());
wlr_layer_surface.set_size(width as u32, height as u32);
let filter = Filter::new(
clone!(@strong self as self_ => move |event, _, _| match event {
Events::LayerSurface { event, object } => match event {
zwlr_layer_surface_v1::Event::Configure {
serial,
width: _,
height: _,
} => {
// TODO: should size window to match `width`/`height`
object.ack_configure(serial);
}
zwlr_layer_surface_v1::Event::Closed => {}
_ => {}
},
}),
);
wlr_layer_surface.assign(filter);
wl_surface.commit();
cosmic_wayland_display
.event_queue
.borrow_mut()
.sync_roundtrip(&mut (), |_, _, _| {})
.unwrap();
*self.inner().wlr_layer_surface.borrow_mut() = Some(wlr_layer_surface);
}
fn set_size(&self, width: u32, height: u32) {
if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() {
wlr_layer_surface.set_size(width, height);
};
}
pub fn anchor(&self) -> Anchor {
self.inner().anchor.get()
}
pub fn set_anchor(&self, anchor: Anchor) {
if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() {
wlr_layer_surface.set_anchor(anchor);
};
self.inner().anchor.set(anchor);
}
pub fn exclusive_zone(&self) -> i32 {
self.inner().exclusive_zone.get()
}
pub fn set_exclusive_zone(&self, zone: i32) {
if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() {
wlr_layer_surface.set_exclusive_zone(zone);
};
self.inner().exclusive_zone.set(zone);
}
pub fn margin(&self) -> (i32, i32, i32, i32) {
self.inner().margin.get()
}
pub fn set_margin(&self, top: i32, right: i32, bottom: i32, left: i32) {
if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() {
wlr_layer_surface.set_margin(top, right, bottom, left);
};
self.inner().margin.set((top, right, bottom, left));
}
pub fn keyboard_interactivity(&self) -> KeyboardInteractivity {
self.inner().keyboard_interactivity.get()
}
pub fn set_keyboard_interactivity(&self, interactivity: KeyboardInteractivity) {
if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() {
wlr_layer_surface.set_keyboard_interactivity(interactivity);
};
self.inner().keyboard_interactivity.set(interactivity);
}
pub fn layer(&self) -> Layer {
self.inner().layer.get()
}
pub fn set_layer(&self, layer: Layer) {
if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() {
wlr_layer_surface.set_layer(layer);
};
self.inner().layer.set(layer);
}
pub fn namespace(&self) -> &str {
self.inner().namespace.as_str()
}
}
pub struct GtkConstraintSolver {
_private: [u8; 0],
}
// XXX needs to be public in gtk
#[link(name = "gtk-4")]
extern "C" {
pub fn gtk_constraint_solver_get_type() -> glib::ffi::GType;
pub fn gdk_surface_set_widget(surface: *mut gdk::ffi::GdkSurface, widget: glib::ffi::gpointer);
pub fn _gtk_widget_set_visible_flag(
widget: *mut gtk4::ffi::GtkWidget,
visible: glib::ffi::gboolean,
);
pub fn gtk_widget_render(
widget: *mut gtk4::ffi::GtkWidget,
surface: *mut gdk::ffi::GdkSurface,
region: *const cairo::ffi::cairo_region_t,
);
pub fn gtk_main_do_event(event: *mut gdk::ffi::GdkEvent);
// Added API
pub fn gdk_wayland_popup_get_xdg_popup(
popup: *mut gdk4_wayland::ffi::GdkWaylandPopup,
) -> *mut wl_proxy;
}
glib::wrapper! {
pub struct ConstraintSolver(Object<GtkConstraintSolver>);
match fn {
type_ => || gtk_constraint_solver_get_type(),
}
}
pub struct GtkNativeInterface {
pub g_iface: gobject_sys::GTypeInterface,
pub get_surface:
Option<unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkNative) -> *mut gdk::ffi::GdkSurface>,
pub get_renderer: Option<
unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkNative) -> *mut gsk::ffi::GskRenderer,
>,
pub get_surface_transform:
Option<unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkNative, x: *mut f64, y: *mut f64)>,
pub layout:
Option<unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkNative, width: c_int, height: c_int)>,
}
pub struct GtkRootInterface {
pub g_iface: gobject_sys::GTypeInterface,
pub get_display:
Option<unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkRoot) -> *mut gdk::ffi::GdkDisplay>,
pub get_constraint_solver:
Option<unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkRoot) -> *mut GtkConstraintSolver>,
pub get_focus:
Option<unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkRoot) -> *mut gtk4::ffi::GtkWidget>,
pub set_focus: Option<
unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkRoot, focus: *mut gtk4::ffi::GtkWidget),
>,
}
unsafe extern "C" fn get_surface(native: *mut gtk4::ffi::GtkNative) -> *mut gdk::ffi::GdkSurface {
let instance = &*(native as *mut <LayerShellWindowInner as ObjectSubclass>::Instance);
let imp = instance.impl_();
imp.surface.borrow().as_ref().map_or(ptr::null_mut(), |x| {
x.upcast_ref::<gdk::Surface>().to_glib_none().0
})
}
unsafe extern "C" fn get_renderer(native: *mut gtk4::ffi::GtkNative) -> *mut gsk::ffi::GskRenderer {
let instance = &*(native as *mut <LayerShellWindowInner as ObjectSubclass>::Instance);
let imp = instance.impl_();
imp.renderer
.borrow()
.as_ref()
.map_or(ptr::null_mut(), |x| x.to_glib_none().0)
}
unsafe extern "C" fn get_surface_transform(
_native: *mut gtk4::ffi::GtkNative,
x: *mut f64,
y: *mut f64,
) {
// TODO: add css logic like `GtkWindow` has
*x = 0.;
*y = 0.;
}
unsafe extern "C" fn layout(native: *mut gtk4::ffi::GtkNative, width: c_int, height: c_int) {
// TODO: `GtkWindow` has more here
gtk4::ffi::gtk_widget_allocate(native as *mut _, width, height, -1, ptr::null_mut());
}
unsafe extern "C" fn get_display(root: *mut gtk4::ffi::GtkRoot) -> *mut gdk::ffi::GdkDisplay {
let instance = &*(root as *mut <LayerShellWindowInner as ObjectSubclass>::Instance);
let imp = instance.impl_();
imp.display.upcast_ref::<gdk::Display>().to_glib_none().0
}
unsafe extern "C" fn get_constraint_solver(
root: *mut gtk4::ffi::GtkRoot,
) -> *mut GtkConstraintSolver {
let instance = &*(root as *mut <LayerShellWindowInner as ObjectSubclass>::Instance);
let imp = instance.impl_();
imp.constraint_solver.to_glib_none().0
}
unsafe extern "C" fn get_focus(root: *mut gtk4::ffi::GtkRoot) -> *mut gtk4::ffi::GtkWidget {
let instance = &*(root as *mut <LayerShellWindowInner as ObjectSubclass>::Instance);
let imp = instance.impl_();
imp.focus_widget
.borrow()
.as_ref()
.map_or(ptr::null_mut(), |x| x.to_glib_none().0)
}
unsafe extern "C" fn set_focus(root: *mut gtk4::ffi::GtkRoot, focus: *mut gtk4::ffi::GtkWidget) {
// TODO: `GtkWindow` does more here
let instance = &*(root as *mut <LayerShellWindowInner as ObjectSubclass>::Instance);
let imp = instance.impl_();
*imp.focus_widget.borrow_mut() = if focus.is_null() {
None
} else {
Some(gtk4::Widget::from_glib_none(focus))
};
}

View file

@ -0,0 +1,144 @@
use gdk4_wayland::{WaylandDisplay, WaylandPopup};
use gtk4::{
gdk,
glib::{self, translate::*},
};
use std::boxed::Box as Box_;
use std::fmt;
mod ffi {
use gdk4_wayland::ffi::{GdkWaylandDisplay, GdkWaylandPopup};
use gtk4::{
gdk::ffi::GdkFrameClock,
glib::ffi::{gboolean, gpointer, GDestroyNotify, GType},
};
use std::os::raw::c_int;
pub type GdkWaylandCustomSurfaceGetPopupFunc = Option<
unsafe extern "C" fn(
*mut GdkWaylandCustomSurface,
*mut GdkWaylandPopup,
gpointer,
) -> gboolean,
>;
#[repr(C)]
pub struct GdkWaylandCustomSurface {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
impl ::std::fmt::Debug for GdkWaylandCustomSurface {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.debug_struct(&format!("GdkWaylandCustomSurface @ {:p}", self))
.finish()
}
}
extern "C" {
pub fn gdk_wayland_custom_surface_get_type() -> GType;
pub fn gdk_wayland_custom_surface_new(
display: *mut GdkWaylandDisplay,
) -> *mut GdkWaylandCustomSurface;
pub fn gdk_wayland_custom_surface_present(
custom_surface: *mut GdkWaylandCustomSurface,
width: c_int,
height: c_int,
);
pub fn gdk_wayland_custom_surface_set_get_popup_func(
custom_surface: *mut GdkWaylandCustomSurface,
get_popup_func: GdkWaylandCustomSurfaceGetPopupFunc,
user_data: gpointer,
destroy: GDestroyNotify,
);
pub fn _gdk_frame_clock_idle_new() -> *mut GdkFrameClock;
}
}
glib::wrapper! {
#[doc(alias = "GdkWaylandCustomSurface")]
pub struct WaylandCustomSurface(Object<ffi::GdkWaylandCustomSurface>) @extends gdk4_wayland::WaylandSurface, gdk::Surface;
match fn {
type_ => || ffi::gdk_wayland_custom_surface_get_type(),
}
}
impl WaylandCustomSurface {
#[doc(alias = "gdk_wayland_custom_surface_new")]
pub fn new(display: &WaylandDisplay) -> WaylandCustomSurface {
unsafe {
from_glib_full(ffi::gdk_wayland_custom_surface_new(
display.to_glib_none().0,
))
}
}
#[doc(alias = "gdk_wayland_custom_surface_present")]
pub fn present(&self, width: i32, height: i32) {
unsafe {
ffi::gdk_wayland_custom_surface_present(self.to_glib_none().0, width, height);
}
}
#[doc(alias = "gdk_wayland_custom_surface_set_get_popup_func")]
pub fn set_get_popup_func(
&self,
get_popup_func: Option<
Box_<dyn Fn(&WaylandCustomSurface, &WaylandPopup) -> bool + 'static>,
>,
) {
let get_popup_func_data: Box_<
Option<Box_<dyn Fn(&WaylandCustomSurface, &WaylandPopup) -> bool + 'static>>,
> = Box_::new(get_popup_func);
unsafe extern "C" fn get_popup_func_func(
custom_surface: *mut ffi::GdkWaylandCustomSurface,
popup: *mut gdk4_wayland::ffi::GdkWaylandPopup,
user_data: glib::ffi::gpointer,
) -> glib::ffi::gboolean {
let custom_surface = from_glib_borrow(custom_surface);
let popup = from_glib_borrow(popup);
let callback: &Option<
Box_<dyn Fn(&WaylandCustomSurface, &WaylandPopup) -> bool + 'static>,
> = &*(user_data as *mut _);
let res = if let Some(ref callback) = *callback {
callback(&custom_surface, &popup)
} else {
panic!("cannot get closure...")
};
res.into_glib()
}
let get_popup_func = if get_popup_func_data.is_some() {
Some(get_popup_func_func as _)
} else {
None
};
unsafe extern "C" fn destroy_func(data: glib::ffi::gpointer) {
let _callback: Box_<
Option<Box_<dyn Fn(&WaylandCustomSurface, &WaylandPopup) -> bool + 'static>>,
> = Box_::from_raw(data as *mut _);
}
let destroy_call3 = Some(destroy_func as _);
let super_callback0: Box_<
Option<Box_<dyn Fn(&WaylandCustomSurface, &WaylandPopup) -> bool + 'static>>,
> = get_popup_func_data;
unsafe {
ffi::gdk_wayland_custom_surface_set_get_popup_func(
self.to_glib_none().0,
get_popup_func,
Box_::into_raw(super_callback0) as *mut _,
destroy_call3,
);
}
}
}
impl fmt::Display for WaylandCustomSurface {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("WaylandCustomSurface")
}
}