Skip to content

Commit 9497a8f

Browse files
author
dcz
committed
Add xx_input_method_v1 support
With popup additions: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/407
1 parent b4ad0b5 commit 9497a8f

File tree

15 files changed

+1620
-27
lines changed

15 files changed

+1620
-27
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ wayland-server = { version = "0.31.9", optional = true }
6363
wayland-sys = { version = "0.31.6", optional = true }
6464
wayland-backend = { version = "0.3.10", optional = true }
6565
winit = { version = "0.30.0", default-features = false, features = ["wayland", "wayland-dlopen", "x11", "rwh_06"], optional = true }
66+
wl-input-method = { version = "0.0.2", git = "https://gitlab.freedesktop.org/dcz/wl-input-method.git", branch = "popup" }
6667
x11rb = { version = "0.13.0", optional = true, features = ["res"]}
6768
xkbcommon = { version = "0.8.0", features = ["wayland"]}
6869
encoding_rs = { version = "0.8.33", optional = true }

anvil/src/shell/mod.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -342,21 +342,27 @@ fn ensure_initial_configure(surface: &WlSurface, space: &Space<WindowElement>, p
342342
return;
343343
}
344344

345-
if let Some(popup) = popups.find_popup(surface) {
346-
let popup = match popup {
347-
PopupKind::Xdg(ref popup) => popup,
345+
if let Some(mut popup) = popups.find_popup(surface) {
346+
match popup {
347+
PopupKind::Xdg(ref popup) => {
348+
if !popup.is_initial_configure_sent() {
349+
// NOTE: This should never fail as the initial configure is always
350+
// allowed.
351+
popup.send_configure().expect("initial configure failed");
352+
};
353+
}
348354
// Doesn't require configure
349355
PopupKind::InputMethod(ref _input_popup) => {
350356
return;
351357
}
358+
PopupKind::InputMethodV3(ref mut popup) => {
359+
if !popup.is_initial_configure_sent() {
360+
popup.send_pending_configure();
361+
popup.input_method().done();
362+
};
363+
}
352364
};
353365

354-
if !popup.is_initial_configure_sent() {
355-
// NOTE: This should never fail as the initial configure is always
356-
// allowed.
357-
popup.send_configure().expect("initial configure failed");
358-
}
359-
360366
return;
361367
};
362368

anvil/src/state.rs

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ use smithay::{
1515
},
1616
},
1717
delegate_compositor, delegate_data_control, delegate_data_device, delegate_fractional_scale,
18-
delegate_input_method_manager, delegate_keyboard_shortcuts_inhibit, delegate_layer_shell,
19-
delegate_output, delegate_pointer_constraints, delegate_pointer_gestures, delegate_presentation,
20-
delegate_primary_selection, delegate_relative_pointer, delegate_seat, delegate_security_context,
21-
delegate_shm, delegate_tablet_manager, delegate_text_input_manager, delegate_viewporter,
22-
delegate_virtual_keyboard_manager, delegate_xdg_activation, delegate_xdg_decoration, delegate_xdg_shell,
18+
delegate_input_method_manager, delegate_input_method_manager_v3, delegate_keyboard_shortcuts_inhibit,
19+
delegate_layer_shell, delegate_output, delegate_pointer_constraints, delegate_pointer_gestures,
20+
delegate_presentation, delegate_primary_selection, delegate_relative_pointer, delegate_seat,
21+
delegate_security_context, delegate_shm, delegate_tablet_manager, delegate_text_input_manager,
22+
delegate_viewporter, delegate_virtual_keyboard_manager, delegate_xdg_activation, delegate_xdg_decoration,
23+
delegate_xdg_shell,
2324
desktop::{
2425
space::SpaceElement,
2526
utils::{
@@ -53,6 +54,10 @@ use smithay::{
5354
fifo::{FifoBarrierCachedState, FifoManagerState},
5455
fractional_scale::{with_fractional_scale, FractionalScaleHandler, FractionalScaleManagerState},
5556
input_method::{InputMethodHandler, InputMethodManagerState, PopupSurface},
57+
input_method_v3::{
58+
self, InputMethodHandler as InputMethodHandlerV3,
59+
InputMethodManagerState as InputMethodManagerStateV3, PopupSurface as PopupSurfaceV3,
60+
},
5661
keyboard_shortcuts_inhibit::{
5762
KeyboardShortcutsInhibitHandler, KeyboardShortcutsInhibitState, KeyboardShortcutsInhibitor,
5863
},
@@ -339,6 +344,64 @@ impl<BackendData: Backend> InputMethodHandler for AnvilState<BackendData> {
339344

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

347+
impl<BackendData: Backend> InputMethodHandlerV3 for AnvilState<BackendData> {
348+
fn new_popup(&mut self, surface: PopupSurfaceV3) {
349+
if let Err(err) = self.popups.track_popup(PopupKind::from(surface)) {
350+
warn!("Failed to track popup: {}", err);
351+
}
352+
}
353+
354+
fn popup_repositioned(&mut self, _: PopupSurfaceV3) {}
355+
356+
fn dismiss_popup(&mut self, surface: PopupSurfaceV3) {
357+
let parent = surface.get_parent().surface.clone();
358+
let _ = PopupManager::dismiss_popup(&parent, &PopupKind::from(surface));
359+
}
360+
361+
fn parent_geometry(&self, parent: &WlSurface) -> Rectangle<i32, smithay::utils::Logical> {
362+
self.space
363+
.elements()
364+
.find_map(|window| (window.wl_surface().as_deref() == Some(parent)).then(|| window.geometry()))
365+
.unwrap_or_default()
366+
}
367+
368+
fn popup_geometry(
369+
&self,
370+
parent: &WlSurface,
371+
cursor: &Rectangle<i32, Logical>,
372+
positioner: &input_method_v3::PositionerState,
373+
) -> Rectangle<i32, Logical> {
374+
let Some(window) = self.window_for_surface(&parent) else {
375+
panic!("Input method popup without parent window");
376+
};
377+
378+
let mut outputs_for_window = self.space.outputs_for_element(&window);
379+
if outputs_for_window.is_empty() {
380+
return Default::default();
381+
}
382+
383+
// Get a union of all outputs' geometries.
384+
let mut outputs_geo = self
385+
.space
386+
.output_geometry(&outputs_for_window.pop().unwrap())
387+
.unwrap();
388+
for output in outputs_for_window {
389+
outputs_geo = outputs_geo.merge(self.space.output_geometry(&output).unwrap());
390+
}
391+
392+
let window_geo = self.space.element_geometry(&window).unwrap();
393+
394+
// The target geometry for the positioner should be relative to its parent's geometry, so
395+
// we will compute that here.
396+
let mut target = outputs_geo;
397+
target.loc -= window_geo.loc;
398+
399+
positioner.get_geometry_from_anchor(*cursor, target)
400+
}
401+
}
402+
403+
delegate_input_method_manager_v3!(@<BackendData: Backend + 'static> AnvilState<BackendData>);
404+
342405
impl<BackendData: Backend> KeyboardShortcutsInhibitHandler for AnvilState<BackendData> {
343406
fn keyboard_shortcuts_inhibit_state(&mut self) -> &mut KeyboardShortcutsInhibitState {
344407
&mut self.keyboard_shortcuts_inhibit_state
@@ -640,6 +703,7 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
640703
let commit_timing_manager_state = CommitTimingManagerState::new::<Self>(&dh);
641704
TextInputManagerState::new::<Self>(&dh);
642705
InputMethodManagerState::new::<Self, _>(&dh, |_client| true);
706+
InputMethodManagerStateV3::new::<Self, _>(&dh, |_client| true);
643707
VirtualKeyboardManagerState::new::<Self, _>(&dh, |_client| true);
644708
// Expose global only if backend supports relative motion events
645709
if BackendData::HAS_RELATIVE_MOTION {

src/desktop/wayland/popup/manager.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ impl PopupManager {
9494
PopupKind::InputMethod(ref _input_method) => {
9595
return Err(PopupGrabError::InvalidGrab);
9696
}
97+
PopupKind::InputMethodV3(ref _input_method) => {
98+
return Err(PopupGrabError::InvalidGrab);
99+
}
97100
}
98101

99102
// The primary store for the grab is the seat, additional we store it

src/desktop/wayland/popup/mod.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
utils::{IsAlive, Logical, Point, Rectangle},
1010
wayland::{
1111
compositor::with_states,
12-
input_method,
12+
input_method, input_method_v3,
1313
shell::xdg::{self, SurfaceCachedState, XdgPopupSurfaceData},
1414
},
1515
};
@@ -21,6 +21,8 @@ pub enum PopupKind {
2121
Xdg(xdg::PopupSurface),
2222
/// input-method [`PopupSurface`](input_method::PopupSurface)
2323
InputMethod(input_method::PopupSurface),
24+
/// input-method-v3 [`PopupSurface`](input_method_v3::PopupSurface)
25+
InputMethodV3(input_method_v3::PopupSurface),
2426
}
2527

2628
impl IsAlive for PopupKind {
@@ -29,6 +31,7 @@ impl IsAlive for PopupKind {
2931
match self {
3032
PopupKind::Xdg(ref p) => p.alive(),
3133
PopupKind::InputMethod(ref p) => p.alive(),
34+
PopupKind::InputMethodV3(ref p) => p.alive(),
3235
}
3336
}
3437
}
@@ -47,13 +50,15 @@ impl PopupKind {
4750
match *self {
4851
PopupKind::Xdg(ref t) => t.wl_surface(),
4952
PopupKind::InputMethod(ref t) => t.wl_surface(),
53+
PopupKind::InputMethodV3(ref t) => t.wl_surface(),
5054
}
5155
}
5256

5357
fn parent(&self) -> Option<WlSurface> {
5458
match *self {
5559
PopupKind::Xdg(ref t) => t.get_parent_surface(),
5660
PopupKind::InputMethod(ref t) => t.get_parent().map(|parent| parent.surface.clone()),
61+
PopupKind::InputMethodV3(ref t) => Some(t.get_parent().surface.clone()),
5762
}
5863
}
5964

@@ -70,13 +75,15 @@ impl PopupKind {
7075
.unwrap_or_default()
7176
}),
7277
PopupKind::InputMethod(ref t) => t.get_parent().map(|parent| parent.location).unwrap_or_default(),
78+
PopupKind::InputMethodV3(ref t) => t.get_parent().location,
7379
}
7480
}
7581

7682
fn send_done(&self) {
7783
match *self {
7884
PopupKind::Xdg(ref t) => t.send_popup_done(),
7985
PopupKind::InputMethod(_) => {} //Nothing to do the IME takes care of this itself
86+
PopupKind::InputMethodV3(_) => {} //Nothing to do the IME takes care of this itself
8087
}
8188
}
8289

@@ -98,6 +105,8 @@ impl PopupKind {
98105
.loc
99106
}
100107
PopupKind::InputMethod(ref t) => t.location(),
108+
// 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.
109+
PopupKind::InputMethodV3(ref t) => t.location(),
101110
}
102111
}
103112
}
@@ -115,3 +124,10 @@ impl From<input_method::PopupSurface> for PopupKind {
115124
PopupKind::InputMethod(p)
116125
}
117126
}
127+
128+
impl From<input_method_v3::PopupSurface> for PopupKind {
129+
#[inline]
130+
fn from(p: input_method_v3::PopupSurface) -> PopupKind {
131+
PopupKind::InputMethodV3(p)
132+
}
133+
}

src/reexports.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,7 @@ pub use wayland_protocols_wlr;
2626
pub use wayland_server;
2727
#[cfg(feature = "backend_winit")]
2828
pub use winit;
29+
#[cfg(feature = "wayland_frontend")]
30+
pub use wl_input_method;
2931
#[cfg(feature = "x11rb_event_source")]
3032
pub use x11rb;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*! Tracks serial number assignment */
2+
use crate::utils::{Serial, SERIAL_COUNTER};
3+
4+
/// Tracks states updated via configure sequences involving serials
5+
#[derive(Debug)]
6+
pub struct ConfigureTracker<State> {
7+
/// An ordered sequence of the configures the server has sent out to the client waiting to be
8+
/// acknowledged by the client. All pending configures that are older than
9+
/// the acknowledged one will be discarded during processing
10+
/// layer_surface.ack_configure.
11+
/// The newest configure has the highest index.
12+
pending_configures: Vec<(State, Serial)>,
13+
14+
/// Holds the last server_pending state that has been acknowledged by the
15+
/// client. This state should be cloned to the current during a commit.
16+
last_acked: Option<State>,
17+
}
18+
19+
impl<State> Default for ConfigureTracker<State> {
20+
fn default() -> Self {
21+
Self {
22+
pending_configures: Vec::new(),
23+
last_acked: None,
24+
}
25+
}
26+
}
27+
28+
impl<State: Clone> ConfigureTracker<State> {
29+
/// Assigns a new pending state and returns its serial
30+
pub fn assign_serial(&mut self, state: State) -> Serial {
31+
let serial = SERIAL_COUNTER.next_serial();
32+
self.pending_configures.push((state, serial));
33+
serial
34+
}
35+
36+
/// Marks that the user accepted the serial and returns the state associated with it.
37+
/// If the serial is not currently pending, returns None.
38+
pub fn ack_serial(&mut self, serial: Serial) -> Option<State> {
39+
let (state, _) = self
40+
.pending_configures
41+
.iter()
42+
.find(|(_, c_serial)| *c_serial == serial)
43+
.cloned()?;
44+
45+
self.pending_configures.retain(|(_, c_serial)| *c_serial > serial);
46+
self.last_acked = Some(state.clone());
47+
Some(state)
48+
}
49+
50+
/// Last state sent to the client but not acknowledged
51+
pub fn last_pending_state(&self) -> Option<&State> {
52+
self.pending_configures.last().map(|(s, _)| s)
53+
}
54+
}

0 commit comments

Comments
 (0)