diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs index daf342296f..0a98fb4b0b 100644 --- a/src/handlers/compositor.rs +++ b/src/handlers/compositor.rs @@ -394,6 +394,34 @@ impl CompositorHandler for State { return; } + for popup in self + .niri + .input_method_v1_popups + .iter() + .filter(|p| p.alive()) + { + let mut popup_root = popup.wl_surface().clone(); + while let Some(parent) = get_parent(&popup_root) { + popup_root = parent; + } + + if popup_root != root_surface { + continue; + } + + if let Some(parent) = popup.get_parent().map(|parent| parent.surface.clone()) { + let mut parent_root = parent; + while let Some(next) = get_parent(&parent_root) { + parent_root = next; + } + + if let Some(output) = self.niri.output_for_root(&parent_root) { + self.niri.queue_redraw(&output.clone()); + return; + } + } + } + // This might be a layer-shell surface. if self.layer_shell_handle_commit(surface) { return; diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 8d67e6a047..ac6e01dd4f 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -32,6 +32,9 @@ use smithay::wayland::fractional_scale::FractionalScaleHandler; use smithay::wayland::idle_inhibit::IdleInhibitHandler; use smithay::wayland::idle_notify::{IdleNotifierHandler, IdleNotifierState}; use smithay::wayland::input_method::{InputMethodHandler, PopupSurface}; +use smithay::wayland::input_method_v1::{ + InputMethodV1Handler, PopupSurface as InputMethodPopupSurfaceV1, +}; use smithay::wayland::keyboard_shortcuts_inhibit::{ KeyboardShortcutsInhibitHandler, KeyboardShortcutsInhibitState, KeyboardShortcutsInhibitor, }; @@ -64,11 +67,12 @@ use smithay::{ delegate_cursor_shape, delegate_data_control, delegate_data_device, delegate_dmabuf, delegate_drm_lease, delegate_ext_data_control, delegate_fractional_scale, delegate_idle_inhibit, delegate_idle_notify, delegate_input_method_manager, - delegate_keyboard_shortcuts_inhibit, delegate_output, delegate_pointer_constraints, - delegate_pointer_gestures, delegate_presentation, delegate_primary_selection, - delegate_relative_pointer, delegate_seat, delegate_security_context, delegate_session_lock, - delegate_single_pixel_buffer, delegate_tablet_manager, delegate_text_input_manager, - delegate_viewporter, delegate_xdg_activation, + delegate_input_method_manager_v1, delegate_keyboard_shortcuts_inhibit, delegate_output, + delegate_pointer_constraints, delegate_pointer_gestures, delegate_presentation, + delegate_primary_selection, delegate_relative_pointer, delegate_seat, + delegate_security_context, delegate_session_lock, delegate_single_pixel_buffer, + delegate_tablet_manager, delegate_text_input_manager, delegate_viewporter, + delegate_xdg_activation, }; pub use crate::handlers::xdg_shell::KdeDecorationsModeState; @@ -249,14 +253,108 @@ impl InputMethodHandler for State { } fn parent_geometry(&self, parent: &WlSurface) -> Rectangle { + let mut root = parent.clone(); + while let Some(next) = get_parent(&root) { + root = next; + } + self.niri .layout - .find_window_and_output(parent) + .find_window_and_output(&root) .map(|(mapped, _)| mapped.window.geometry()) .unwrap_or_default() } } +impl InputMethodV1Handler for State { + fn new_popup(&mut self, surface: InputMethodPopupSurfaceV1) { + if let Some(parent) = surface.get_parent().map(|parent| parent.surface.clone()) { + let mut root = parent; + while let Some(next) = get_parent(&root) { + root = next; + } + + if let Some(output) = self.niri.output_for_root(&root) { + let scale = output.current_scale(); + let transform = output.current_transform(); + let wl_surface = surface.wl_surface(); + with_states(wl_surface, |data| { + send_scale_transform(wl_surface, data, scale, transform); + }); + } + } + + self.niri + .input_method_v1_popups + .retain(|p| p.alive() && p.wl_surface() != surface.wl_surface()); + self.niri.input_method_v1_popups.push(surface); + self.niri.queue_redraw_all(); + } + + fn popup_repositioned(&mut self, surface: InputMethodPopupSurfaceV1) { + self.niri + .input_method_v1_popups + .retain(|p| p.alive() && p.wl_surface() != surface.wl_surface()); + self.niri.input_method_v1_popups.push(surface); + self.niri.queue_redraw_all(); + } + + fn dismiss_popup(&mut self, surface: InputMethodPopupSurfaceV1) { + self.niri + .input_method_v1_popups + .retain(|p| p.wl_surface() != surface.wl_surface()); + self.niri.queue_redraw_all(); + } + + fn parent_geometry(&self, parent: &WlSurface) -> Rectangle { + self.input_method_parent_geometry(parent) + } +} + +impl State { + fn input_method_parent_geometry(&self, parent: &WlSurface) -> Rectangle { + let mut root = parent.clone(); + while let Some(next) = get_parent(&root) { + root = next; + } + + let Some((mapped, output)) = self.niri.layout.find_window_and_output(&root) else { + return Rectangle::default(); + }; + + let window_geometry = mapped.window.geometry(); + let window = mapped.window.clone(); + let Some(output) = output.cloned() else { + return window_geometry; + }; + + let Some(mon) = self.niri.layout.monitor_for_output(&output) else { + return window_geometry; + }; + let Some((ws, ws_geo)) = mon + .workspaces_with_render_geo() + .find(|(ws, _)| ws.has_window(&window)) + else { + return window_geometry; + }; + let Some((tile, tile_offset, _)) = ws + .tiles_with_render_positions() + .find(|(tile, _, _)| tile.window().window == window) + else { + return window_geometry; + }; + + let zoom = mon.overview_zoom(); + let Some(output_geo) = self.niri.global_space.output_geometry(&output) else { + return window_geometry; + }; + + let loc = + output_geo.loc.to_f64() + ws_geo.loc + tile_offset.upscale(zoom) + tile.window_loc(); + Rectangle::new(loc.to_i32_round(), window_geometry.size) + } +} + impl KeyboardShortcutsInhibitHandler for State { fn keyboard_shortcuts_inhibit_state(&mut self) -> &mut KeyboardShortcutsInhibitState { &mut self.niri.keyboard_shortcuts_inhibit_state @@ -278,6 +376,7 @@ impl KeyboardShortcutsInhibitHandler for State { } delegate_input_method_manager!(State); +delegate_input_method_manager_v1!(State); delegate_keyboard_shortcuts_inhibit!(State); impl SelectionHandler for State { diff --git a/src/niri.rs b/src/niri.rs index 414f702d80..3f52c1e056 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -71,7 +71,7 @@ use smithay::utils::{ Transform, SERIAL_COUNTER, }; use smithay::wayland::compositor::{ - with_states, with_surface_tree_downward, CompositorClientState, CompositorHandler, + get_parent, with_states, with_surface_tree_downward, CompositorClientState, CompositorHandler, CompositorState, HookId, SurfaceData, TraversalAction, }; use smithay::wayland::cursor_shape::CursorShapeManagerState; @@ -80,6 +80,8 @@ use smithay::wayland::fractional_scale::FractionalScaleManagerState; use smithay::wayland::idle_inhibit::IdleInhibitManagerState; use smithay::wayland::idle_notify::IdleNotifierState; use smithay::wayland::input_method::InputMethodManagerState; +use smithay::wayland::input_method_v1::InputMethodV1ManagerState; +use smithay::wayland::input_method_v1::PopupSurface as InputMethodPopupSurfaceV1; use smithay::wayland::keyboard_shortcuts_inhibit::{ KeyboardShortcutsInhibitState, KeyboardShortcutsInhibitor, }; @@ -286,6 +288,7 @@ pub struct Niri { pub tablet_state: TabletManagerState, pub text_input_state: TextInputManagerState, pub input_method_state: InputMethodManagerState, + pub input_method_v1_state: InputMethodV1ManagerState, pub keyboard_shortcuts_inhibit_state: KeyboardShortcutsInhibitState, pub virtual_keyboard_state: VirtualKeyboardManagerState, pub virtual_pointer_state: VirtualPointerManagerState, @@ -299,6 +302,7 @@ pub struct Niri { pub wlr_data_control_state: WlrDataControlState, pub ext_data_control_state: ExtDataControlState, pub popups: PopupManager, + pub input_method_v1_popups: Vec, pub popup_grab: Option, pub presentation_state: PresentationState, pub security_context_state: SecurityContextState, @@ -2269,6 +2273,8 @@ impl Niri { let text_input_state = TextInputManagerState::new::(&display_handle); let input_method_state = InputMethodManagerState::new::(&display_handle, client_is_unrestricted); + let input_method_v1_state = + InputMethodV1ManagerState::new::(&display_handle, client_is_unrestricted); let keyboard_shortcuts_inhibit_state = KeyboardShortcutsInhibitState::new::(&display_handle); let virtual_keyboard_state = @@ -2471,6 +2477,7 @@ impl Niri { xdg_foreign_state, text_input_state, input_method_state, + input_method_v1_state, keyboard_shortcuts_inhibit_state, virtual_keyboard_state, virtual_pointer_state, @@ -2490,6 +2497,7 @@ impl Niri { wlr_data_control_state, ext_data_control_state, popups: PopupManager::default(), + input_method_v1_popups: Vec::new(), popup_grab: None, suppressed_keys: HashSet::new(), suppressed_buttons: HashSet::new(), @@ -4074,6 +4082,35 @@ impl Niri { self.render_pointer(renderer, output, &mut |elem| push(elem.into())); } + // input-method-v1 popups (candidate window / panel), below the pointer. + for popup in self.input_method_v1_popups.iter().filter(|p| p.alive()) { + let Some(parent) = popup.get_parent() else { + continue; + }; + + let mut root = parent.surface.clone(); + while let Some(next) = get_parent(&root) { + root = next; + } + + if self.output_for_root(&root) != Some(output) { + continue; + } + + let location = (parent.location.loc + popup.location()) + .to_f64() + .to_physical_precise_round(output_scale); + push_elements_from_surface_tree( + renderer, + popup.wl_surface(), + location, + output_scale, + 1., + Kind::ScanoutCandidate, + &mut |elem| push(elem.into()), + ); + } + // Next, the screen transition texture. { let state = self.output_state.get(output).unwrap(); diff --git a/src/protocols/virtual_keyboard.rs b/src/protocols/virtual_keyboard.rs index 1b55036120..9eecc17b56 100644 --- a/src/protocols/virtual_keyboard.rs +++ b/src/protocols/virtual_keyboard.rs @@ -1,16 +1,8 @@ -use smithay::backend::input::{ - Device, DeviceCapability, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, Keycode, - UnusedEvent, -}; +use smithay::backend::input::{Device, DeviceCapability}; use smithay::delegate_virtual_keyboard_manager; -use smithay::input::keyboard::xkb::ModMask; -use smithay::input::keyboard::KeyboardHandle; -use smithay::wayland::virtual_keyboard::VirtualKeyboardHandler; use crate::niri::State; -pub struct VirtualKeyboardInputBackend; - #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct VirtualKeyboard; @@ -35,98 +27,4 @@ impl Device for VirtualKeyboard { None } } - -pub struct VirtualKeyboardKeyEvent { - pub keycode: Keycode, - pub state: KeyState, - pub time: u32, -} - -impl Event for VirtualKeyboardKeyEvent { - fn time(&self) -> u64 { - self.time as u64 * 1000 // millis to micros - } - - fn device(&self) -> VirtualKeyboard { - VirtualKeyboard - } -} - -impl KeyboardKeyEvent for VirtualKeyboardKeyEvent { - fn key_code(&self) -> Keycode { - self.keycode - } - - fn state(&self) -> KeyState { - self.state - } - - fn count(&self) -> u32 { - 0 // Not used by niri - } -} - -impl InputBackend for VirtualKeyboardInputBackend { - type Device = VirtualKeyboard; - - type KeyboardKeyEvent = VirtualKeyboardKeyEvent; - type PointerAxisEvent = UnusedEvent; - type PointerButtonEvent = UnusedEvent; - type PointerMotionEvent = UnusedEvent; - type PointerMotionAbsoluteEvent = UnusedEvent; - - type GestureSwipeBeginEvent = UnusedEvent; - type GestureSwipeUpdateEvent = UnusedEvent; - type GestureSwipeEndEvent = UnusedEvent; - type GesturePinchBeginEvent = UnusedEvent; - type GesturePinchUpdateEvent = UnusedEvent; - type GesturePinchEndEvent = UnusedEvent; - type GestureHoldBeginEvent = UnusedEvent; - type GestureHoldEndEvent = UnusedEvent; - - type TouchDownEvent = UnusedEvent; - type TouchUpEvent = UnusedEvent; - type TouchMotionEvent = UnusedEvent; - type TouchCancelEvent = UnusedEvent; - type TouchFrameEvent = UnusedEvent; - type TabletToolAxisEvent = UnusedEvent; - type TabletToolProximityEvent = UnusedEvent; - type TabletToolTipEvent = UnusedEvent; - type TabletToolButtonEvent = UnusedEvent; - - type SwitchToggleEvent = UnusedEvent; - - type SpecialEvent = UnusedEvent; -} - -impl VirtualKeyboardHandler for State { - fn on_keyboard_event( - &mut self, - keycode: Keycode, - state: KeyState, - time: u32, - _keyboard: KeyboardHandle, - ) { - // The virtual keyboard impl in Smithay changes the keymap, so we'll need to reset it on - // the next real keyboard event. - self.niri.reset_keymap = true; - - let event = VirtualKeyboardKeyEvent { - keycode, - state, - time, - }; - self.process_input_event(InputEvent::::Keyboard { event }); - } - - // We handle modifiers when the key event is sent. - fn on_keyboard_modifiers( - &mut self, - _depressed_mods: ModMask, - _latched_mods: ModMask, - _locked_mods: ModMask, - _keyboard: KeyboardHandle, - ) { - } -} delegate_virtual_keyboard_manager!(State);