diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index fbcee6214..38c723927 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -43,3 +43,7 @@ changelog entry. ### Changed - On Wayland, no longer send an explicit clearing `Ime::Preedit` just prior to a new `Ime::Preedit`. + +### Fixed + +- On X11, fixed crash with uim diff --git a/src/platform_impl/linux/x11/ime/callbacks.rs b/src/platform_impl/linux/x11/ime/callbacks.rs index c75724ec8..fa0fa5c6f 100644 --- a/src/platform_impl/linux/x11/ime/callbacks.rs +++ b/src/platform_impl/linux/x11/ime/callbacks.rs @@ -123,19 +123,15 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> { let is_allowed = old_context.as_ref().map(|old_context| old_context.is_allowed()).unwrap_or_default(); - // We can't use the style from the old context here, since it may change on reload, so - // pick style from the new XIM based on the old state. - let style = if is_allowed { new_im.preedit_style } else { new_im.none_style }; - let new_context = { let result = unsafe { ImeContext::new( xconn, - new_im.im, - style, + &new_im, *window, spot, (*inner).event_sender.clone(), + is_allowed, ) }; 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 89a241cce..e90836a5f 100644 --- a/src/platform_impl/linux/x11/ime/context.rs +++ b/src/platform_impl/linux/x11/ime/context.rs @@ -5,10 +5,9 @@ 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 super::{ffi, util, XConnection, XError}; +use crate::platform_impl::platform::x11::ime::input_method::{InputMethod, Style, XIMStyle}; +use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender}; /// IME creation error. #[derive(Debug)] @@ -184,7 +183,7 @@ struct ImeContextClientData { pub struct ImeContext { pub(crate) ic: ffi::XIC, pub(crate) ic_spot: ffi::XPoint, - pub(crate) style: Style, + pub(crate) allowed: bool, // Since the data is passed shared between X11 XIM callbacks, but couldn't be directly free // from there we keep the pointer to automatically deallocate it. _client_data: Box, @@ -193,11 +192,11 @@ pub struct ImeContext { impl ImeContext { pub(crate) unsafe fn new( xconn: &Arc, - im: ffi::XIM, - style: Style, + im: &InputMethod, window: ffi::Window, ic_spot: Option, event_sender: ImeEventSender, + allowed: bool, ) -> Result { let client_data = Box::into_raw(Box::new(ImeContextClientData { window, @@ -206,20 +205,24 @@ impl ImeContext { cursor_pos: 0, })); + let style = if allowed { im.preedit_style } else { im.none_style }; + let ic = match style as _ { Style::Preedit(style) => unsafe { ImeContext::create_preedit_ic( xconn, - im, + im.im, style, window, client_data as ffi::XPointer, ) }, Style::Nothing(style) => unsafe { - ImeContext::create_nothing_ic(xconn, im, style, window) + ImeContext::create_nothing_ic(xconn, im.im, style, window) + }, + Style::None(style) => unsafe { + ImeContext::create_none_ic(xconn, im.im, style, window) }, - Style::None(style) => unsafe { ImeContext::create_none_ic(xconn, im, style, window) }, } .ok_or(ImeContextCreationError::Null)?; @@ -228,7 +231,7 @@ impl ImeContext { let mut context = ImeContext { ic, ic_spot: ffi::XPoint { x: 0, y: 0 }, - style, + allowed, _client_data: unsafe { Box::from_raw(client_data) }, }; @@ -335,7 +338,7 @@ impl ImeContext { } pub fn is_allowed(&self) -> bool { - !matches!(self.style, Style::None(_)) + self.allowed } // Set the spot for preedit text. Setting spot isn't working with libX11 when preedit callbacks diff --git a/src/platform_impl/linux/x11/ime/input_method.rs b/src/platform_impl/linux/x11/ime/input_method.rs index 7f147bf1c..b9d3ca710 100644 --- a/src/platform_impl/linux/x11/ime/input_method.rs +++ b/src/platform_impl/linux/x11/ime/input_method.rs @@ -81,9 +81,7 @@ impl InputMethod { } let preedit_style = preedit_style.unwrap_or_else(|| none_style.unwrap()); - // Always initialize none style even when it's not advertised, since it seems to work - // regardless... - let none_style = none_style.unwrap_or(Style::None(XIM_NONE_STYLE)); + let none_style = none_style.unwrap_or(preedit_style); Some(InputMethod { im, _name: name, preedit_style, none_style }) } diff --git a/src/platform_impl/linux/x11/ime/mod.rs b/src/platform_impl/linux/x11/ime/mod.rs index 063598a3e..0a419c8d5 100644 --- a/src/platform_impl/linux/x11/ime/mod.rs +++ b/src/platform_impl/linux/x11/ime/mod.rs @@ -10,15 +10,13 @@ use std::sync::Arc; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use tracing::debug; - -use super::{ffi, util, XConnection, XError}; use self::callbacks::*; use self::context::ImeContext; pub use self::context::ImeContextCreationError; use self::inner::{close_im, ImeInner}; -use self::input_method::{PotentialInputMethods, Style}; +use self::input_method::PotentialInputMethods; +use super::{ffi, util, XConnection, XError}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -114,39 +112,26 @@ impl Ime { pub fn create_context( &mut self, window: ffi::Window, - with_preedit: bool, + with_ime: bool, ) -> Result { let context = if self.is_destroyed() { // Create empty entry in map, so that when IME is rebuilt, this window has a context. None } else { let im = self.inner.im.as_ref().unwrap(); - let style = if with_preedit { im.preedit_style } else { im.none_style }; let context = unsafe { ImeContext::new( &self.inner.xconn, - im.im, - style, + im, window, None, self.inner.event_sender.clone(), + with_ime, )? }; - // Check the state on the context, since it could fail to enable or disable preedit. - let event = if matches!(style, Style::None(_)) { - if with_preedit { - debug!("failed to create IME context with preedit support.") - } - ImeEvent::Disabled - } else { - if !with_preedit { - debug!("failed to create IME context without preedit support.") - } - ImeEvent::Enabled - }; - + let event = if context.is_allowed() { ImeEvent::Enabled } else { ImeEvent::Disabled }; self.inner.event_sender.send((window, event)).expect("Failed to send enabled event"); Some(context)