x11: Add XIM support (#11657)
This pull request adds XIM (X Input Method) support to x11 platform. The implementation utilizes [xim-rs](https://crates.io/crates/xim), a XIM library written entirely in Rust, to provide asynchronous XIM communication. Preedit and candidate positioning are fully supported in the editor interface, yet notably absent in the terminal environment. This work is sponsored by [Rainlab Inc.](https://rainlab.co.jp/en/) Release Notes: - N/A --------- Signed-off-by: npmania <np@mkv.li>
This commit is contained in:
parent
97691c1def
commit
b60254feca
6 changed files with 423 additions and 5 deletions
154
crates/gpui/src/platform/linux/x11/xim_handler.rs
Normal file
154
crates/gpui/src/platform/linux/x11/xim_handler.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use std::cell::RefCell;
|
||||
use std::default::Default;
|
||||
use std::rc::Rc;
|
||||
|
||||
use calloop::channel;
|
||||
|
||||
use x11rb::protocol::{xproto, Event};
|
||||
use xim::{AHashMap, AttributeName, Client, ClientError, ClientHandler, InputStyle, Point};
|
||||
|
||||
use crate::{Keystroke, PlatformInput, X11ClientState};
|
||||
|
||||
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 xim_tx: channel::Sender<XimCallbackEvent>,
|
||||
pub connected: bool,
|
||||
pub window: xproto::Window,
|
||||
}
|
||||
|
||||
impl XimHandler {
|
||||
pub fn new(xim_tx: channel::Sender<XimCallbackEvent>) -> Self {
|
||||
Self {
|
||||
im_id: Default::default(),
|
||||
ic_id: Default::default(),
|
||||
xim_tx,
|
||||
connected: false,
|
||||
window: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
| InputStyle::STATUS_NOTHING
|
||||
| InputStyle::PREEDIT_NONE,
|
||||
)
|
||||
.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.xim_tx.send(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.xim_tx
|
||||
.send(XimCallbackEvent::XimXEvent(Event::KeyPress(xev)));
|
||||
}
|
||||
x11rb::protocol::xproto::KEY_RELEASE_EVENT => {
|
||||
self.xim_tx
|
||||
.send(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev)));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_close(&mut self, client: &mut C, _input_method_id: u16) -> Result<(), ClientError> {
|
||||
client.disconnect()
|
||||
}
|
||||
|
||||
fn handle_destroy_ic(
|
||||
&mut self,
|
||||
client: &mut C,
|
||||
input_method_id: u16,
|
||||
_input_context_id: u16,
|
||||
) -> Result<(), ClientError> {
|
||||
client.close(input_method_id)
|
||||
}
|
||||
|
||||
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.
|
||||
let mark_range = self.xim_tx.send(XimCallbackEvent::XimPreeditEvent(
|
||||
self.window,
|
||||
String::from(preedit_string),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue