wayland: Rework handlers and protocols into separate files

This commit is contained in:
Victoria Brekenfeld 2022-07-04 15:26:26 +02:00
parent d182d5b388
commit 06d5989223
25 changed files with 3303 additions and 909 deletions

View file

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
reexports::wayland_server::protocol::wl_buffer::WlBuffer,
wayland::buffer::BufferHandler,
};
use crate::utils::prelude::*;
impl BufferHandler for State {
fn buffer_destroyed(&mut self, _buffer: &WlBuffer) {}
}

View file

@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-3.0-only
use std::{
sync::Mutex,
};
use smithay::{
backend::renderer::utils::{
on_commit_buffer_handler,
},
desktop::{
LayerSurface,
PopupKind,
Kind,
WindowSurfaceType,
},
reexports::wayland_server::{
DisplayHandle,
protocol::wl_surface::WlSurface,
},
wayland::{
compositor::{
CompositorHandler,
CompositorState,
with_states,
},
shell::{
xdg::{
ToplevelSurface,
XdgToplevelSurfaceRoleAttributes,
XdgPopupSurfaceRoleAttributes,
},
wlr_layer::LayerSurfaceAttributes,
},
},
delegate_compositor,
};
use crate::{
state::BackendData,
utils::prelude::*,
};
impl State {
fn early_import_surface(&mut self, dh: &DisplayHandle, surface: &WlSurface) {
let mut import_nodes = std::collections::HashSet::new();
for output in self.common.shell.outputs_for_surface(&surface) {
if let BackendData::Kms(ref mut kms_state) = &mut self.backend {
if let Some(target) = kms_state.target_node_for_output(&output) {
if import_nodes.insert(target) {
kms_state.try_early_import(dh, surface, &output, target, &self.common.shell);
}
}
}
self
.backend
.schedule_render(&self.common.event_loop_handle, &output);
}
}
fn toplevel_ensure_initial_configure(&mut self, toplevel: &ToplevelSurface) -> bool {
// send the initial configure if relevant
let initial_configure_sent = with_states(toplevel.wl_surface(), |states| {
states
.data_map
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
.unwrap()
.lock()
.unwrap()
.initial_configure_sent
});
if !initial_configure_sent {
// TODO: query expected size from shell (without inserting and mapping)
toplevel.with_pending_state(|states| states.size = None);
toplevel.send_configure();
}
initial_configure_sent
}
fn xdg_popup_ensure_initial_configure(&mut self, popup: &PopupKind) {
let PopupKind::Xdg(ref popup) = popup;
let initial_configure_sent = with_states(popup.wl_surface(), |states| {
states
.data_map
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
.unwrap()
.lock()
.unwrap()
.initial_configure_sent
});
if !initial_configure_sent {
// NOTE: This should never fail as the initial configure is always
// allowed.
popup.send_configure().expect("initial configure failed");
}
}
fn layer_surface_ensure_inital_configure(&mut self, surface: &LayerSurface) -> bool {
// send the initial configure if relevant
let initial_configure_sent = with_states(surface.wl_surface(), |states| {
states
.data_map
.get::<Mutex<LayerSurfaceAttributes>>()
.unwrap()
.lock()
.unwrap()
.initial_configure_sent
});
if !initial_configure_sent {
// TODO: we should be able to compute the initial dimensions
surface.layer_surface().send_configure();
}
initial_configure_sent
}
}
impl CompositorHandler for State {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.common.compositor_state
}
fn commit(&mut self, dh: &DisplayHandle, surface: &WlSurface) {
// initial configure
if let Some((window, seat)) = self.common.shell.pending_windows.iter().find(|(window, _)| {
window.toplevel().wl_surface() == surface
}).cloned() {
match window.toplevel() {
Kind::Xdg(toplevel) => {
if self.toplevel_ensure_initial_configure(&toplevel) {
let output = active_output(&seat, &self.common);
self.common.shell.map_window(&window, &output);
} else {
return;
}
}
}
}
if let Some((layer_surface, _, _)) = self.common.shell.pending_layers.iter().find(|(layer_surface, _, _)| {
layer_surface.wl_surface() == surface
}).cloned() {
if self.layer_surface_ensure_inital_configure(&layer_surface) {
self.common.shell.map_layer(&layer_surface, dh);
} else {
return;
}
};
if let Some(popup) = self.common.shell.popups.find_popup(surface) {
self.xdg_popup_ensure_initial_configure(&popup);
}
// 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((space, window)) =
self.common
.shell
.space_for_surface_mut(surface)
.and_then(|workspace| {
workspace
.space
.window_for_surface(surface, WindowSurfaceType::TOPLEVEL)
.cloned()
.map(|window| (&mut workspace.space, window))
})
{
let new_location = crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_state(
&window,
space.window_location(&window).unwrap(),
window.geometry().size,
);
if let Some(location) = new_location {
space.map_window(&window, location, true);
}
}
on_commit_buffer_handler(surface);
self.early_import_surface(dh, surface);
self.common.shell.popups.commit(surface);
for workspace in &self.common.shell.spaces {
workspace.space.commit(surface);
}
}
}
delegate_compositor!(State);

