🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
use smithay::{
delegate_xdg_shell,
desktop::{find_popup_root_surface, get_popup_toplevel_coords, PopupKind, PopupManager, Space, Window},
input::{
pointer::{Focus, GrabStartData as PointerGrabStartData},
Seat,
},
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel,
wayland_server::{
protocol::{wl_seat, wl_surface::WlSurface},
Resource,
},
},
utils::{Rectangle, Serial},
wayland::{
compositor::with_states,
shell::xdg::{
PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
XdgToplevelSurfaceData,
},
},
};
use crate::{
grabs::{MoveSurfaceGrab, ResizeSurfaceGrab},
Smallvil,
};
impl XdgShellHandler for Smallvil {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
&mut self.xdg_shell_state
}
fn new_toplevel(&mut self, surface: ToplevelSurface) {
let window = Window::new_wayland_window(surface);
self.space.map_element(window, (0, 0), false);
}
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
self.unconstrain_popup(&surface);
let _ = self.popups.track_popup(PopupKind::Xdg(surface));
}
fn reposition_request(&mut self, surface: PopupSurface, positioner: PositionerState, token: u32) {
surface.with_pending_state(|state| {
let geometry = positioner.get_geometry();
state.geometry = geometry;
state.positioner = positioner;
});
self.unconstrain_popup(&surface);
surface.send_repositioned(token);
}
fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) {
let seat = Seat::from_resource(&seat).unwrap();
let wl_surface = surface.wl_surface();
if let Some(start_data) = check_grab(&seat, wl_surface, serial) {
let pointer = seat.get_pointer().unwrap();
let window = self
.space
.elements()
.find(|w| w.toplevel().unwrap().wl_surface() == wl_surface)
.unwrap()
.clone();
let initial_window_location = self.space.element_location(&window).unwrap();
let grab = MoveSurfaceGrab {
start_data,
window,
initial_window_location,
};
pointer.set_grab(self, grab, serial, Focus::Clear);
}
}
fn resize_request(
&mut self,
surface: ToplevelSurface,
seat: wl_seat::WlSeat,
serial: Serial,
edges: xdg_toplevel::ResizeEdge,
) {
let seat = Seat::from_resource(&seat).unwrap();
let wl_surface = surface.wl_surface();
if let Some(start_data) = check_grab(&seat, wl_surface, serial) {
let pointer = seat.get_pointer().unwrap();
let window = self
.space
.elements()
.find(|w| w.toplevel().unwrap().wl_surface() == wl_surface)
.unwrap()
.clone();
let initial_window_location = self.space.element_location(&window).unwrap();
let initial_window_size = window.geometry().size;
surface.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Resizing);
});
surface.send_pending_configure();
let grab = ResizeSurfaceGrab::start(
start_data,
window,
edges.into(),
Rectangle::new(initial_window_location, initial_window_size),
);
pointer.set_grab(self, grab, serial, Focus::Clear);
}
}
fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
// TODO popup grabs
}
}
// Xdg Shell
delegate_xdg_shell!(Smallvil);
fn check_grab(
seat: &Seat<Smallvil>,
surface: &WlSurface,
serial: Serial,
) -> Option<PointerGrabStartData<Smallvil>> {
let pointer = seat.get_pointer()?;
// Check that this surface has a click grab.
if !pointer.has_grab(serial) {
return None;
}
let start_data = pointer.grab_start_data()?;
let (focus, _) = start_data.focus.as_ref()?;
// If the focus was for a different surface, ignore the request.
if !focus.id().same_client_as(&surface.id()) {
return None;
}
Some(start_data)
}
/// Should be called on `WlSurface::commit`
pub fn handle_commit(popups: &mut PopupManager, space: &Space<Window>, surface: &WlSurface) {
// Handle toplevel commits.
if let Some(window) = space
.elements()
.find(|w| w.toplevel().unwrap().wl_surface() == surface)
.cloned()
{
let initial_configure_sent = with_states(surface, |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap()
.initial_configure_sent
});
if !initial_configure_sent {
window.toplevel().unwrap().send_configure();
}
}
// Handle popup commits.
popups.commit(surface);
if let Some(popup) = popups.find_popup(surface) {
match popup {
PopupKind::Xdg(ref xdg) => {
if !xdg.is_initial_configure_sent() {
// NOTE: This should never fail as the initial configure is always
// allowed.
xdg.send_configure().expect("initial configure failed");
}
}
PopupKind::InputMethod(ref _input_method) => {}
}
}
}
impl Smallvil {
fn unconstrain_popup(&self, popup: &PopupSurface) {
let Ok(root) = find_popup_root_surface(&PopupKind::Xdg(popup.clone())) else {
return;
};
let Some(window) = self
.space
.elements()
.find(|w| w.toplevel().unwrap().wl_surface() == &root)
else {
return;
};
let output = self.space.outputs().next().unwrap();
let output_geo = self.space.output_geometry(output).unwrap();
let window_geo = self.space.element_geometry(window).unwrap();
// The target geometry for the positioner should be relative to its parent's geometry, so
// we will compute that here.
let mut target = output_geo;
target.loc -= get_popup_toplevel_coords(&PopupKind::Xdg(popup.clone()));
target.loc -= window_geo.loc;
popup.with_pending_state(|state| {
state.geometry = state.positioner.get_unconstrained_geometry(target);
});
}
}