x11: Only access XIM from the event loop thread (#439)
XIM isn't thread-safe at all. Any call made to it from another thread will result in the event loop freezing (this is why the old implementation of Drop for Window had that problem). XIM is now confined to one thread, and the existing API is maintained using channels. In testing this with Alacritty, I initially thought the occasional slight lag on updating the spot location was due to this change, but it's present without it as well.
This commit is contained in:
parent
7cd440135a
commit
1c4973d5b7
3 changed files with 162 additions and 115 deletions
|
|
@ -298,3 +298,114 @@ pub unsafe fn lookup_utf8(
|
|||
|
||||
str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
|
||||
}
|
||||
|
||||
// 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