View file

@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-3.0-only
use std::cell::RefCell;
use smithay::{
reexports::wayland_server::protocol::{
wl_data_source::WlDataSource,
wl_surface::WlSurface,
},
wayland::{
data_device::{
DataDeviceState,
DataDeviceHandler,
ClientDndGrabHandler,
ServerDndGrabHandler,
},
seat::Seat,
},
delegate_data_device,
};
use crate::state::State;
pub struct DnDIcon {
surface: RefCell<Option<WlSurface>>,
}
pub fn get_dnd_icon(seat: &Seat<State>) -> Option<WlSurface> {
let userdata = seat.user_data();
userdata
.get::<DnDIcon>()
.and_then(|x| x.surface.borrow().clone())
}
impl ClientDndGrabHandler for State {
fn started(
&mut self,
_source: Option<WlDataSource>,
icon: Option<WlSurface>,
seat: Seat<Self>
) {
let user_data = seat.user_data();
user_data.insert_if_missing(|| DnDIcon {
surface: RefCell::new(None),
});
*user_data.get::<DnDIcon>().unwrap().surface.borrow_mut() = icon;
}
fn dropped(&mut self, seat: Seat<Self>) {
seat.user_data()
.get::<DnDIcon>()
.unwrap()
.surface
.borrow_mut()
.take();
}
}
impl ServerDndGrabHandler for State {}
impl DataDeviceHandler for State {
fn data_device_state(&self) -> &DataDeviceState {
&self.common.data_device_state
}
}
delegate_data_device!(State);

View file

@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
backend::{
allocator::dmabuf::Dmabuf,
renderer::ImportDma,
},
reexports::wayland_server::DisplayHandle,
wayland::dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportError},
delegate_dmabuf,
};
use crate::state::{BackendData, State};
impl DmabufHandler for State {
fn dmabuf_state(&mut self) -> &mut DmabufState {
&mut self.common.dmabuf_state
}
fn dmabuf_imported(&mut self, dh: &DisplayHandle, global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<(), ImportError> {
match &mut self.backend {
BackendData::Kms(ref mut state) => state
.dmabuf_imported(dh, global, dmabuf)
.map_err(|_| ImportError::Failed),
BackendData::Winit(ref mut state) => state.backend
.renderer()
.import_dmabuf(&dmabuf, None)
.map(|_| ())
.map_err(|_| ImportError::Failed),
BackendData::X11(ref mut state) => state.renderer
.import_dmabuf(&dmabuf, None)
.map(|_| ())
.map_err(|_| ImportError::Failed),
_ => unreachable!("No backend set when importing dmabuf"),
}
}
}
delegate_dmabuf!(State);

View file

@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
desktop::LayerSurface,
reexports::wayland_server::{
DisplayHandle,
protocol::wl_output::WlOutput,
},
wayland::{
output::Output,
shell::wlr_layer::{
WlrLayerShellHandler,
WlrLayerShellState,
LayerSurface as WlrLayerSurface,
Layer,
},
},
delegate_layer_shell,
};
use crate::utils::prelude::*;
impl WlrLayerShellHandler for State {
fn shell_state(&mut self) -> &mut WlrLayerShellState {
&mut self.common.shell.layer_shell_state
}
fn new_layer_surface(
&mut self,
_dh: &DisplayHandle,
surface: WlrLayerSurface,
wl_output: Option<WlOutput>,
_layer: Layer,
namespace: String
) {
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
let seat = self.common.last_active_seat.clone();
let output = wl_output
.as_ref()
.and_then(Output::from_resource)
.unwrap_or_else(|| active_output(&seat, &self.common));
self.common.shell.pending_layers.push((
LayerSurface::new(surface, namespace),
output,
seat,
));
}
}
delegate_layer_shell!(State);

