use std::{
fmt,
sync::{Arc, Mutex},
};
use wayland_server::{protocol::wl_surface::WlSurface, Resource};
use crate::{
backend::input::{ButtonState, KeyState, Keycode},
input::{
keyboard::{
GrabStartData as KeyboardGrabStartData, KeyboardGrab, KeyboardHandle, KeyboardInnerHandle,
ModifiersState,
},
pointer::{
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent,
GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
PointerInnerHandle, RelativeMotionEvent,
},
SeatHandler,
},
utils::{DeadResource, IsAlive, Logical, Point, Serial, SERIAL_COUNTER},
wayland::seat::WaylandFocus,
};
use thiserror::Error;
use super::{PopupKind, PopupManager};
#[derive(Debug, Error)]
pub enum PopupGrabError {
#[error(transparent)]
DeadResource(#[from] DeadResource),
#[error("the parent of the popup has been already dismissed")]
ParentDismissed,
#[error("tried to grab after being mapped")]
InvalidGrab,
#[error("popup was not created on the topmost popup")]
NotTheTopmostPopup,
}
#[derive(Debug, Copy, Clone)]
pub enum PopupUngrabStrategy {
Topmost,
All,
}
#[derive(Debug, Default)]
struct PopupGrabInternal {
serial: Option<Serial>,
active_grabs: Vec<PopupKind>,
dismissed_grabs: Vec<PopupKind>,
}
impl PopupGrabInternal {
fn active(&self) -> bool {
!self.active_grabs.is_empty() || !self.dismissed_grabs.is_empty()
}
fn current_grab(&self) -> Option<&WlSurface> {
self.active_grabs
.iter()
.rev()
.find(|p| p.alive())
.map(|p| p.wl_surface())
}
fn is_dismissed(&self, surface: &WlSurface) -> bool {
self.dismissed_grabs.iter().any(|p| p.wl_surface() == surface)
}
fn append_grab(&mut self, popup: &PopupKind) {
self.active_grabs.push(popup.clone());
}
fn cleanup(&mut self) {
let mut i = 0;
while i < self.active_grabs.len() {
if !self.active_grabs[i].alive() {
let grab = self.active_grabs.remove(i);
self.dismissed_grabs.push(grab);
} else {
i += 1;
}
}
self.dismissed_grabs.retain(|p| p.alive());
}
}
#[derive(Debug, Clone, Default)]
pub(super) struct PopupGrabInner {
internal: Arc<Mutex<PopupGrabInternal>>,
}
impl PopupGrabInner {
pub(super) fn active(&self) -> bool {
let guard = self.internal.lock().unwrap();
guard.active()
}
fn current_grab(&self) -> Option<PopupKind> {
let guard = self.internal.lock().unwrap();
guard.active_grabs.iter().rev().find(|p| p.alive()).cloned()
}
pub(super) fn cleanup(&self) {
let mut guard = self.internal.lock().unwrap();
guard.cleanup();
}
pub(super) fn grab(&self, popup: &PopupKind, serial: Serial) -> Result<Option<Serial>, PopupGrabError> {
let parent = popup.parent().ok_or(DeadResource)?;
self.cleanup();
let mut guard = self.internal.lock().unwrap();
if let Some(grab) = guard.current_grab() {
if grab != &parent {
if guard.is_dismissed(&parent) {
return Err(PopupGrabError::ParentDismissed);
}
return Err(PopupGrabError::NotTheTopmostPopup);
}
}
guard.append_grab(popup);
Ok(guard.serial.replace(serial))
}
fn ungrab(&self, root: &WlSurface, strategy: PopupUngrabStrategy) -> Option<WlSurface> {
let mut guard = self.internal.lock().unwrap();
let dismissed = match strategy {
PopupUngrabStrategy::Topmost => {
if let Some(grab) = guard.active_grabs.pop() {
let dismissed = PopupManager::dismiss_popup(root, &grab);
if dismissed.is_ok() {
guard.dismissed_grabs.push(grab);
}
dismissed
} else {
Ok(())
}
}
PopupUngrabStrategy::All => {
let grabs = guard.active_grabs.drain(..).collect::<Vec<_>>();
if let Some(grab) = grabs.first() {
let dismissed = PopupManager::dismiss_popup(root, grab);
if dismissed.is_ok() {
guard.dismissed_grabs.push(grab.clone());
guard.dismissed_grabs.extend(grabs);
}
dismissed
} else {
Ok(())
}
}
};
if dismissed.is_err() {
guard.active_grabs.drain(..);
}
guard.current_grab().cloned()
}
}
pub struct PopupGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
root: <D as SeatHandler>::KeyboardFocus,
serial: Serial,
previous_serial: Option<Serial>,
toplevel_grab: PopupGrabInner,
keyboard_handle: Option<KeyboardHandle<D>>,
keyboard_grab_start_data: KeyboardGrabStartData<D>,
pointer_grab_start_data: PointerGrabStartData<D>,
}
impl<D> fmt::Debug for PopupGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PopupGrab")
.field("root", &self.root)
.field("serial", &self.serial)
.field("previous_serial", &self.previous_serial)
.field("keyboard_handle", &self.keyboard_handle)
.field("keyboard_grab_start_data", &self.keyboard_grab_start_data)
.field("pointer_grab_start_data", &self.pointer_grab_start_data)
.finish()
}
}
impl<D> Clone for PopupGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
fn clone(&self) -> Self {
PopupGrab {
root: self.root.clone(),
serial: self.serial,
previous_serial: self.previous_serial,
toplevel_grab: self.toplevel_grab.clone(),
keyboard_handle: self.keyboard_handle.clone(),
keyboard_grab_start_data: self.keyboard_grab_start_data.clone(),
pointer_grab_start_data: self.pointer_grab_start_data.clone(),
}
}
}
impl<D> PopupGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus + From<PopupKind>,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
pub(super) fn new(
toplevel_popups: PopupGrabInner,
root: <D as SeatHandler>::KeyboardFocus,
serial: Serial,
previous_serial: Option<Serial>,
keyboard_handle: Option<KeyboardHandle<D>>,
) -> Self {
PopupGrab {
root: root.clone(),
serial,
previous_serial,
toplevel_grab: toplevel_popups,
keyboard_handle,
keyboard_grab_start_data: KeyboardGrabStartData {
focus: Some(root.clone()),
},
pointer_grab_start_data: PointerGrabStartData {
button: 0,
focus: Some((root.into(), (0f64, 0f64).into())),
location: (0f64, 0f64).into(),
},
}
}
pub fn serial(&self) -> Serial {
self.serial
}
pub fn previous_serial(&self) -> Option<Serial> {
self.previous_serial
}
pub fn has_ended(&self) -> bool {
!self.root.alive() || !self.toplevel_grab.active()
}
pub fn current_grab(&self) -> Option<<D as SeatHandler>::KeyboardFocus> {
self.toplevel_grab
.current_grab()
.map(From::from)
.or_else(|| Some(self.root.clone()))
}
pub fn ungrab(&mut self, strategy: PopupUngrabStrategy) -> Option<WlSurface> {
let root_surface = self.root.wl_surface()?;
self.toplevel_grab
.ungrab(&root_surface, strategy)
.or(Some(root_surface.into_owned()))
}
pub fn keyboard_grab_start_data(&self) -> &KeyboardGrabStartData<D> {
&self.keyboard_grab_start_data
}
pub fn pointer_grab_start_data(&self) -> &PointerGrabStartData<D> {
&self.pointer_grab_start_data
}
fn unset_keyboard_grab(&self, data: &mut D, serial: Serial) {
if let Some(keyboard) = self.keyboard_handle.as_ref() {
if keyboard.is_grabbed()
&& (keyboard.has_grab(self.serial)
|| keyboard.has_grab(self.previous_serial.unwrap_or(self.serial)))
{
keyboard.unset_grab(data);
keyboard.set_focus(data, Some(self.root.clone()), serial);
}
}
}
}
pub struct PopupKeyboardGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
popup_grab: PopupGrab<D>,
}
impl<D> fmt::Debug for PopupKeyboardGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PopupKeyboardGrab")
.field("popup_grab", &self.popup_grab)
.finish()
}
}
impl<D> PopupKeyboardGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
pub fn new(popup_grab: &PopupGrab<D>) -> Self {
PopupKeyboardGrab {
popup_grab: popup_grab.clone(),
}
}
}
impl<D> KeyboardGrab<D> for PopupKeyboardGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus + From<PopupKind>,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
fn input(
&mut self,
data: &mut D,
handle: &mut KeyboardInnerHandle<'_, D>,
keycode: Keycode,
state: KeyState,
modifiers: Option<ModifiersState>,
serial: Serial,
time: u32,
) {
if let Some(focus) = self.popup_grab.current_grab() {
handle.set_focus(data, Some(focus), serial);
}
if self.popup_grab.has_ended() {
handle.unset_grab(self, data, serial, false);
}
handle.input(data, keycode, state, modifiers, serial, time)
}
fn set_focus(
&mut self,
data: &mut D,
handle: &mut KeyboardInnerHandle<'_, D>,
focus: Option<<D as SeatHandler>::KeyboardFocus>,
serial: Serial,
) {
if self.popup_grab.has_ended() {
handle.set_focus(data, focus, serial);
handle.unset_grab(self, data, serial, false);
return;
}
if self.popup_grab.current_grab() == focus {
handle.set_focus(data, focus, serial);
}
}
fn start_data(&self) -> &KeyboardGrabStartData<D> {
self.popup_grab.keyboard_grab_start_data()
}
fn unset(&mut self, _data: &mut D) {}
}
pub struct PopupPointerGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
popup_grab: PopupGrab<D>,
}
impl<D> fmt::Debug for PopupPointerGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PopupPointerGrab")
.field("popup_grab", &self.popup_grab)
.finish()
}
}
impl<D> PopupPointerGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
pub fn new(popup_grab: &PopupGrab<D>) -> Self {
PopupPointerGrab {
popup_grab: popup_grab.clone(),
}
}
}
impl<D> PointerGrab<D> for PopupPointerGrab<D>
where
D: SeatHandler + 'static,
<D as SeatHandler>::KeyboardFocus: WaylandFocus + From<PopupKind>,
<D as SeatHandler>::PointerFocus: From<<D as SeatHandler>::KeyboardFocus> + WaylandFocus,
{
fn motion(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
focus: Option<(<D as SeatHandler>::PointerFocus, Point<f64, Logical>)>,
event: &MotionEvent,
) {
if self.popup_grab.has_ended() {
handle.unset_grab(self, data, event.serial, event.time, true);
return;
}
if focus
.as_ref()
.and_then(|f1| {
self.popup_grab
.current_grab()
.as_ref()
.and_then(|f2| f2.wl_surface())
.map(|s| f1.0.same_client_as(&s.id()))
})
.unwrap_or(false)
{
handle.motion(data, focus, event);
} else {
handle.motion(data, None, event);
}
}
fn relative_motion(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
focus: Option<(<D as SeatHandler>::PointerFocus, Point<f64, Logical>)>,
event: &RelativeMotionEvent,
) {
handle.relative_motion(data, focus, event);
}
fn button(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &ButtonEvent) {
let serial = event.serial;
let time = event.time;
let state = event.state;
if self.popup_grab.has_ended() {
handle.unset_grab(self, data, serial, time, true);
handle.button(data, event);
return;
}
if state == ButtonState::Pressed
&& !handle
.current_focus()
.and_then(|f| {
self.popup_grab
.current_grab()
.and_then(|f2| f.0.wl_surface().map(|s| f2.same_client_as(&s.id())))
})
.unwrap_or(false)
{
let _ = self.popup_grab.ungrab(PopupUngrabStrategy::All);
handle.unset_grab(self, data, serial, time, true);
handle.button(data, event);
return;
}
handle.button(data, event);
}
fn axis(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, details: AxisFrame) {
handle.axis(data, details);
}
fn frame(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>) {
handle.frame(data);
}
fn gesture_swipe_begin(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
event: &GestureSwipeBeginEvent,
) {
handle.gesture_swipe_begin(data, event);
}
fn gesture_swipe_update(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
event: &GestureSwipeUpdateEvent,
) {
handle.gesture_swipe_update(data, event);
}
fn gesture_swipe_end(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
event: &GestureSwipeEndEvent,
) {
handle.gesture_swipe_end(data, event);
}
fn gesture_pinch_begin(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
event: &GesturePinchBeginEvent,
) {
handle.gesture_pinch_begin(data, event);
}
fn gesture_pinch_update(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
event: &GesturePinchUpdateEvent,
) {
handle.gesture_pinch_update(data, event);
}
fn gesture_pinch_end(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
event: &GesturePinchEndEvent,
) {
handle.gesture_pinch_end(data, event);
}
fn gesture_hold_begin(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
event: &GestureHoldBeginEvent,
) {
handle.gesture_hold_begin(data, event);
}
fn gesture_hold_end(
&mut self,
data: &mut D,
handle: &mut PointerInnerHandle<'_, D>,
event: &GestureHoldEndEvent,
) {
handle.gesture_hold_end(data, event);
}
fn start_data(&self) -> &PointerGrabStartData<D> {
self.popup_grab.pointer_grab_start_data()
}
fn unset(&mut self, data: &mut D) {
let serial = SERIAL_COUNTER.next_serial();
self.popup_grab.unset_keyboard_grab(data, serial);
}
}