x11: fix crash with uim

Let's just not forward events to the IME once the user requested that
it should be disabled, though, still try to change its state explicitly.

Fixes #4082.
This commit is contained in:
Kirill Chibisov 2025-02-03 20:43:43 +03:00 committed by GitHub
parent 5c48ec7977
commit a6998af997
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 22 additions and 37 deletions

View file

@ -227,3 +227,4 @@ changelog entry.
- On macOS, fixed redundant `SurfaceResized` event at window creation. - On macOS, fixed redundant `SurfaceResized` event at window creation.
- On Windows, fixed the event loop not waking on accessibility requests. - On Windows, fixed the event loop not waking on accessibility requests.
- On X11, fixed cursor grab mode state tracking on error. - On X11, fixed cursor grab mode state tracking on error.
- On X11, fixed crash with uim

View file

@ -122,19 +122,15 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
let is_allowed = let is_allowed =
old_context.as_ref().map(|old_context| old_context.is_allowed()).unwrap_or_default(); 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 new_context = {
let result = unsafe { let result = unsafe {
ImeContext::new( ImeContext::new(
xconn, xconn,
new_im.im, &new_im,
style,
*window, *window,
spot, spot,
(*inner).event_sender.clone(), (*inner).event_sender.clone(),
is_allowed,
) )
}; };
if result.is_err() { if result.is_err() {

View file

@ -7,7 +7,7 @@ use std::{fmt, mem, ptr};
use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct}; use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct};
use super::{ffi, util, XConnection, XError}; use super::{ffi, util, XConnection, XError};
use crate::platform_impl::platform::x11::ime::input_method::{Style, XIMStyle}; use crate::platform_impl::platform::x11::ime::input_method::{InputMethod, Style, XIMStyle};
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender}; use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender};
/// IME creation error. /// IME creation error.
@ -197,7 +197,7 @@ struct ImeContextClientData {
pub struct ImeContext { pub struct ImeContext {
pub(crate) ic: ffi::XIC, pub(crate) ic: ffi::XIC,
pub(crate) ic_spot: ffi::XPoint, 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 // 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. // from there we keep the pointer to automatically deallocate it.
_client_data: Box<ImeContextClientData>, _client_data: Box<ImeContextClientData>,
@ -206,11 +206,11 @@ pub struct ImeContext {
impl ImeContext { impl ImeContext {
pub(crate) unsafe fn new( pub(crate) unsafe fn new(
xconn: &Arc<XConnection>, xconn: &Arc<XConnection>,
im: ffi::XIM, im: &InputMethod,
style: Style,
window: ffi::Window, window: ffi::Window,
ic_spot: Option<ffi::XPoint>, ic_spot: Option<ffi::XPoint>,
event_sender: ImeEventSender, event_sender: ImeEventSender,
allowed: bool,
) -> Result<Self, ImeContextCreationError> { ) -> Result<Self, ImeContextCreationError> {
let client_data = Box::into_raw(Box::new(ImeContextClientData { let client_data = Box::into_raw(Box::new(ImeContextClientData {
window, window,
@ -219,20 +219,24 @@ impl ImeContext {
cursor_pos: 0, cursor_pos: 0,
})); }));
let style = if allowed { im.preedit_style } else { im.none_style };
let ic = match style as _ { let ic = match style as _ {
Style::Preedit(style) => unsafe { Style::Preedit(style) => unsafe {
ImeContext::create_preedit_ic( ImeContext::create_preedit_ic(
xconn, xconn,
im, im.im,
style, style,
window, window,
client_data as ffi::XPointer, client_data as ffi::XPointer,
) )
}, },
Style::Nothing(style) => unsafe { 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)?; .ok_or(ImeContextCreationError::Null)?;
@ -241,7 +245,7 @@ impl ImeContext {
let mut context = ImeContext { let mut context = ImeContext {
ic, ic,
ic_spot: ffi::XPoint { x: 0, y: 0 }, ic_spot: ffi::XPoint { x: 0, y: 0 },
style, allowed,
_client_data: unsafe { Box::from_raw(client_data) }, _client_data: unsafe { Box::from_raw(client_data) },
}; };
@ -348,7 +352,7 @@ impl ImeContext {
} }
pub fn is_allowed(&self) -> bool { 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 // Set the spot for preedit text. Setting spot isn't working with libX11 when preedit callbacks

View file

@ -82,9 +82,7 @@ impl InputMethod {
} }
let preedit_style = preedit_style.unwrap_or_else(|| none_style.unwrap()); 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 let none_style = none_style.unwrap_or(preedit_style);
// regardless...
let none_style = none_style.unwrap_or(Style::None(XIM_NONE_STYLE));
Some(InputMethod { im, _name: name, preedit_style, none_style }) Some(InputMethod { im, _name: name, preedit_style, none_style })
} }

View file

@ -10,13 +10,12 @@ use std::sync::Arc;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::debug;
use self::callbacks::*; use self::callbacks::*;
use self::context::ImeContext; use self::context::ImeContext;
pub use self::context::ImeContextCreationError; pub use self::context::ImeContextCreationError;
use self::inner::{close_im, ImeInner}; use self::inner::{close_im, ImeInner};
use self::input_method::{PotentialInputMethods, Style}; use self::input_method::PotentialInputMethods;
use super::{ffi, util, XConnection, XError}; use super::{ffi, util, XConnection, XError};
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -113,39 +112,26 @@ impl Ime {
pub fn create_context( pub fn create_context(
&mut self, &mut self,
window: ffi::Window, window: ffi::Window,
with_preedit: bool, with_ime: bool,
) -> Result<bool, ImeContextCreationError> { ) -> Result<bool, ImeContextCreationError> {
let context = if self.is_destroyed() { let context = if self.is_destroyed() {
// Create empty entry in map, so that when IME is rebuilt, this window has a context. // Create empty entry in map, so that when IME is rebuilt, this window has a context.
None None
} else { } else {
let im = self.inner.im.as_ref().unwrap(); let im = self.inner.im.as_ref().unwrap();
let style = if with_preedit { im.preedit_style } else { im.none_style };
let context = unsafe { let context = unsafe {
ImeContext::new( ImeContext::new(
&self.inner.xconn, &self.inner.xconn,
im.im, im,
style,
window, window,
None, None,
self.inner.event_sender.clone(), 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 context.is_allowed() { ImeEvent::Enabled } else { ImeEvent::Disabled };
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
};
self.inner.event_sender.send((window, event)).expect("Failed to send enabled event"); self.inner.event_sender.send((window, event)).expect("Failed to send enabled event");
Some(context) Some(context)