View file

@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-3.0-only
pub mod buffer;
pub mod compositor;
pub mod data_device;
pub mod dmabuf;
pub mod layer_shell;
pub mod output;
pub mod output_configuration;
pub mod seat;
pub mod shm;
pub mod toplevel_info;
pub mod viewporter;
pub mod wl_drm;
pub mod workspace;
pub mod xdg_shell;
use crate::state::Common;
use smithay::{
reexports::wayland_server::protocol::wl_surface::WlSurface,
wayland::compositor::{add_destruction_hook, with_states},
};
fn mark_dirty_on_drop(state: &Common, wl_surface: &WlSurface) {
use std::sync::{
Arc,
atomic::{AtomicBool, Ordering},
};
let dirty = state.dirty_flag.clone();
struct DirtyFlag(Arc<AtomicBool>);
with_states(wl_surface, |data| {
data.data_map.insert_if_missing(|| DirtyFlag(dirty));
});
add_destruction_hook(wl_surface, |data| if let Some(DirtyFlag(dirty)) = data.data_map.get::<DirtyFlag>() {
dirty.store(true, Ordering::SeqCst);
})
}

View file

@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::delegate_output;
use crate::state::State;
delegate_output!(State);

View file

@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::wayland::output::Output;
use crate::{
config::OutputConfig,
state::State,
wayland::protocols::output_configuration::{
OutputConfigurationState,
OutputConfigurationHandler,
OutputConfiguration,
ModeConfiguration,
delegate_output_configuration,
},
};
use std::cell::RefCell;
impl OutputConfigurationHandler for State {
fn output_configuration_state(&mut self) -> &mut OutputConfigurationState<Self> {
&mut self.common.output_configuration_state
}
fn test_configuration(&mut self, conf: Vec<(Output, OutputConfiguration)>) -> bool {
self.output_configuration(true, conf)
}
fn apply_configuration(&mut self, conf: Vec<(Output, OutputConfiguration)>) -> bool {
self.output_configuration(false, conf)
}
}
impl State {
fn output_configuration(&mut self, test_only: bool, conf: Vec<(Output, OutputConfiguration)>) -> bool {
if conf.iter().all(|(_, conf)| matches!(conf, OutputConfiguration::Disabled)) {
return false; // we don't allow the user to accidentally disable all their outputs
}
let mut backups = Vec::new();
for (output, conf) in &conf {
{
let mut current_config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
backups.push((output, current_config.clone()));
if let OutputConfiguration::Enabled {
mode,
scale,
transform,
position,
} = conf {
match mode {
Some(ModeConfiguration::Mode(mode)) => {
current_config.mode =
((mode.size.w, mode.size.h), Some(mode.refresh as u32));
}
Some(ModeConfiguration::Custom { size, refresh }) => {
current_config.mode =
((size.w, size.h), refresh.map(|x| x as u32));
}
_ => {}
}
if let Some(scale) = scale {
current_config.scale = *scale;
}
if let Some(transform) = transform {
current_config.transform = *transform;
}
if let Some(position) = position {
current_config.position = (*position).into();
}
current_config.enabled = true;
} else {
current_config.enabled = false;
}
}
if let Err(err) = self.backend.apply_config_for_output(
output,
test_only,
&mut self.common.shell,
&self.common.event_loop_handle,
) {
slog_scope::warn!(
"Failed to apply config to {}: {}. Resetting",
output.name(),
err
);
for (output, backup) in backups {
{
let mut current_config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
*current_config = backup;
}
if !test_only {
if let Err(err) = self.backend.apply_config_for_output(
output,
false,
&mut self.common.shell,
&self.common.event_loop_handle,
) {
slog_scope::error!(
"Failed to reset output config for {}: {}",
output.name(),
err
);
}
}
}
return false;
}
}
for output in conf.iter().filter(|(_, c)| matches!(c, OutputConfiguration::Enabled { .. })).map(|(o, _)| o) {
self.common.output_configuration_state.enable_head(output);
}
for output in conf.iter().filter(|(_, c)| matches!(c, OutputConfiguration::Disabled)).map(|(o, _)| o) {
self.common.output_configuration_state.disable_head(output);
}
self
.common
.config
.write_outputs(self.common.output_configuration_state.outputs());
self.common.event_loop_handle.insert_idle(move |data| {
data
.state
.common
.output_configuration_state
.update();
});
true
}
}
delegate_output_configuration!(State);

