use slint::platform::WindowAdapter;
use smithay_client_toolkit::{
reexports::{
client::{
QueueHandle,
protocol::{wl_shm, wl_surface::WlSurface},
},
protocols::xdg::shell::client::xdg_surface::XdgSurface,
},
shell::xdg::popup::Popup,
shm::slot::{Buffer, SlotPool},
};
use std::{
cell::{Cell, RefCell},
collections::HashMap,
rc::Rc,
};
use tracing::{info, warn};
use crate::{
PopupSlint,
configure::{PopupConf, PopupCore},
slint_adapter::{ADAPTERS, SpellSkiaWinAdapter},
wayland_adapter::{
SpellWin,
fractional_scaling::{
FractionalScaleHandler, FractionalScaleState, delegate_fractional_scale,
},
viewporter::{ViewporterState, delegate_viewporter},
},
};
pub(super) struct PopupManager {
id_gen: u32,
popups: HashMap<u32, Box<dyn PopupSlint>>,
pool: Option<Rc<RefCell<SlotPool>>>,
}
impl PopupManager {
pub(super) fn new() -> Self {
PopupManager {
id_gen: 0,
popups: HashMap::new(),
pool: None,
}
}
pub(super) fn return_popup(&self, popup_inner: &Popup) -> Option<&dyn PopupSlint> {
for popup in self.popups.values() {
if popup_inner == popup.inner() {
return Some(popup.as_ref());
}
}
None
}
pub(super) fn set_pool(&mut self, pool: Rc<RefCell<SlotPool>>) {
self.pool = Some(pool);
}
pub(super) fn create_popup_core(
&mut self,
popup: Popup,
popup_conf: PopupConf,
_fractional_scale_state: &FractionalScaleState,
_viewporter_state: &ViewporterState,
_qh: &QueueHandle<SpellWin>,
) -> PopupCore {
let stride = popup_conf.width as i32 * 4;
let (buffer, _) = self
.pool
.as_ref()
.unwrap()
.borrow_mut()
.create_buffer(
popup_conf.width as i32,
popup_conf.height as i32,
stride,
wl_shm::Format::Argb8888,
)
.expect("failed to create buffer for popup");
popup.wl_surface().commit();
PopupCore {
pool: self.pool.as_ref().unwrap().clone(),
popup,
popup_conf,
buffer,
}
}
pub(super) fn add_popup<T: PopupSlint + 'static>(&mut self, popup_instance: T) -> u32 {
self.popups.insert(self.id_gen, Box::new(popup_instance));
info!("[Popup Manager]: Popup added for rendering");
self.id_gen = self.id_gen.wrapping_add(1);
self.id_gen - 1
}
pub(super) fn redraw_popups(&self, qh: &QueueHandle<SpellWin>) {
for popup in self.popups.values() {
popup.converter_popup(popup.inner().wl_surface(), qh);
}
}
pub(super) fn return_adapter(
&self,
surface: &WlSurface,
) -> Option<&std::rc::Rc<SpellSkiaWinAdapter>> {
for popup in self.popups.values() {
if popup.inner().wl_surface() == surface {
return Some(popup.adapter());
}
}
None
}
pub(super) fn call_ack(&self, xdg_surface: &XdgSurface, serial: u32) {
for popup in self.popups.values() {
if popup.inner().xdg_surface() == xdg_surface {
popup.inner().xdg_surface().ack_configure(serial);
}
}
}
pub(super) fn close_popup(&mut self, id: &u32) {
if let Some(rem_popup) = self.popups.remove(id) {
rem_popup.inner().xdg_popup().destroy();
info!("Removed Popup with id: {}", id);
} else {
warn!(
"[PopupManager]: trying to remove a non-existant popup with id: {}",
id
);
};
}
}
pub struct SpellXDGPopup {
adapter: Rc<SpellSkiaWinAdapter>,
popup: Popup,
buffer: Buffer,
first_configure: Cell<bool>,
}
delegate_fractional_scale!(SpellXDGPopup);
delegate_viewporter!(SpellXDGPopup);
impl SpellXDGPopup {
pub fn new(popup_settings: PopupCore) -> Self {
let adapter_value: Rc<SpellSkiaWinAdapter> = SpellSkiaWinAdapter::new(
popup_settings.pool,
RefCell::new(popup_settings.buffer.slot()),
popup_settings.popup_conf.width,
popup_settings.popup_conf.height,
);
ADAPTERS.with_borrow_mut(|v| v.push(adapter_value.clone()));
SpellXDGPopup {
adapter: adapter_value,
popup: popup_settings.popup,
buffer: popup_settings.buffer,
first_configure: Cell::new(true),
}
}
pub fn popup(&self) -> &Popup {
&self.popup
}
pub fn first_configure(&self) -> bool {
if self.first_configure.get() {
self.first_configure.set(false);
true
} else {
false
}
}
pub fn adapter(&self) -> &std::rc::Rc<SpellSkiaWinAdapter> {
&self.adapter
}
pub fn converter_popup<'a>(&self, wl_surface: &'a WlSurface, qh: &'a QueueHandle<SpellWin>) {
slint::platform::update_timers_and_animations();
let width: u32 = self.adapter.as_ref().size.get().width;
let height: u32 = self.adapter.as_ref().size.get().height;
let window_adapter = self.adapter.clone();
let redraw_val: bool = window_adapter.draw_if_needed();
let buffer = &self.buffer;
if self.first_configure.get() || redraw_val {
wl_surface.damage_buffer(0, 0, width as i32, height as i32);
wl_surface.attach(Some(buffer.wl_buffer()), 0, 0);
wl_surface.frame(qh, wl_surface.clone());
wl_surface.commit();
} else {
wl_surface.commit();
}
}
}
impl FractionalScaleHandler for SpellXDGPopup {
fn preferred_scale(
&mut self,
_: &smithay_client_toolkit::reexports::client::Connection,
_: &QueueHandle<Self>,
_: &WlSurface,
scale: u32,
) {
info!(
"Scale factor of popup changed, invoked from custom trait: {}",
scale
);
let _width_old = self.adapter.size_original.get().width;
let _height_old = self.adapter.size_original.get().height;
self.popup.wl_surface().damage_buffer(
0,
0,
self.adapter.size.get().width as i32,
self.adapter.size.get().height as i32,
);
let (buffer, _width, _height, scale_factor) = self.adapter.changed_scale_factor(scale);
self.buffer = buffer;
self.adapter
.try_dispatch_event(slint::platform::WindowEvent::ScaleFactorChanged { scale_factor })
.unwrap();
self.adapter.request_redraw();
self.popup.wl_surface().commit();
}
}