cosmic-comp/src/wayland/handlers/xdg_shell/mod.rs
2022-11-23 16:08:32 +01:00

290 lines
9.8 KiB
Rust

// SPDX-License-Identifier: GPL-3.0-only
use crate::utils::prelude::*;
use smithay::{
delegate_xdg_shell,
desktop::{
find_popup_root_surface, Kind, PopupGrab, PopupKeyboardGrab, PopupKind, PopupPointerGrab,
PopupUngrabStrategy, Window,
},
input::{
pointer::{Focus, GrabStartData as PointerGrabStartData},
Seat,
},
output::Output,
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel,
wayland_server::protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface},
},
utils::Serial,
wayland::{
seat::WaylandFocus,
shell::xdg::{
Configure, PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler,
XdgShellState,
},
},
};
use std::cell::Cell;
pub mod popup;
pub type PopupGrabData = Cell<Option<PopupGrab<State>>>;
impl XdgShellHandler for State {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
&mut self.common.shell.xdg_shell_state
}
fn new_toplevel(&mut self, surface: ToplevelSurface) {
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
let seat = self.common.last_active_seat().clone();
let window = Window::new(Kind::Xdg(surface));
self.common.shell.toplevel_info_state.new_toplevel(&window);
self.common.shell.pending_windows.push((window, seat));
// We will position the window after the first commit, when we know its size hints
}
fn new_popup(&mut self, surface: PopupSurface, positioner: PositionerState) {
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
surface.with_pending_state(|state| {
state.geometry = positioner.get_geometry();
state.positioner = positioner;
});
if surface.get_parent_surface().is_some() {
// let other shells deal with their popups
self.common.shell.unconstrain_popup(&surface, &positioner);
if surface.send_configure().is_ok() {
self.common
.shell
.popups
.track_popup(PopupKind::from(surface))
.unwrap();
}
}
}
fn ack_configure(&mut self, surface: WlSurface, configure: Configure) {
if let Configure::Toplevel(configure) = configure {
// If we would re-position the window inside the grab we would get a weird jittery animation.
// We only want to resize once the client has acknoledged & commited the new size,
// so we need to carefully track the state through different handlers.
if let Some(mapped) = self.common.shell.element_for_surface(&surface) {
//TODO
//crate::shell::layout::floating::ResizeSurfaceGrab::ack_configure(window, configure)
}
}
}
fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) {
let seat = Seat::from_resource(&seat).unwrap();
let kind = PopupKind::Xdg(surface);
if let Some(root) = find_popup_root_surface(&kind)
.ok()
.and_then(|root| self.common.shell.element_for_surface(&root))
{
let target = root.clone().into();
let ret = self
.common
.shell
.popups
.grab_popup(target, kind, &seat, serial);
if let Ok(mut grab) = ret {
if let Some(keyboard) = seat.get_keyboard() {
if keyboard.is_grabbed()
&& !(keyboard.has_grab(serial)
|| keyboard.has_grab(grab.previous_serial().unwrap_or(serial)))
{
grab.ungrab(PopupUngrabStrategy::All);
return;
}
Common::set_focus(self, grab.current_grab().as_ref(), &seat, Some(serial));
keyboard.set_grab(PopupKeyboardGrab::new(&grab), serial);
}
if let Some(pointer) = seat.get_pointer() {
if pointer.is_grabbed()
&& !(pointer.has_grab(serial)
|| pointer
.has_grab(grab.previous_serial().unwrap_or_else(|| grab.serial())))
{
grab.ungrab(PopupUngrabStrategy::All);
return;
}
pointer.set_grab(self, PopupPointerGrab::new(&grab), serial, Focus::Keep);
}
seat.user_data()
.insert_if_missing(|| PopupGrabData::new(None));
seat.user_data()
.get::<PopupGrabData>()
.unwrap()
.set(Some(grab));
}
}
}
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.common.shell.unconstrain_popup(&surface, &positioner);
surface.send_repositioned(token);
if let Err(err) = surface.send_configure() {
slog_scope::warn!(
"Compositor bug: Unable to re configure repositioned popup: {}",
err
);
}
}
fn move_request(&mut self, surface: ToplevelSurface, seat: WlSeat, serial: Serial) {
let seat = Seat::from_resource(&seat).unwrap();
if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) {
if let Some(mapped) = self.common.shell.element_for_surface(surface.wl_surface()) {
// Shell::move_request(self, &window, &seat, serial, start_data);
}
}
}
fn resize_request(
&mut self,
surface: ToplevelSurface,
seat: WlSeat,
serial: Serial,
edges: xdg_toplevel::ResizeEdge,
) {
let seat = Seat::from_resource(&seat).unwrap();
if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) {
if let Some(mapped) = self.common.shell.element_for_surface(surface.wl_surface()) {
// Shell::resize_request(self, mapped, &seat, serial, start_data, edges);
}
}
}
fn maximize_request(&mut self, surface: ToplevelSurface) {
let seat = self.common.last_active_seat();
let output = seat.active_output();
if let Some(mapped) = self
.common
.shell
.element_for_surface(surface.wl_surface())
.cloned()
{
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
let (window, _) = mapped
.windows()
.find(|(w, _)| matches!(w.toplevel(), Kind::Xdg(s) if s == &surface))
.unwrap();
workspace.maximize_request(&window, &output)
}
}
}
fn unmaximize_request(&mut self, surface: ToplevelSurface) {
if let Some(mapped) = self
.common
.shell
.element_for_surface(surface.wl_surface())
.cloned()
{
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
let (window, _) = mapped
.windows()
.find(|(w, _)| matches!(w.toplevel(), Kind::Xdg(s) if s == &surface))
.unwrap();
workspace.unmaximize_request(&window)
}
}
}
fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option<WlOutput>) {
let output = output
.as_ref()
.and_then(Output::from_resource)
.unwrap_or_else(|| {
let seat = self.common.last_active_seat();
seat.active_output()
});
if let Some(mapped) = self
.common
.shell
.element_for_surface(surface.wl_surface())
.cloned()
{
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
let (window, _) = mapped
.windows()
.find(|(w, _)| matches!(w.toplevel(), Kind::Xdg(s) if s == &surface))
.unwrap();
workspace.fullscreen_request(&window, &output)
}
}
}
fn unfullscreen_request(&mut self, surface: ToplevelSurface) {
if let Some(mapped) = self
.common
.shell
.element_for_surface(surface.wl_surface())
.cloned()
{
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
let (window, _) = mapped
.windows()
.find(|(w, _)| matches!(w.toplevel(), Kind::Xdg(s) if s == &surface))
.unwrap();
workspace.unfullscreen_request(&window)
}
}
}
}
fn check_grab_preconditions(
seat: &Seat<State>,
surface: &WlSurface,
serial: Serial,
) -> Option<PointerGrabStartData<State>> {
use smithay::reexports::wayland_server::Resource;
// TODO: touch resize.
let pointer = seat.get_pointer().unwrap();
// Check that this surface has a click grab.
if !pointer.has_grab(serial) {
return None;
}
let start_data = pointer.grab_start_data().unwrap();
// If the focus was for a different surface, ignore the request.
if start_data.focus.is_none()
|| !start_data
.focus
.as_ref()
.unwrap()
.0
.same_client_as(&surface.id())
{
return None;
}
Some(start_data)
}
delegate_xdg_shell!(State);