View file

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
wayland::seat::{SeatHandler, SeatState},
delegate_seat,
};
use crate::state::State;
impl SeatHandler for State {
fn seat_state(&mut self) -> &mut SeatState<Self> {
&mut self.common.seat_state
}
}
delegate_seat!(State);

View file

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
wayland::shm::{ShmHandler, ShmState},
delegate_shm,
};
use crate::state::State;
impl ShmHandler for State {
fn shm_state(&self) -> &ShmState {
&self.common.shm_state
}
}
delegate_shm!(State);

View file

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
state::State,
wayland::protocols::toplevel_info::{
ToplevelInfoHandler,
ToplevelInfoState,
delegate_toplevel_info,
},
};
impl ToplevelInfoHandler for State {
fn toplevel_info_state(&self) -> &ToplevelInfoState<State> {
&self.common.shell.toplevel_info_state
}
fn toplevel_info_state_mut(&mut self) -> &mut ToplevelInfoState<State> {
&mut self.common.shell.toplevel_info_state
}
}
delegate_toplevel_info!(State);

View file

@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::delegate_viewporter;
use crate::state::State;
delegate_viewporter!(State);

View file

@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
state::State,
wayland::protocols::drm::delegate_wl_drm,
};
delegate_wl_drm!(State);

View file

@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::reexports::wayland_server::DisplayHandle;
use crate::{
state::ClientState,
wayland::protocols::workspace::{
Request,
WorkspaceHandler,
WorkspaceState,
WorkspaceClientHandler,
WorkspaceClientState,
delegate_workspace,
},
utils::prelude::*,
};
impl WorkspaceClientHandler for ClientState {
fn workspace_state(&self) -> &WorkspaceClientState {
&self.workspace_client_state
}
}
impl WorkspaceHandler for State {
type Client = ClientState;
fn workspace_state(&self) -> &WorkspaceState<Self> {
&self.common.shell.workspace_state
}
fn workspace_state_mut(&mut self) -> &mut WorkspaceState<Self> {
&mut self.common.shell.workspace_state
}
fn commit_requests(&mut self, dh: &DisplayHandle, requests: Vec<Request>) {
for request in requests.into_iter() {
match request {
Request::Activate(handle) => {
if let Some(idx) = self.common.shell.spaces.iter().position(|w| w.handle == handle) {
let seat = &self.common.last_active_seat;
let output = active_output(seat, &self.common);
self.common.shell.activate(dh, seat, &output, idx);
}
},
_ => {},
}
}
}
}
delegate_workspace!(State);

View file

