
Closes #15833 Related to [#12495 comment](https://github.com/zed-industries/zed/pull/12495#issuecomment-2328356125) Destroying and recreating the Input context was the only way to reset the IME but it's making the keyboard unresponsive sometimes due to a XIM error. The keyboard will still be unresponsive if you close your IME while using zed, but I don't know how to fix this. * Fixed preedit drawing for CJK * Fixed unresponsive keyboard by properly implementing reset_ic in `xim-rs` Release Notes: - N/A
133 lines
4 KiB
Rust
133 lines
4 KiB
Rust
use std::default::Default;
|
|
|
|
use x11rb::protocol::{xproto, Event};
|
|
use xim::{AHashMap, AttributeName, Client, ClientError, ClientHandler, InputStyle};
|
|
|
|
pub enum XimCallbackEvent {
|
|
XimXEvent(x11rb::protocol::Event),
|
|
XimPreeditEvent(xproto::Window, String),
|
|
XimCommitEvent(xproto::Window, String),
|
|
}
|
|
|
|
pub struct XimHandler {
|
|
pub im_id: u16,
|
|
pub ic_id: u16,
|
|
pub connected: bool,
|
|
pub window: xproto::Window,
|
|
pub last_callback_event: Option<XimCallbackEvent>,
|
|
}
|
|
|
|
impl XimHandler {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
im_id: Default::default(),
|
|
ic_id: Default::default(),
|
|
connected: false,
|
|
window: Default::default(),
|
|
last_callback_event: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<C: Client<XEvent = xproto::KeyPressEvent>> ClientHandler<C> for XimHandler {
|
|
fn handle_connect(&mut self, client: &mut C) -> Result<(), ClientError> {
|
|
client.open("C")
|
|
}
|
|
|
|
fn handle_open(&mut self, client: &mut C, input_method_id: u16) -> Result<(), ClientError> {
|
|
self.im_id = input_method_id;
|
|
|
|
client.get_im_values(input_method_id, &[AttributeName::QueryInputStyle])
|
|
}
|
|
|
|
fn handle_get_im_values(
|
|
&mut self,
|
|
client: &mut C,
|
|
input_method_id: u16,
|
|
_attributes: AHashMap<AttributeName, Vec<u8>>,
|
|
) -> Result<(), ClientError> {
|
|
let ic_attributes = client
|
|
.build_ic_attributes()
|
|
.push(AttributeName::InputStyle, InputStyle::PREEDIT_CALLBACKS)
|
|
.push(AttributeName::ClientWindow, self.window)
|
|
.push(AttributeName::FocusWindow, self.window)
|
|
.build();
|
|
client.create_ic(input_method_id, ic_attributes)
|
|
}
|
|
|
|
fn handle_create_ic(
|
|
&mut self,
|
|
_client: &mut C,
|
|
_input_method_id: u16,
|
|
input_context_id: u16,
|
|
) -> Result<(), ClientError> {
|
|
self.connected = true;
|
|
self.ic_id = input_context_id;
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_commit(
|
|
&mut self,
|
|
_client: &mut C,
|
|
_input_method_id: u16,
|
|
_input_context_id: u16,
|
|
text: &str,
|
|
) -> Result<(), ClientError> {
|
|
self.last_callback_event = Some(XimCallbackEvent::XimCommitEvent(
|
|
self.window,
|
|
String::from(text),
|
|
));
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_forward_event(
|
|
&mut self,
|
|
_client: &mut C,
|
|
_input_method_id: u16,
|
|
_input_context_id: u16,
|
|
_flag: xim::ForwardEventFlag,
|
|
xev: C::XEvent,
|
|
) -> Result<(), ClientError> {
|
|
match xev.response_type {
|
|
x11rb::protocol::xproto::KEY_PRESS_EVENT => {
|
|
self.last_callback_event = Some(XimCallbackEvent::XimXEvent(Event::KeyPress(xev)));
|
|
}
|
|
x11rb::protocol::xproto::KEY_RELEASE_EVENT => {
|
|
self.last_callback_event =
|
|
Some(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev)));
|
|
}
|
|
_ => {}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_close(&mut self, client: &mut C, _input_method_id: u16) -> Result<(), ClientError> {
|
|
client.disconnect()
|
|
}
|
|
|
|
fn handle_preedit_draw(
|
|
&mut self,
|
|
_client: &mut C,
|
|
_input_method_id: u16,
|
|
_input_context_id: u16,
|
|
_caret: i32,
|
|
_chg_first: i32,
|
|
_chg_len: i32,
|
|
_status: xim::PreeditDrawStatus,
|
|
preedit_string: &str,
|
|
_feedbacks: Vec<xim::Feedback>,
|
|
) -> Result<(), ClientError> {
|
|
// XIMReverse: 1, XIMPrimary: 8, XIMTertiary: 32: selected text
|
|
// XIMUnderline: 2, XIMSecondary: 16: underlined text
|
|
// XIMHighlight: 4: normal text
|
|
// XIMVisibleToForward: 64, XIMVisibleToBackward: 128, XIMVisibleCenter: 256: text align position
|
|
// XIMPrimary, XIMHighlight, XIMSecondary, XIMTertiary are not specified,
|
|
// but interchangeable as above
|
|
// Currently there's no way to support these.
|
|
self.last_callback_event = Some(XimCallbackEvent::XimPreeditEvent(
|
|
self.window,
|
|
String::from(preedit_string),
|
|
));
|
|
Ok(())
|
|
}
|
|
}
|