Add isolated native Rime service and MiSans candidate UI
This commit is contained in:
1 parent
f21167e1a2
commit
8e9a5838ab
28 files changed
+2381
-17
No files matched your search
@@ -81,6 +81,7 @@ impl CompositorHandler for Ferese {
|
||||
|
||||
layer_shell::handle_commit(self, surface);
|
||||
xdg_shell::handle_commit(self, surface);
|
||||
self.position_committed_input_popup(surface);
|
||||
crate::backends::direct::render_surface(self, surface);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,8 @@ delegate_protocols!(
|
||||
misc::zwp_input_method_v2::server::zwp_input_method_manager_v2::ZwpInputMethodManagerV2,
|
||||
misc::zwp_input_method_v2::server::zwp_input_method_v2::ZwpInputMethodV2,
|
||||
misc::zwp_input_method_v2::server::zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2,
|
||||
misc::zwp_virtual_keyboard_v1::server::zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1,
|
||||
misc::zwp_virtual_keyboard_v1::server::zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
|
||||
wlr::data_control::v1::server::zwlr_data_control_device_v1::ZwlrDataControlDeviceV1,
|
||||
wlr::data_control::v1::server::zwlr_data_control_manager_v1::ZwlrDataControlManagerV1,
|
||||
wlr::data_control::v1::server::zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use smithay::desktop::{PopupKind, WindowSurfaceType, layer_map_for_output};
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::utils::{Logical, Rectangle};
|
||||
use smithay::utils::{Point, Size};
|
||||
use smithay::wayland::input_method::{InputMethodHandler, PopupSurface};
|
||||
|
||||
use crate::Ferese;
|
||||
|
||||
impl InputMethodHandler for Ferese {
|
||||
fn new_popup(&mut self, surface: PopupSurface) {
|
||||
self.position_input_popup(&surface);
|
||||
if let Err(error) = self
|
||||
.popups
|
||||
.track_popup(PopupKind::InputMethod(surface.clone()))
|
||||
@@ -23,6 +25,7 @@ impl InputMethodHandler for Ferese {
|
||||
}
|
||||
|
||||
fn popup_repositioned(&mut self, surface: PopupSurface) {
|
||||
self.position_input_popup(&surface);
|
||||
crate::backends::direct::render_surface(self, surface.wl_surface());
|
||||
}
|
||||
|
||||
@@ -58,3 +61,95 @@ impl InputMethodHandler for Ferese {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Ferese {
|
||||
pub(crate) fn position_committed_input_popup(&mut self, surface: &WlSurface) {
|
||||
if let Some(PopupKind::InputMethod(popup)) = self.popups.find_popup(surface) {
|
||||
self.position_input_popup(&popup);
|
||||
}
|
||||
}
|
||||
|
||||
fn position_input_popup(&self, popup: &PopupSurface) {
|
||||
let Some(parent) = popup.get_parent() else {
|
||||
return;
|
||||
};
|
||||
let parent = self.parent_geometry(&parent.surface);
|
||||
let cursor = popup.text_input_rectangle();
|
||||
let caret = parent.loc + cursor.loc;
|
||||
let output = self
|
||||
.space
|
||||
.outputs()
|
||||
.filter_map(|output| self.space.output_geometry(output))
|
||||
.find(|geometry| geometry.contains(caret))
|
||||
.or_else(|| {
|
||||
self.space
|
||||
.outputs()
|
||||
.find_map(|output| self.space.output_geometry(output))
|
||||
});
|
||||
let Some(output) = output else {
|
||||
return;
|
||||
};
|
||||
let bounds = smithay::desktop::utils::bbox_from_surface_tree(popup.wl_surface(), (0, 0));
|
||||
popup.set_location(input_popup_location(parent, cursor, bounds.size, output));
|
||||
}
|
||||
}
|
||||
|
||||
fn input_popup_location(
|
||||
parent: Rectangle<i32, Logical>,
|
||||
cursor: Rectangle<i32, Logical>,
|
||||
size: Size<i32, Logical>,
|
||||
output: Rectangle<i32, Logical>,
|
||||
) -> Point<i32, Logical> {
|
||||
let caret = parent.loc + cursor.loc;
|
||||
let mut y = caret.y + cursor.size.h;
|
||||
if y + size.h > output.loc.y + output.size.h {
|
||||
y = caret.y - size.h;
|
||||
}
|
||||
let x = caret.x.clamp(
|
||||
output.loc.x,
|
||||
(output.loc.x + output.size.w - size.w).max(output.loc.x),
|
||||
);
|
||||
let y = y.clamp(
|
||||
output.loc.y,
|
||||
(output.loc.y + output.size.h - size.h).max(output.loc.y),
|
||||
);
|
||||
Point::from((x, y)) - parent.loc
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn input_popup_uses_cursor_and_flips_at_output_edges() {
|
||||
let output = Rectangle::new((0, 0).into(), (1280, 800).into());
|
||||
let parent = Rectangle::new((400, 36).into(), (880, 560).into());
|
||||
assert_eq!(
|
||||
input_popup_location(
|
||||
parent,
|
||||
Rectangle::new((28, 164).into(), (1, 24).into()),
|
||||
(420, 219).into(),
|
||||
output
|
||||
),
|
||||
Point::from((28, 188))
|
||||
);
|
||||
assert_eq!(
|
||||
input_popup_location(
|
||||
Rectangle::default(),
|
||||
Rectangle::new((1230, 750).into(), (1, 20).into()),
|
||||
(420, 220).into(),
|
||||
output
|
||||
),
|
||||
Point::from((860, 530))
|
||||
);
|
||||
let left = Rectangle::new((-1280, 0).into(), (1280, 800).into());
|
||||
assert_eq!(
|
||||
input_popup_location(
|
||||
Rectangle::default(),
|
||||
Rectangle::new((-10, 780).into(), (1, 20).into()),
|
||||
(420, 220).into(),
|
||||
left
|
||||
),
|
||||
Point::from((-420, 560))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ pub(crate) mod screenshot_worker;
|
||||
pub(crate) mod security_context;
|
||||
#[cfg(test)]
|
||||
mod toplevel_icon_tests;
|
||||
mod virtual_keyboard;
|
||||
pub(crate) mod window_capture;
|
||||
mod xdg_shell;
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
use smithay::backend::input::{Event, InputEvent, KeyboardKeyEvent};
|
||||
use smithay::utils::SERIAL_COUNTER;
|
||||
use smithay::wayland::input_method::InputMethodSeat;
|
||||
use smithay::wayland::virtual_keyboard::{
|
||||
VirtualKeyboardBackend, VirtualKeyboardHandler, VirtualKeyboardSpecialEvent,
|
||||
};
|
||||
|
||||
use crate::Ferese;
|
||||
|
||||
impl VirtualKeyboardHandler for Ferese {
|
||||
fn process_virtual_keyboard_event(&mut self, event: InputEvent<VirtualKeyboardBackend>) {
|
||||
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
||||
if self.session_lock.active() {
|
||||
// Synthetic input never enters a lock surface. Still reconcile
|
||||
// keys from a departing/previously active auxiliary device.
|
||||
let device = match &event {
|
||||
InputEvent::Keyboard { event } => Some(event.device()),
|
||||
InputEvent::DeviceRemoved { device } => Some(device.clone()),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(device) = device {
|
||||
keyboard.release_source(self, device.source());
|
||||
}
|
||||
return;
|
||||
}
|
||||
match event {
|
||||
InputEvent::DeviceAdded { device }
|
||||
| InputEvent::Special(VirtualKeyboardSpecialEvent::KeymapChanged { device }) => {
|
||||
if let Some(keymap) = device.keymap() {
|
||||
let current = keyboard.with_xkb_state(self, |context| {
|
||||
context.xkb().lock().unwrap().keymap_as_string()
|
||||
});
|
||||
if current != *keymap
|
||||
&& let Err(error) =
|
||||
keyboard.set_keymap_from_string(self, keymap.to_string())
|
||||
{
|
||||
tracing::warn!(%error, "invalid virtual keyboard keymap");
|
||||
}
|
||||
}
|
||||
}
|
||||
InputEvent::Keyboard { event } => {
|
||||
let device = event.device();
|
||||
let grab_client = self.seat.input_method().keyboard_grab_client();
|
||||
if grab_client.is_some() && grab_client == device.client() {
|
||||
// An IME forwards keys it did not consume. The physical path
|
||||
// already updated XKB and compositor shortcuts: forwarding
|
||||
// through that path again would recurse into the IME grab.
|
||||
keyboard.input_forward_bypassing_grab(
|
||||
self,
|
||||
event.key_code(),
|
||||
event.state(),
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
event.time(),
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
self.process_input_event_from_source::<VirtualKeyboardBackend>(
|
||||
InputEvent::Keyboard { event },
|
||||
device.source(),
|
||||
);
|
||||
}
|
||||
}
|
||||
InputEvent::Special(VirtualKeyboardSpecialEvent::Modifiers {
|
||||
device,
|
||||
mods_depressed,
|
||||
mods_latched,
|
||||
mods_locked,
|
||||
group,
|
||||
}) => {
|
||||
let grab_client = self.seat.input_method().keyboard_grab_client();
|
||||
if grab_client.is_some() && grab_client == device.client() {
|
||||
// The physical path already owns these modifiers. An
|
||||
// asynchronous IME echo must not restore an old mask after
|
||||
// the physical release has arrived.
|
||||
return;
|
||||
}
|
||||
keyboard.with_xkb_state(self, |mut context| {
|
||||
context.set_modifier_mask(mods_depressed, mods_latched, mods_locked, group)
|
||||
});
|
||||
}
|
||||
InputEvent::DeviceRemoved { device } => keyboard.release_source(self, device.source()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user