Skip to content

Add xx_input_method_v1 support. #1745

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ wayland-server = { version = "0.31.9", optional = true }
wayland-sys = { version = "0.31.6", optional = true }
wayland-backend = { version = "0.3.10", optional = true }
winit = { version = "0.30.0", default-features = false, features = ["wayland", "wayland-dlopen", "x11", "rwh_06"], optional = true }
wl-input-method = { version = "0.0.2", git = "https://gitlab.freedesktop.org/dcz/wl-input-method.git", branch = "popup" }
x11rb = { version = "0.13.0", optional = true, features = ["res"]}
xkbcommon = { version = "0.8.0", features = ["wayland"]}
encoding_rs = { version = "0.8.33", optional = true }
Expand Down
24 changes: 15 additions & 9 deletions anvil/src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,21 +342,27 @@ fn ensure_initial_configure(surface: &WlSurface, space: &Space<WindowElement>, p
return;
}

if let Some(popup) = popups.find_popup(surface) {
let popup = match popup {
PopupKind::Xdg(ref popup) => popup,
if let Some(mut popup) = popups.find_popup(surface) {
match popup {
PopupKind::Xdg(ref popup) => {
if !popup.is_initial_configure_sent() {
// NOTE: This should never fail as the initial configure is always
// allowed.
popup.send_configure().expect("initial configure failed");
};
}
// Doesn't require configure
PopupKind::InputMethod(ref _input_popup) => {
return;
}
PopupKind::InputMethodV3(ref mut popup) => {
if !popup.is_initial_configure_sent() {
popup.send_pending_configure();
popup.input_method().done();
};
}
};

if !popup.is_initial_configure_sent() {
// NOTE: This should never fail as the initial configure is always
// allowed.
popup.send_configure().expect("initial configure failed");
}

return;
};

Expand Down
74 changes: 69 additions & 5 deletions anvil/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ use smithay::{
},
},
delegate_compositor, delegate_data_control, delegate_data_device, delegate_fractional_scale,
delegate_input_method_manager, delegate_keyboard_shortcuts_inhibit, delegate_layer_shell,
delegate_output, delegate_pointer_constraints, delegate_pointer_gestures, delegate_presentation,
delegate_primary_selection, delegate_relative_pointer, delegate_seat, delegate_security_context,
delegate_shm, delegate_tablet_manager, delegate_text_input_manager, delegate_viewporter,
delegate_virtual_keyboard_manager, delegate_xdg_activation, delegate_xdg_decoration, delegate_xdg_shell,
delegate_input_method_manager, delegate_input_method_manager_v3, delegate_keyboard_shortcuts_inhibit,
delegate_layer_shell, delegate_output, delegate_pointer_constraints, delegate_pointer_gestures,
delegate_presentation, delegate_primary_selection, delegate_relative_pointer, delegate_seat,
delegate_security_context, delegate_shm, delegate_tablet_manager, delegate_text_input_manager,
delegate_viewporter, delegate_virtual_keyboard_manager, delegate_xdg_activation, delegate_xdg_decoration,
delegate_xdg_shell,
desktop::{
space::SpaceElement,
utils::{
Expand Down Expand Up @@ -53,6 +54,10 @@ use smithay::{
fifo::{FifoBarrierCachedState, FifoManagerState},
fractional_scale::{with_fractional_scale, FractionalScaleHandler, FractionalScaleManagerState},
input_method::{InputMethodHandler, InputMethodManagerState, PopupSurface},
input_method_v3::{
self, InputMethodHandler as InputMethodHandlerV3,
InputMethodManagerState as InputMethodManagerStateV3, PopupSurface as PopupSurfaceV3,
},
keyboard_shortcuts_inhibit::{
KeyboardShortcutsInhibitHandler, KeyboardShortcutsInhibitState, KeyboardShortcutsInhibitor,
},
Expand Down Expand Up @@ -339,6 +344,64 @@ impl<BackendData: Backend> InputMethodHandler for AnvilState<BackendData> {

delegate_input_method_manager!(@<BackendData: Backend + 'static> AnvilState<BackendData>);

impl<BackendData: Backend> InputMethodHandlerV3 for AnvilState<BackendData> {
fn new_popup(&mut self, surface: PopupSurfaceV3) {
if let Err(err) = self.popups.track_popup(PopupKind::from(surface)) {
warn!("Failed to track popup: {}", err);
}
}

fn popup_repositioned(&mut self, _: PopupSurfaceV3) {}

fn dismiss_popup(&mut self, surface: PopupSurfaceV3) {
let parent = surface.get_parent().surface.clone();
let _ = PopupManager::dismiss_popup(&parent, &PopupKind::from(surface));
}

fn parent_geometry(&self, parent: &WlSurface) -> Rectangle<i32, smithay::utils::Logical> {
self.space
.elements()
.find_map(|window| (window.wl_surface().as_deref() == Some(parent)).then(|| window.geometry()))
.unwrap_or_default()
}

fn popup_geometry(
&self,
parent: &WlSurface,
cursor: &Rectangle<i32, Logical>,
positioner: &input_method_v3::PositionerState,
) -> Rectangle<i32, Logical> {
let Some(window) = self.window_for_surface(&parent) else {
panic!("Input method popup without parent window");
};

let mut outputs_for_window = self.space.outputs_for_element(&window);
if outputs_for_window.is_empty() {
return Default::default();
}

// Get a union of all outputs' geometries.
let mut outputs_geo = self
.space
.output_geometry(&outputs_for_window.pop().unwrap())
.unwrap();
for output in outputs_for_window {
outputs_geo = outputs_geo.merge(self.space.output_geometry(&output).unwrap());
}

let window_geo = self.space.element_geometry(&window).unwrap();

// The target geometry for the positioner should be relative to its parent's geometry, so
// we will compute that here.
let mut target = outputs_geo;
target.loc -= window_geo.loc;

positioner.get_geometry_from_anchor(*cursor, target)
}
}

delegate_input_method_manager_v3!(@<BackendData: Backend + 'static> AnvilState<BackendData>);

impl<BackendData: Backend> KeyboardShortcutsInhibitHandler for AnvilState<BackendData> {
fn keyboard_shortcuts_inhibit_state(&mut self) -> &mut KeyboardShortcutsInhibitState {
&mut self.keyboard_shortcuts_inhibit_state
Expand Down Expand Up @@ -640,6 +703,7 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
let commit_timing_manager_state = CommitTimingManagerState::new::<Self>(&dh);
TextInputManagerState::new::<Self>(&dh);
InputMethodManagerState::new::<Self, _>(&dh, |_client| true);
InputMethodManagerStateV3::new::<Self, _>(&dh, |_client| true);
VirtualKeyboardManagerState::new::<Self, _>(&dh, |_client| true);
// Expose global only if backend supports relative motion events
if BackendData::HAS_RELATIVE_MOTION {
Expand Down
3 changes: 3 additions & 0 deletions src/desktop/wayland/popup/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ impl PopupManager {
PopupKind::InputMethod(ref _input_method) => {
return Err(PopupGrabError::InvalidGrab);
}
PopupKind::InputMethodV3(ref _input_method) => {
return Err(PopupGrabError::InvalidGrab);
}
}

// The primary store for the grab is the seat, additional we store it
Expand Down
18 changes: 17 additions & 1 deletion src/desktop/wayland/popup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
utils::{IsAlive, Logical, Point, Rectangle},
wayland::{
compositor::with_states,
input_method,
input_method, input_method_v3,
shell::xdg::{self, SurfaceCachedState, XdgPopupSurfaceData},
},
};
Expand All @@ -21,6 +21,8 @@ pub enum PopupKind {
Xdg(xdg::PopupSurface),
/// input-method [`PopupSurface`](input_method::PopupSurface)
InputMethod(input_method::PopupSurface),
/// input-method-v3 [`PopupSurface`](input_method_v3::PopupSurface)
InputMethodV3(input_method_v3::PopupSurface),
}

impl IsAlive for PopupKind {
Expand All @@ -29,6 +31,7 @@ impl IsAlive for PopupKind {
match self {
PopupKind::Xdg(ref p) => p.alive(),
PopupKind::InputMethod(ref p) => p.alive(),
PopupKind::InputMethodV3(ref p) => p.alive(),
}
}
}
Expand All @@ -47,13 +50,15 @@ impl PopupKind {
match *self {
PopupKind::Xdg(ref t) => t.wl_surface(),
PopupKind::InputMethod(ref t) => t.wl_surface(),
PopupKind::InputMethodV3(ref t) => t.wl_surface(),
}
}

fn parent(&self) -> Option<WlSurface> {
match *self {
PopupKind::Xdg(ref t) => t.get_parent_surface(),
PopupKind::InputMethod(ref t) => t.get_parent().map(|parent| parent.surface.clone()),
PopupKind::InputMethodV3(ref t) => Some(t.get_parent().surface.clone()),
}
}

Expand All @@ -70,13 +75,15 @@ impl PopupKind {
.unwrap_or_default()
}),
PopupKind::InputMethod(ref t) => t.get_parent().map(|parent| parent.location).unwrap_or_default(),
PopupKind::InputMethodV3(ref t) => t.get_parent().location,
}
}

fn send_done(&self) {
match *self {
PopupKind::Xdg(ref t) => t.send_popup_done(),
PopupKind::InputMethod(_) => {} //Nothing to do the IME takes care of this itself
PopupKind::InputMethodV3(_) => {} // The IME receives a deactivate event which already indicates that the popup is destroyed.
}
}

Expand All @@ -98,6 +105,8 @@ impl PopupKind {
.loc
}
PopupKind::InputMethod(ref t) => t.location(),
// Use (0,0) as the location for unmapped surfaces. Can't think of anything better. The only use in higher layers is to iterate over all popups, so maybe this is enough.
PopupKind::InputMethodV3(ref t) => t.location(),
}
}
}
Expand All @@ -115,3 +124,10 @@ impl From<input_method::PopupSurface> for PopupKind {
PopupKind::InputMethod(p)
}
}

impl From<input_method_v3::PopupSurface> for PopupKind {
#[inline]
fn from(p: input_method_v3::PopupSurface) -> PopupKind {
PopupKind::InputMethodV3(p)
}
}
2 changes: 2 additions & 0 deletions src/reexports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@ pub use wayland_protocols_wlr;
pub use wayland_server;
#[cfg(feature = "backend_winit")]
pub use winit;
#[cfg(feature = "wayland_frontend")]
pub use wl_input_method;
#[cfg(feature = "x11rb_event_source")]
pub use x11rb;
54 changes: 54 additions & 0 deletions src/wayland/input_method_v3/configure_tracker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*! Tracks serial number assignment */
use crate::utils::{Serial, SERIAL_COUNTER};

/// Tracks states updated via configure sequences involving serials
#[derive(Debug)]
pub struct ConfigureTracker<State> {
/// An ordered sequence of the configures the server has sent out to the client waiting to be
/// acknowledged by the client. All pending configures that are older than
/// the acknowledged one will be discarded during processing
/// layer_surface.ack_configure.
/// The newest configure has the highest index.
pending_configures: Vec<(State, Serial)>,

/// Holds the last server_pending state that has been acknowledged by the
/// client. This state should be cloned to the current during a commit.
last_acked: Option<State>,
}

impl<State> Default for ConfigureTracker<State> {
fn default() -> Self {
Self {
pending_configures: Vec::new(),
last_acked: None,
}
}
}

impl<State: Clone> ConfigureTracker<State> {
/// Assigns a new pending state and returns its serial
pub fn assign_serial(&mut self, state: State) -> Serial {
let serial = SERIAL_COUNTER.next_serial();
self.pending_configures.push((state, serial));
serial
}

/// Marks that the user accepted the serial and returns the state associated with it.
/// If the serial is not currently pending, returns None.
pub fn ack_serial(&mut self, serial: Serial) -> Option<State> {
let (state, _) = self
.pending_configures
.iter()
.find(|(_, c_serial)| *c_serial == serial)
.cloned()?;

self.pending_configures.retain(|(_, c_serial)| *c_serial > serial);
self.last_acked = Some(state.clone());
Some(state)
}

/// Last state sent to the client but not acknowledged
pub fn last_pending_state(&self) -> Option<&State> {
self.pending_configures.last().map(|(s, _)| s)
}
}
Loading
Loading