Skip to content

Commit

Permalink
Add support for XWayland keyboard grab protocol
Browse files Browse the repository at this point in the history
This protocol is only exposed to the X server, as required by the
protocol spec.
  • Loading branch information
ids1024 committed Aug 9, 2023
1 parent bb25baf commit fe9beab
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 0 deletions.
17 changes: 17 additions & 0 deletions anvil/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,13 @@ use crate::cursor::Cursor;
use crate::{focus::FocusTarget, shell::WindowElement};
#[cfg(feature = "xwayland")]
use smithay::{
delegate_xwayland_keyboard_grab,
reexports::wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
utils::{Point, Size},
wayland::{
data_device::with_source_metadata as with_data_device_source_metadata,
primary_selection::with_source_metadata as with_primary_source_metadata,
xwayland_keyboard_grab::{XWaylandKeyboardGrabHandler, XWaylandKeyboardGrabState},
},
xwayland::{xwm::SelectionType, X11Wm, XWayland, XWaylandEvent},
};
Expand Down Expand Up @@ -438,6 +440,19 @@ impl<BackendData: Backend> FractionalScaleHandler for AnvilState<BackendData> {
}
delegate_fractional_scale!(@<BackendData: Backend + 'static> AnvilState<BackendData>);

#[cfg(feature = "xwayland")]
impl<BackendData: Backend + 'static> XWaylandKeyboardGrabHandler for AnvilState<BackendData> {
fn keyboard_focus_for_xsurface(&self, surface: &WlSurface) -> Option<FocusTarget> {
let elem = self
.space
.elements()
.find(|elem| elem.wl_surface().as_ref() == Some(surface))?;
Some(FocusTarget::Window(elem.clone()))
}
}
#[cfg(feature = "xwayland")]
delegate_xwayland_keyboard_grab!(@<BackendData: Backend + 'static> AnvilState<BackendData>);

impl<BackendData: Backend + 'static> AnvilState<BackendData> {
pub fn init(
display: &mut Display<AnvilState<BackendData>>,
Expand Down Expand Up @@ -525,6 +540,8 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {

#[cfg(feature = "xwayland")]
let xwayland = {
XWaylandKeyboardGrabState::new::<Self>(&dh);

let (xwayland, channel) = XWayland::new(&dh);
let ret = handle.insert_source(channel, move |event, _, data| match event {
XWaylandEvent::Ready {
Expand Down
2 changes: 2 additions & 0 deletions src/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,5 @@ pub mod text_input;
pub mod viewporter;
pub mod virtual_keyboard;
pub mod xdg_activation;
#[cfg(feature = "xwayland")]
pub mod xwayland_keyboard_grab;
242 changes: 242 additions & 0 deletions src/wayland/xwayland_keyboard_grab.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
//! Utilities for handling the xwayland keyboard grab protocol
//!
//! ```
//! use smithay::wayland::xwayland_keyboard_grab::{
//! XWaylandKeyboardGrabHandler,
//! XWaylandKeyboardGrabState
//! };
//! use smithay::delegate_xwayland_keyboard_grab;
//!
//! # struct State;
//! # let mut display = wayland_server::Display::<State>::new().unwrap();
//! // Create the keyboard grab state:
//! XWaylandKeyboardGrabState::new::<State>(
//! &display.handle(), // the display
//! );
//! #
//! # use smithay::input::{Seat, SeatHandler, SeatState, pointer::CursorImageStatus};
//! # use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
//! # impl SeatHandler for State {
//! # type KeyboardFocus = WlSurface;
//! # type PointerFocus = WlSurface;
//! # fn seat_state(&mut self) -> &mut SeatState<Self> { unimplemented!() }
//! # fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&WlSurface>) { unimplemented!() }
//! # fn cursor_image(&mut self, seat: &Seat<Self>, image: CursorImageStatus) { unimplemented!() }
//! # }
//!
//! impl XWaylandKeyboardGrabHandler for State {
//! fn keyboard_focus_for_xsurface(&self, _: &WlSurface) -> Option<Self::KeyboardFocus> {
//! // Return a `SeatHandler::KeyboardFocus` that the grab can use to set focus to the
//! // XWayland surface corresponding to this `WlSurface`.
//! todo!()
//! }
//! }
//!
//! // implement Dispatch for the keyboard grab types
//! delegate_xwayland_keyboard_grab!(State);
//! ```

use wayland_protocols::xwayland::keyboard_grab::zv1::server::{
zwp_xwayland_keyboard_grab_manager_v1::{self, ZwpXwaylandKeyboardGrabManagerV1},
zwp_xwayland_keyboard_grab_v1::{self, ZwpXwaylandKeyboardGrabV1},
};
use wayland_server::{
backend::GlobalId, protocol::wl_surface, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New,
Resource,
};

use crate::{
backend::input::KeyState,
input::{
keyboard::{self, KeyboardGrab, KeyboardInnerHandle},
Seat, SeatHandler,
},
utils::{Serial, SERIAL_COUNTER},
xwayland::XWaylandClientData,
};

const MANAGER_VERSION: u32 = 1;

/// Handler for xwayland keyboard grab protocol
pub trait XWaylandKeyboardGrabHandler: SeatHandler {
/// XWayland client has requested a keyboard grab for `surface` on `seat`
///
/// The default implementation calls `KeyboardHandle::set_grab` if `seat`
/// has a keyboard.
fn grab(&mut self, _surface: wl_surface::WlSurface, seat: Seat<Self>, grab: XWaylandKeyboardGrab<Self>) {
if let Some(keyboard) = seat.get_keyboard() {
keyboard.set_grab(grab, SERIAL_COUNTER.next_serial());
}
}

/// Defines what `KeyboardFocus` an `XWaylandKeyboardGrab` can use to focus the
/// `X11Surface` corresponding to `surface`.
///
/// If this returns `None`, no grab is created.
fn keyboard_focus_for_xsurface(&self, surface: &wl_surface::WlSurface) -> Option<Self::KeyboardFocus>;
}

/// A grab created by xwayland keyboard grab protocol.
///
/// This implements `KeyboardGrab`, and deactivates the grab when the corresponding
/// `ZwpXwaylandKeyboardGrabV1` is destroyed by the client.
#[derive(Debug)]
pub struct XWaylandKeyboardGrab<D: SeatHandler + 'static> {
grab: ZwpXwaylandKeyboardGrabV1,
start_data: keyboard::GrabStartData<D>,
}

impl<D: XWaylandKeyboardGrabHandler + 'static> KeyboardGrab<D> for XWaylandKeyboardGrab<D> {
fn input(
&mut self,
data: &mut D,
handle: &mut KeyboardInnerHandle<'_, D>,
keycode: u32,
state: KeyState,
modifiers: Option<keyboard::ModifiersState>,
serial: Serial,
time: u32,
) {
handle.set_focus(data, self.start_data.focus.clone(), serial);

if !self.grab.is_alive() {
handle.unset_grab(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.grab.is_alive() {
handle.unset_grab(data, serial, false);
handle.set_focus(data, focus, serial);
}
}

fn start_data(&self) -> &keyboard::GrabStartData<D> {
&self.start_data
}
}

/// State of the xwayland keyboard grab global
#[derive(Debug)]
pub struct XWaylandKeyboardGrabState {
global: GlobalId,
}

impl XWaylandKeyboardGrabState {
/// Register new [ZwpXwaylandKeyboardGrabManagerV1] global
pub fn new<D>(display: &DisplayHandle) -> Self
where
D: GlobalDispatch<ZwpXwaylandKeyboardGrabManagerV1, ()>,
D: Dispatch<ZwpXwaylandKeyboardGrabManagerV1, ()>,
D: Dispatch<ZwpXwaylandKeyboardGrabV1, ()>,
D: 'static,
{
let global = display.create_global::<D, ZwpXwaylandKeyboardGrabManagerV1, _>(MANAGER_VERSION, ());

Self { global }
}

/// [ZwpXwaylandKeyboardGrabV1] GlobalId getter
pub fn global(&self) -> GlobalId {
self.global.clone()
}
}

impl<D> GlobalDispatch<ZwpXwaylandKeyboardGrabManagerV1, (), D> for XWaylandKeyboardGrabState
where
D: GlobalDispatch<ZwpXwaylandKeyboardGrabManagerV1, ()>
+ Dispatch<ZwpXwaylandKeyboardGrabManagerV1, ()>
+ 'static,
{
fn bind(
_state: &mut D,
_dh: &DisplayHandle,
_client: &Client,
resource: New<ZwpXwaylandKeyboardGrabManagerV1>,
_global_data: &(),
data_init: &mut DataInit<'_, D>,
) {
data_init.init(resource, ());
}

fn can_view(client: Client, _global_data: &()) -> bool {
client.get_data::<XWaylandClientData>().is_some()
}
}

impl<D> Dispatch<ZwpXwaylandKeyboardGrabManagerV1, (), D> for XWaylandKeyboardGrabState
where
D: Dispatch<ZwpXwaylandKeyboardGrabManagerV1, ()> + 'static,
D: Dispatch<ZwpXwaylandKeyboardGrabV1, ()> + 'static,
D: XWaylandKeyboardGrabHandler,
{
fn request(
state: &mut D,
_client: &wayland_server::Client,
_grab_manager: &ZwpXwaylandKeyboardGrabManagerV1,
request: zwp_xwayland_keyboard_grab_manager_v1::Request,
_data: &(),
_dh: &DisplayHandle,
data_init: &mut wayland_server::DataInit<'_, D>,
) {
match request {
zwp_xwayland_keyboard_grab_manager_v1::Request::GrabKeyboard { id, surface, seat } => {
let grab = data_init.init(id, ());
if let Some(focus) = state.keyboard_focus_for_xsurface(&surface) {
let grab = XWaylandKeyboardGrab {
grab,
start_data: keyboard::GrabStartData { focus: Some(focus) },
};
let seat = Seat::from_resource(&seat).unwrap();
state.grab(surface, seat, grab);
}
}
zwp_xwayland_keyboard_grab_manager_v1::Request::Destroy => {}
_ => unreachable!(),
}
}
}

impl<D> Dispatch<ZwpXwaylandKeyboardGrabV1, (), D> for XWaylandKeyboardGrabState
where
D: Dispatch<ZwpXwaylandKeyboardGrabV1, ()> + 'static,
{
fn request(
_state: &mut D,
_client: &wayland_server::Client,
_grab: &ZwpXwaylandKeyboardGrabV1,
request: zwp_xwayland_keyboard_grab_v1::Request,
_data: &(),
_dh: &DisplayHandle,
_data_init: &mut wayland_server::DataInit<'_, D>,
) {
match request {
zwp_xwayland_keyboard_grab_v1::Request::Destroy => {}
_ => unreachable!(),
}
}
}

/// Macro to delegate implementation of the xwayland keyboard grab protocol
#[macro_export]
macro_rules! delegate_xwayland_keyboard_grab {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
$crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::wayland_protocols::xwayland::keyboard_grab::zv1::server::zwp_xwayland_keyboard_grab_manager_v1::ZwpXwaylandKeyboardGrabManagerV1: ()
] => $crate::wayland::xwayland_keyboard_grab::XWaylandKeyboardGrabState);
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::wayland_protocols::xwayland::keyboard_grab::zv1::server::zwp_xwayland_keyboard_grab_manager_v1::ZwpXwaylandKeyboardGrabManagerV1: ()
] => $crate::wayland::xwayland_keyboard_grab::XWaylandKeyboardGrabState);
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::reexports::wayland_protocols::xwayland::keyboard_grab::zv1::server::zwp_xwayland_keyboard_grab_v1::ZwpXwaylandKeyboardGrabV1: ()
] => $crate::wayland::xwayland_keyboard_grab::XWaylandKeyboardGrabState);
};
}

0 comments on commit fe9beab

Please sign in to comment.