diff --git a/CHANGELOG.md b/CHANGELOG.md index 6679fdf83f..d53278c77f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Wayland, fixed `Ime::Preedit` not being sent on IME reset. - Fixed unbound version specified for `raw-window-handle` leading to compilation failures. - Empty `Ime::Preedit` event will be sent before `Ime::Commit` to help clearing preedit. +- On X11, fixed IME context picking by querying for supported styles beforehand. # 0.27.2 (2022-8-12) diff --git a/src/platform_impl/linux/x11/ime/callbacks.rs b/src/platform_impl/linux/x11/ime/callbacks.rs index 566cdcfe09..1f6bc23b1b 100644 --- a/src/platform_impl/linux/x11/ime/callbacks.rs +++ b/src/platform_impl/linux/x11/ime/callbacks.rs @@ -108,17 +108,17 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> { let mut new_contexts = HashMap::new(); for (window, old_context) in (*inner).contexts.iter() { let spot = old_context.as_ref().map(|old_context| old_context.ic_spot); - let is_allowed = old_context + let style = old_context .as_ref() - .map(|old_context| old_context.is_allowed) + .map(|old_context| old_context.style) .unwrap_or_default(); let new_context = { let result = ImeContext::new( xconn, - &new_im, + new_im.im, + style, *window, spot, - is_allowed, (*inner).event_sender.clone(), ); if result.is_err() { diff --git a/src/platform_impl/linux/x11/ime/context.rs b/src/platform_impl/linux/x11/ime/context.rs index dd031b8d7d..8870cad5de 100644 --- a/src/platform_impl/linux/x11/ime/context.rs +++ b/src/platform_impl/linux/x11/ime/context.rs @@ -5,8 +5,8 @@ use std::{mem, ptr}; use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct}; +use crate::platform_impl::platform::x11::ime::input_method::{Style, XIMStyle}; use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender}; -use crate::platform_impl::platform::x11::ime::input_method::InputMethod; use super::{ffi, util, XConnection, XError}; @@ -192,9 +192,9 @@ struct ImeContextClientData { // still exists on the server. Since `ImeInner` has that awareness, destruction must be handled // through `ImeInner`. pub struct ImeContext { - pub(super) ic: ffi::XIC, - pub(super) ic_spot: ffi::XPoint, - pub(super) is_allowed: bool, + pub(crate) ic: ffi::XIC, + pub(crate) ic_spot: ffi::XPoint, + pub(crate) style: Style, // Since the data is passed shared between X11 XIM callbacks, but couldn't be direclty free from // there we keep the pointer to automatically deallocate it. _client_data: Box, @@ -203,10 +203,10 @@ pub struct ImeContext { impl ImeContext { pub unsafe fn new( xconn: &Arc, - im: &InputMethod, + im: ffi::XIM, + style: Style, window: ffi::Window, ic_spot: Option, - is_allowed: bool, event_sender: ImeEventSender, ) -> Result { let client_data = Box::into_raw(Box::new(ImeContextClientData { @@ -216,12 +216,18 @@ impl ImeContext { cursor_pos: 0, })); - let ic = if is_allowed { - ImeContext::create_ic(xconn, im.im, window, client_data as ffi::XPointer) - .ok_or(ImeContextCreationError::Null)? - } else { - ImeContext::create_none_ic(xconn, im.im, window).ok_or(ImeContextCreationError::Null)? - }; + let ic = match style as _ { + Style::Preedit(style) => ImeContext::create_preedit_ic( + xconn, + im, + style, + window, + client_data as ffi::XPointer, + ), + Style::Nothing(style) => ImeContext::create_nothing_ic(xconn, im, style, window), + Style::None(style) => ImeContext::create_none_ic(xconn, im, style, window), + } + .ok_or(ImeContextCreationError::Null)?; xconn .check_errors() @@ -230,7 +236,7 @@ impl ImeContext { let mut context = ImeContext { ic, ic_spot: ffi::XPoint { x: 0, y: 0 }, - is_allowed, + style, _client_data: Box::from_raw(client_data), }; @@ -245,12 +251,13 @@ impl ImeContext { unsafe fn create_none_ic( xconn: &Arc, im: ffi::XIM, + style: XIMStyle, window: ffi::Window, ) -> Option { let ic = (xconn.xlib.XCreateIC)( im, ffi::XNInputStyle_0.as_ptr() as *const _, - ffi::XIMPreeditNone | ffi::XIMStatusNone, + style, ffi::XNClientWindow_0.as_ptr() as *const _, window, ptr::null_mut::<()>(), @@ -259,9 +266,10 @@ impl ImeContext { (!ic.is_null()).then(|| ic) } - unsafe fn create_ic( + unsafe fn create_preedit_ic( xconn: &Arc, im: ffi::XIM, + style: XIMStyle, window: ffi::Window, client_data: ffi::XPointer, ) -> Option { @@ -283,32 +291,35 @@ impl ImeContext { ) .expect("XVaCreateNestedList returned NULL"); - let ic = { - let ic = (xconn.xlib.XCreateIC)( - im, - ffi::XNInputStyle_0.as_ptr() as *const _, - ffi::XIMPreeditCallbacks | ffi::XIMStatusNothing, - ffi::XNClientWindow_0.as_ptr() as *const _, - window, - ffi::XNPreeditAttributes_0.as_ptr() as *const _, - preedit_attr.ptr, - ptr::null_mut::<()>(), - ); + let ic = (xconn.xlib.XCreateIC)( + im, + ffi::XNInputStyle_0.as_ptr() as *const _, + style, + ffi::XNClientWindow_0.as_ptr() as *const _, + window, + ffi::XNPreeditAttributes_0.as_ptr() as *const _, + preedit_attr.ptr, + ptr::null_mut::<()>(), + ); - // If we've failed to create IC with preedit callbacks fallback to normal one. - if ic.is_null() { - (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::<()>(), - ) - } else { - ic - } - }; + (!ic.is_null()).then(|| ic) + } + + unsafe fn create_nothing_ic( + xconn: &Arc, + im: ffi::XIM, + style: XIMStyle, + window: ffi::Window, + ) -> Option { + // If we've failed to create IC with preedit callbacks fallback to normal one. + let ic = (xconn.xlib.XCreateIC)( + im, + ffi::XNInputStyle_0.as_ptr() as *const _, + style, + ffi::XNClientWindow_0.as_ptr() as *const _, + window, + ptr::null_mut::<()>(), + ); (!ic.is_null()).then(|| ic) } @@ -327,13 +338,17 @@ impl ImeContext { xconn.check_errors() } + pub fn is_allowed(&self) -> bool { + !matches!(self.style, Style::None(_)) + } + // Set the spot for preedit text. Setting spot isn't working with libX11 when preedit callbacks // are being used. Certain IMEs do show selection window, but it's placed in bottom left of the // window and couldn't be changed. // // For me see: https://bugs.freedesktop.org/show_bug.cgi?id=1580. pub fn set_spot(&mut self, xconn: &Arc, x: c_short, y: c_short) { - if !self.is_allowed || self.ic_spot.x == x && self.ic_spot.y == y { + if !self.is_allowed() || self.ic_spot.x == x && self.ic_spot.y == y { return; } diff --git a/src/platform_impl/linux/x11/ime/input_method.rs b/src/platform_impl/linux/x11/ime/input_method.rs index 945c4a1e45..9dc36eda97 100644 --- a/src/platform_impl/linux/x11/ime/input_method.rs +++ b/src/platform_impl/linux/x11/ime/input_method.rs @@ -40,7 +40,8 @@ unsafe fn open_im(xconn: &Arc, locale_modifiers: &CStr) -> Option, + pub preedit_style: Option