@ -0,0 +1,298 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
desktop::{
Kind,
PopupKind,
PopupUngrabStrategy,
PopupKeyboardGrab,
PopupPointerGrab,
PopupGrab,
Window,
WindowSurfaceType,
},
reexports::{
wayland_server::{
DisplayHandle,
protocol::{
wl_seat::WlSeat,
wl_surface::WlSurface,
wl_output::WlOutput,
},
},
wayland_protocols::xdg::shell::server::xdg_toplevel,
},
wayland::{
shell::xdg::{
XdgShellHandler,
XdgShellState,
ToplevelSurface,
PopupSurface,
PositionerState,
Configure,
},
seat::{
Seat,
PointerGrabStartData,
},
output::Output,
Serial,
},
delegate_xdg_shell,
};
use std::cell::Cell;
use crate::utils::prelude::*;
pub type PopupGrabData = Cell<Option<PopupGrab>>;
impl XdgShellHandler for State {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
&mut self.common.shell.xdg_shell_state
}
fn new_toplevel(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
let seat = &self.common.last_active_seat;
let window = Window::new(Kind::Xdg(surface));
self.common.shell.toplevel_info_state.new_toplevel(&window);
self.common.shell.pending_windows.push((window, seat.clone()));
// We will position the window after the first commit, when we know its size hints
}
fn new_popup(
&mut self,
_dh: &DisplayHandle,
surface: PopupSurface,
_positioner: PositionerState,
) {
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
self.common
.shell
.popups
.track_popup(PopupKind::from(surface))
.unwrap();
}
fn ack_configure(
&mut self,
_dh: &DisplayHandle,
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(window) = self.common
.shell
.space_for_surface(&surface)
.and_then(|workspace| workspace.space.window_for_surface(&surface, WindowSurfaceType::TOPLEVEL))
{
crate::shell::layout::floating::ResizeSurfaceGrab::ack_configure(
window, configure,
)
}
}
}
fn grab(
&mut self,
dh: &DisplayHandle,
surface: PopupSurface,
seat: WlSeat,
serial: Serial,
) {
let seat = Seat::from_resource(&seat).unwrap();
let ret = self.common.shell.popups.grab_popup(dh, surface.into(), &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(dh, PopupUngrabStrategy::All);
return;
}
self.common.set_focus(dh, 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(dh, PopupUngrabStrategy::All);
return;
}
pointer.set_grab(PopupPointerGrab::new(&grab), serial, 0);
}
seat.user_data()
.insert_if_missing(|| PopupGrabData::new(None));
seat.user_data()
.get::<PopupGrabData>()
.unwrap()
.set(Some(grab));
}
}
fn reposition_request(
&mut self,
_dh: &DisplayHandle,
surface: PopupSurface,
positioner: PositionerState,
token: u32
) {
surface.with_pending_state(|state| {
// TODO: This is a simplification, a proper compositor would
// calculate the geometry of the popup here.
// For now we just use the default implementation here that does not take the
// window position and output constraints into account.
let geometry = positioner.get_geometry();
state.geometry = geometry;
state.positioner = positioner;
});
surface.send_repositioned(token);
}
fn move_request(
&mut self,
_dh: &DisplayHandle,
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)
{
let workspace = self
.common
.shell
.space_for_surface_mut(surface.wl_surface())
.unwrap();
let window = workspace
.space
.window_for_surface(surface.wl_surface(), WindowSurfaceType::TOPLEVEL)
.unwrap()
.clone();
workspace.move_request(&window, &seat, serial, start_data);
}
}
fn resize_request(
&mut self,
_dh: &DisplayHandle,
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)
{
let workspace = self
.common
.shell
.space_for_surface_mut(surface.wl_surface())
.unwrap();
let window = workspace
.space
.window_for_surface(surface.wl_surface(), WindowSurfaceType::TOPLEVEL)
.unwrap()
.clone();
workspace.resize_request(&window, &seat, serial, start_data, edges);
}
}
fn maximize_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
let surface = surface.wl_surface();
let seat = &self.common.last_active_seat;
let output = active_output(seat, &self.common);
if let Some(workspace) = self.common.shell.space_for_surface_mut(surface) {
let window =
workspace.space.window_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap().clone();
workspace.maximize_request(&window, &output)
}
}
fn unmaximize_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
surface.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.size = None;
});
surface.send_configure();
}
fn fullscreen_request(&mut self, _dh: &DisplayHandle, 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;
active_output(seat, &self.common)
});
let surface = surface.wl_surface();
if let Some(workspace) = self.common.shell.space_for_surface_mut(surface) {
let window =
workspace.space.window_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap().clone();
workspace.fullscreen_request(&window, &output)
}
}
fn unfullscreen_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
let surface = surface.wl_surface();
if let Some(workspace) = self.common.shell.space_for_surface_mut(surface) {
let window =
workspace.space.window_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap().clone();
workspace.unfullscreen_request(&window)
}
}
}
fn check_grab_preconditions(
seat: &Seat<State>,
surface: &WlSurface,
serial: Serial,
) -> Option<PointerGrabStartData> {
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
.id()
.same_client_as(&surface.id())
{
return None;
}
Some(start_data)
}
delegate_xdg_shell!(State);