x11: Overhaul XIM code (#451)
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.
This commit is contained in:
parent
f08bf44670
commit
09c809003b
8 changed files with 880 additions and 137 deletions
|
|
@ -90,7 +90,7 @@ pub unsafe fn send_client_msg(
|
|||
xconn.check_errors().map(|_| ())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GetPropertyError {
|
||||
XError(XError),
|
||||
TypeMismatch(ffi::Atom),
|
||||
|
|
@ -309,7 +309,6 @@ pub unsafe fn lookup_utf8(
|
|||
str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FrameExtents {
|
||||
pub left: c_ulong,
|
||||
|
|
@ -364,114 +363,3 @@ impl WindowGeometry {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Important: all XIM calls need to happen from the same thread!
|
||||
pub struct Ime {
|
||||
xconn: Arc<XConnection>,
|
||||
pub im: ffi::XIM,
|
||||
pub ic: ffi::XIC,
|
||||
ic_spot: ffi::XPoint,
|
||||
}
|
||||
|
||||
impl Ime {
|
||||
pub fn new(xconn: Arc<XConnection>, window: ffi::Window) -> Option<Self> {
|
||||
let im = unsafe {
|
||||
let mut im: ffi::XIM = ptr::null_mut();
|
||||
|
||||
// Setting an empty string as the locale modifier results in the user's XMODIFIERS
|
||||
// environment variable being read, which should result in the user's configured input
|
||||
// method (ibus, fcitx, etc.) being used. If that fails, we fall back to internal
|
||||
// input methods which should always be available, though only support compose keys.
|
||||
for modifiers in &[b"\0" as &[u8], b"@im=local\0", b"@im=\0"] {
|
||||
if !im.is_null() {
|
||||
break;
|
||||
}
|
||||
|
||||
(xconn.xlib.XSetLocaleModifiers)(modifiers.as_ptr() as *const _);
|
||||
im = (xconn.xlib.XOpenIM)(
|
||||
xconn.display,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
if im.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
im
|
||||
};
|
||||
|
||||
let ic = unsafe {
|
||||
let ic = (xconn.xlib.XCreateIC)(
|
||||
im,
|
||||
b"inputStyle\0".as_ptr() as *const _,
|
||||
ffi::XIMPreeditNothing | ffi::XIMStatusNothing,
|
||||
b"clientWindow\0".as_ptr() as *const _,
|
||||
window,
|
||||
ptr::null::<()>(),
|
||||
);
|
||||
if ic.is_null() {
|
||||
return None;
|
||||
}
|
||||
(xconn.xlib.XSetICFocus)(ic);
|
||||
xconn.check_errors().expect("Failed to call XSetICFocus");
|
||||
ic
|
||||
};
|
||||
|
||||
Some(Ime {
|
||||
xconn,
|
||||
im,
|
||||
ic,
|
||||
ic_spot: ffi::XPoint { x: 0, y: 0 },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn focus(&self) -> Result<(), XError> {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XSetICFocus)(self.ic);
|
||||
}
|
||||
self.xconn.check_errors()
|
||||
}
|
||||
|
||||
pub fn unfocus(&self) -> Result<(), XError> {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XUnsetICFocus)(self.ic);
|
||||
}
|
||||
self.xconn.check_errors()
|
||||
}
|
||||
|
||||
pub fn send_xim_spot(&mut self, x: i16, y: i16) {
|
||||
let nspot = ffi::XPoint { x: x as _, y: y as _ };
|
||||
if self.ic_spot.x == x && self.ic_spot.y == y {
|
||||
return;
|
||||
}
|
||||
self.ic_spot = nspot;
|
||||
unsafe {
|
||||
let preedit_attr = (self.xconn.xlib.XVaCreateNestedList)(
|
||||
0,
|
||||
b"spotLocation\0",
|
||||
&nspot,
|
||||
ptr::null::<()>(),
|
||||
);
|
||||
(self.xconn.xlib.XSetICValues)(
|
||||
self.ic,
|
||||
b"preeditAttributes\0",
|
||||
preedit_attr,
|
||||
ptr::null::<()>(),
|
||||
);
|
||||
(self.xconn.xlib.XFree)(preedit_attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Ime {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XDestroyIC)(self.ic);
|
||||
(self.xconn.xlib.XCloseIM)(self.im);
|
||||
}
|
||||
self.xconn.check_errors().expect("Failed to close input method");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue