mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 23:23:14 -04:00
Fixes #195 Fixes #277 Fixes #455 * Read `XMODIFIERS` explicitly/directly instead of calling `XSetLocaleModifiers` with an empty string. This is useful for debugging purposes, and more clear to read and handle. * Fallback to local input method if the one specified in `XMODIFIERS` is later closed on the server end (i.e. if ibus/fcitx is terminated). Previously, that would cause the event loop to freeze and usually also segfault. * If using the fallback input method, respond to the `XMODIFIERS` input method later becoming available. This means that the input method restarting is handled, and that even if the program was started while ibus/fcitx/etc. was unavailable, it will start using it as soon as it becomes available. * Only one input method is opened for the whole event loop, with each window having its own input context. * IME works completely out of the box now, no longer requiring application developers to call `setlocale` or `XSetLocaleModifiers`. * Detailed error messages are provided if no input method could be opened. However, no information is provided to the user if their intended `XMODIFIERS` input method failed to open but the fallbacks (which will ostensibly always succeed) succeeded; in my opinion, this is something that is best filled by adding a logging feature to winit.
135 lines
3.8 KiB
Rust
135 lines
3.8 KiB
Rust
use std::ptr;
|
|
use std::sync::Arc;
|
|
use std::os::raw::{c_short, c_void};
|
|
|
|
use super::{ffi, util, XConnection, XError};
|
|
|
|
#[derive(Debug)]
|
|
pub enum ImeContextCreationError {
|
|
XError(XError),
|
|
Null,
|
|
}
|
|
|
|
unsafe fn create_pre_edit_attr<'a>(
|
|
xconn: &'a Arc<XConnection>,
|
|
ic_spot: &'a ffi::XPoint,
|
|
) -> util::XSmartPointer<'a, c_void> {
|
|
util::XSmartPointer::new(
|
|
xconn,
|
|
(xconn.xlib.XVaCreateNestedList)(
|
|
0,
|
|
ffi::XNSpotLocation_0.as_ptr() as *const _,
|
|
ic_spot,
|
|
ptr::null_mut::<()>(),
|
|
),
|
|
).expect("XVaCreateNestedList returned NULL")
|
|
}
|
|
|
|
// WARNING: this struct doesn't destroy its XIC resource when dropped.
|
|
// This is intentional, as it doesn't have enough information to know whether or not the context
|
|
// still exists on the server. Since `ImeInner` has that awareness, destruction must be handled
|
|
// through `ImeInner`.
|
|
#[derive(Debug)]
|
|
pub struct ImeContext {
|
|
pub ic: ffi::XIC,
|
|
pub ic_spot: ffi::XPoint,
|
|
}
|
|
|
|
impl ImeContext {
|
|
pub unsafe fn new(
|
|
xconn: &Arc<XConnection>,
|
|
im: ffi::XIM,
|
|
window: ffi::Window,
|
|
ic_spot: Option<ffi::XPoint>,
|
|
) -> Result<Self, ImeContextCreationError> {
|
|
let ic = if let Some(ic_spot) = ic_spot {
|
|
ImeContext::create_ic_with_spot(xconn, im, window, ic_spot)
|
|
} else {
|
|
ImeContext::create_ic(xconn, im, window)
|
|
};
|
|
|
|
let ic = ic.ok_or(ImeContextCreationError::Null)?;
|
|
xconn.check_errors().map_err(ImeContextCreationError::XError)?;
|
|
|
|
Ok(ImeContext {
|
|
ic,
|
|
ic_spot: ic_spot.unwrap_or_else(|| ffi::XPoint { x: 0, y: 0 }),
|
|
})
|
|
}
|
|
|
|
unsafe fn create_ic(
|
|
xconn: &Arc<XConnection>,
|
|
im: ffi::XIM,
|
|
window: ffi::Window,
|
|
) -> Option<ffi::XIC> {
|
|
let ic = (xconn.xlib.XCreateIC)(
|
|
im,
|
|
ffi::XNInputStyle_0.as_ptr() as *const _,
|
|
ffi::XIMPreeditNothing | ffi::XIMStatusNothing,
|
|
ffi::XNClientWindow_0.as_ptr() as *const _,
|
|
window,
|
|
ptr::null_mut::<()>(),
|
|
);
|
|
if ic.is_null() {
|
|
None
|
|
} else {
|
|
Some(ic)
|
|
}
|
|
}
|
|
|
|
unsafe fn create_ic_with_spot(
|
|
xconn: &Arc<XConnection>,
|
|
im: ffi::XIM,
|
|
window: ffi::Window,
|
|
ic_spot: ffi::XPoint,
|
|
) -> Option<ffi::XIC> {
|
|
let pre_edit_attr = create_pre_edit_attr(xconn, &ic_spot);
|
|
let ic = (xconn.xlib.XCreateIC)(
|
|
im,
|
|
ffi::XNInputStyle_0.as_ptr() as *const _,
|
|
ffi::XIMPreeditNothing | ffi::XIMStatusNothing,
|
|
ffi::XNClientWindow_0.as_ptr() as *const _,
|
|
window,
|
|
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
|
|
pre_edit_attr.ptr,
|
|
ptr::null_mut::<()>(),
|
|
);
|
|
if ic.is_null() {
|
|
None
|
|
} else {
|
|
Some(ic)
|
|
}
|
|
}
|
|
|
|
pub fn focus(&self, xconn: &Arc<XConnection>) -> Result<(), XError> {
|
|
unsafe {
|
|
(xconn.xlib.XSetICFocus)(self.ic);
|
|
}
|
|
xconn.check_errors()
|
|
}
|
|
|
|
pub fn unfocus(&self, xconn: &Arc<XConnection>) -> Result<(), XError> {
|
|
unsafe {
|
|
(xconn.xlib.XUnsetICFocus)(self.ic);
|
|
}
|
|
xconn.check_errors()
|
|
}
|
|
|
|
pub fn set_spot(&mut self, xconn: &Arc<XConnection>, x: c_short, y: c_short) {
|
|
if self.ic_spot.x == x && self.ic_spot.y == y {
|
|
return;
|
|
}
|
|
self.ic_spot = ffi::XPoint { x, y };
|
|
|
|
unsafe {
|
|
let pre_edit_attr = create_pre_edit_attr(xconn, &self.ic_spot);
|
|
(xconn.xlib.XSetICValues)(
|
|
self.ic,
|
|
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
|
|
pre_edit_attr.ptr,
|
|
ptr::null_mut::<()>(),
|
|
);
|
|
}
|
|
}
|
|
}
|