mirror of
https://github.com/wez/wezterm.git
synced 2024-11-22 22:42:48 +03:00
Implement inputhandler
This commit is contained in:
parent
a3eb8a8bd4
commit
5b2f960438
@ -1,20 +1,117 @@
|
||||
//! Implements zwp_text_input_v3 for handling IME
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use smithay_client_toolkit::globals::GlobalData;
|
||||
use wayland_client::backend::ObjectId;
|
||||
use wayland_client::globals::{BindError, GlobalList};
|
||||
use wayland_client::protocol::wl_keyboard::WlKeyboard;
|
||||
use wayland_client::protocol::wl_seat::WlSeat;
|
||||
use wayland_client::protocol::wl_surface::WlSurface;
|
||||
use wayland_client::{Dispatch, Proxy, QueueHandle};
|
||||
use wayland_protocols::wp::text_input::zv3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
|
||||
use wayland_protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3;
|
||||
use wayland_protocols::wp::text_input::zv3::client::zwp_text_input_v3::{
|
||||
Event as TextInputEvent, ZwpTextInputV3,
|
||||
};
|
||||
use wezterm_input_types::{KeyCode, KeyEvent, KeyboardLedStatus, Modifiers};
|
||||
|
||||
use crate::{DeadKeyStatus, WindowEvent};
|
||||
|
||||
use super::state::WaylandState;
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct PendingState {
|
||||
pre_edit: Option<String>,
|
||||
commit: Option<String>,
|
||||
}
|
||||
|
||||
pub(super) struct TextInputState {
|
||||
text_input_manager: ZwpTextInputManagerV3,
|
||||
inner: Mutex<Inner>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Inner {
|
||||
input_by_seat: HashMap<ObjectId, ZwpTextInputV3>,
|
||||
keyboard_to_seat: HashMap<ObjectId, ObjectId>,
|
||||
surface_to_keyboard: HashMap<ObjectId, ObjectId>,
|
||||
pending_state: HashMap<ObjectId, PendingState>,
|
||||
}
|
||||
|
||||
impl TextInputState {
|
||||
pub(super) fn bind(
|
||||
globals: &GlobalList,
|
||||
queue_handle: &QueueHandle<WaylandState>,
|
||||
) -> Result<Self, BindError> {
|
||||
let text_input_manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
|
||||
Ok(Self {
|
||||
text_input_manager,
|
||||
inner: Mutex::new(Inner::default()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_text_input_for_keyboard(&self, keyboard: &WlKeyboard) -> Option<ZwpTextInputV3> {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
let keyboard_id = keyboard.id();
|
||||
let seat_id = inner.keyboard_to_seat.get(&keyboard_id)?;
|
||||
inner.input_by_seat.get(&seat_id).cloned()
|
||||
}
|
||||
|
||||
pub(super) fn get_text_input_for_surface(&self, surface: &WlSurface) -> Option<ZwpTextInputV3> {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
let surface_id = surface.id();
|
||||
let keyboard_id = inner.surface_to_keyboard.get(&surface_id)?;
|
||||
let seat_id = inner.keyboard_to_seat.get(&keyboard_id)?;
|
||||
inner.input_by_seat.get(&seat_id).cloned()
|
||||
}
|
||||
|
||||
fn get_text_input_for_seat(
|
||||
&self,
|
||||
seat: &WlSeat,
|
||||
qh: &QueueHandle<WaylandState>,
|
||||
) -> Option<ZwpTextInputV3> {
|
||||
let mgr = &self.text_input_manager;
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let seat_id = seat.id();
|
||||
let input = inner.input_by_seat.entry(seat_id).or_insert_with(|| {
|
||||
let input = mgr.get_text_input(seat, &qh, TextInputData::default());
|
||||
input.into()
|
||||
});
|
||||
Some(input.clone())
|
||||
}
|
||||
|
||||
pub(super) fn advise_surface(&self, surface: &WlSurface, keyboard: &WlKeyboard) {
|
||||
let surface_id = surface.id();
|
||||
let keyboard_id = keyboard.id();
|
||||
self.inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.surface_to_keyboard
|
||||
.insert(surface_id, keyboard_id);
|
||||
}
|
||||
|
||||
pub(super) fn advise_seat(
|
||||
&self,
|
||||
seat: &WlSeat,
|
||||
keyboard: &WlKeyboard,
|
||||
qh: &QueueHandle<WaylandState>,
|
||||
) {
|
||||
self.get_text_input_for_seat(seat, qh);
|
||||
let keyboard_id = keyboard.id();
|
||||
let seat_id = seat.id();
|
||||
self.inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.keyboard_to_seat
|
||||
.insert(keyboard_id, seat_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct TextInputData {
|
||||
inner: Mutex<TextInputDataInner>,
|
||||
// XXX: inner could probably be moved here
|
||||
_inner: Mutex<TextInputDataInner>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -29,19 +126,80 @@ impl Dispatch<ZwpTextInputManagerV3, GlobalData, WaylandState> for TextInputStat
|
||||
_conn: &wayland_client::Connection,
|
||||
_qhandle: &QueueHandle<WaylandState>,
|
||||
) {
|
||||
todo!()
|
||||
// No events from ZwpTextInputMangerV3
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<ZwpTextInputV3, TextInputData, WaylandState> for TextInputState {
|
||||
fn event(
|
||||
_state: &mut WaylandState,
|
||||
_proxy: &ZwpTextInputV3,
|
||||
_event: <ZwpTextInputV3 as Proxy>::Event,
|
||||
state: &mut WaylandState,
|
||||
input: &ZwpTextInputV3,
|
||||
event: <ZwpTextInputV3 as Proxy>::Event,
|
||||
_data: &TextInputData,
|
||||
_conn: &wayland_client::Connection,
|
||||
_qhandle: &QueueHandle<WaylandState>,
|
||||
) {
|
||||
todo!()
|
||||
log::trace!("ZwpTextInputEvent: {event:?}");
|
||||
let mut pending_state = {
|
||||
let text_input = &mut state.text_input;
|
||||
let mut inner = text_input.inner.lock().unwrap();
|
||||
inner.pending_state.entry(input.id()).or_default().clone()
|
||||
};
|
||||
|
||||
match event {
|
||||
TextInputEvent::PreeditString {
|
||||
text,
|
||||
cursor_begin: _,
|
||||
cursor_end: _,
|
||||
} => {
|
||||
pending_state.pre_edit = text;
|
||||
}
|
||||
TextInputEvent::CommitString { text } => {
|
||||
pending_state.commit = text;
|
||||
state.dispatch_to_focused_window(WindowEvent::AdviseDeadKeyStatus(
|
||||
DeadKeyStatus::None,
|
||||
));
|
||||
}
|
||||
TextInputEvent::Done { serial } => {
|
||||
*state.last_serial.borrow_mut() = serial;
|
||||
if let Some(text) = pending_state.commit.take() {
|
||||
state.dispatch_to_focused_window(WindowEvent::KeyEvent(KeyEvent {
|
||||
key: KeyCode::composed(&text),
|
||||
modifiers: Modifiers::NONE,
|
||||
leds: KeyboardLedStatus::empty(),
|
||||
repeat_count: 1,
|
||||
key_is_down: true,
|
||||
raw: None,
|
||||
}));
|
||||
}
|
||||
let status = if let Some(text) = pending_state.pre_edit.take() {
|
||||
DeadKeyStatus::Composing(text)
|
||||
} else {
|
||||
DeadKeyStatus::None
|
||||
};
|
||||
state.dispatch_to_focused_window(WindowEvent::AdviseDeadKeyStatus(status));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
state
|
||||
.text_input
|
||||
.inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.pending_state
|
||||
.insert(input.id(), pending_state);
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandState {
|
||||
fn dispatch_to_focused_window(&self, event: WindowEvent) {
|
||||
if let Some(&window_id) = self.keyboard_window_id.borrow().as_ref() {
|
||||
if let Some(win) = self.window_by_id(window_id) {
|
||||
let mut inner = win.borrow_mut();
|
||||
inner.events.dispatch(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use super::SurfaceUserData;
|
||||
impl Dispatch<WlKeyboard, KeyboardData> for WaylandState {
|
||||
fn event(
|
||||
state: &mut WaylandState,
|
||||
_proxy: &WlKeyboard,
|
||||
keyboard: &WlKeyboard,
|
||||
event: <WlKeyboard as wayland_client::Proxy>::Event,
|
||||
_data: &KeyboardData,
|
||||
_conn: &wayland_client::Connection,
|
||||
@ -31,13 +31,21 @@ impl Dispatch<WlKeyboard, KeyboardData> for WaylandState {
|
||||
if let Some(sud) = SurfaceUserData::try_from_wl(&surface) {
|
||||
let window_id = sud.window_id;
|
||||
state.keyboard_window_id.borrow_mut().replace(window_id);
|
||||
if let Some(input) = state.text_input.get_text_input_for_keyboard(keyboard) {
|
||||
input.enable();
|
||||
input.commit();
|
||||
}
|
||||
state.text_input.advise_surface(surface, keyboard);
|
||||
} else {
|
||||
log::warn!("{:?}, no known surface", event);
|
||||
}
|
||||
}
|
||||
WlKeyboardEvent::Leave { serial, .. } => {
|
||||
// TODO: I know nothing about the input handler currently
|
||||
*state.last_serial.borrow_mut() = *serial;
|
||||
if let Some(input) = state.text_input.get_text_input_for_keyboard(keyboard) {
|
||||
input.disable();
|
||||
input.commit();
|
||||
}
|
||||
}
|
||||
WlKeyboardEvent::Key { serial, .. } | WlKeyboardEvent::Modifiers { serial, .. } => {
|
||||
*state.last_serial.borrow_mut() = *serial;
|
||||
|
@ -28,6 +28,8 @@ impl SeatHandler for WaylandState {
|
||||
log::trace!("Setting keyboard capability");
|
||||
let keyboard = seat.get_keyboard(qh, KeyboardData {});
|
||||
self.keyboard = Some(keyboard.clone());
|
||||
|
||||
self.text_input.advise_seat(&seat, &keyboard, qh);
|
||||
}
|
||||
|
||||
if capability == Capability::Pointer && self.pointer.is_none() {
|
||||
|
@ -42,6 +42,7 @@ pub(super) struct WaylandState {
|
||||
registry: RegistryState,
|
||||
pub(super) output: OutputState,
|
||||
pub(super) compositor: CompositorState,
|
||||
pub(super) text_input: TextInputState,
|
||||
pub(super) seat: SeatState,
|
||||
pub(super) xdg: XdgShell,
|
||||
pub(super) windows: RefCell<HashMap<usize, Rc<RefCell<WaylandWindowInner>>>>,
|
||||
@ -72,6 +73,7 @@ impl WaylandState {
|
||||
registry: RegistryState::new(globals),
|
||||
output: OutputState::new(globals, qh),
|
||||
compositor: CompositorState::bind(globals, qh)?,
|
||||
text_input: TextInputState::bind(globals, qh)?,
|
||||
windows: RefCell::new(HashMap::new()),
|
||||
seat: SeatState::new(globals, qh),
|
||||
xdg: XdgShell::bind(globals, qh)?,
|
||||
|
@ -354,6 +354,13 @@ impl WindowOps for WaylandWindow {
|
||||
});
|
||||
}
|
||||
|
||||
fn set_text_cursor_position(&self, cursor: Rect) {
|
||||
WaylandConnection::with_window_inner(self.0, move |inner| {
|
||||
inner.set_text_cursor_position(cursor);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
let title = title.to_owned();
|
||||
WaylandConnection::with_window_inner(self.0, |inner| {
|
||||
@ -851,6 +858,34 @@ impl WaylandWindowInner {
|
||||
self.do_paint().unwrap();
|
||||
}
|
||||
|
||||
fn set_text_cursor_position(&mut self, rect: Rect) {
|
||||
let conn = WaylandConnection::get().unwrap().wayland();
|
||||
let state = &conn.wayland_state.borrow();
|
||||
let surface = self.surface().clone();
|
||||
|
||||
let surface_id = surface.id();
|
||||
let active_surface_id = state.active_surface_id.borrow().as_ref().unwrap().clone();
|
||||
|
||||
if surface_id == active_surface_id {
|
||||
if self.text_cursor.map(|prior| prior != rect).unwrap_or(true) {
|
||||
self.text_cursor.replace(rect);
|
||||
|
||||
let surface_udata = SurfaceUserData::from_wl(&surface);
|
||||
let factor = surface_udata.surface_data().scale_factor();
|
||||
|
||||
if let Some(input) = state.text_input.get_text_input_for_surface(&surface) {
|
||||
input.set_cursor_rectangle(
|
||||
rect.min_x() as i32 / factor,
|
||||
rect.min_y() as i32 / factor,
|
||||
rect.width() as i32 / factor,
|
||||
rect.height() as i32 / factor,
|
||||
);
|
||||
input.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_title(&mut self, title: String) {
|
||||
if let Some(last_title) = self.title.as_ref() {
|
||||
if last_title == &title {
|
||||
|
Loading…
Reference in New Issue
Block a user