Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XWayland keyboard grab protocol #1093

Merged
merged 1 commit into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
};
}
Loading