1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-11 14:25:57 +03:00

Implement inputhandler

This commit is contained in:
Timmy Xiao 2024-01-22 23:21:28 -08:00 committed by Wez Furlong
parent a3eb8a8bd4
commit 5b2f960438
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
5 changed files with 214 additions and 9 deletions

View File

@ -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);
}
}
}
}

View File

@ -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;

View File

@ -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() {

View File

@ -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)?,

View File

@ -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 {