diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 436cf6cf8..890b2166a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -85,6 +85,19 @@ fn linux_webkit_rendering_workarounds() -> &'static [(&'static str, &'static str ] } +#[cfg_attr(not(target_os = "linux"), allow(dead_code))] +fn linux_system_gtk3_immodules_cache_path() -> Option<&'static str> { + [ + "/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules.cache", + "/usr/lib/aarch64-linux-gnu/gtk-3.0/3.0.0/immodules.cache", + "/usr/lib64/gtk-3.0/3.0.0/immodules.cache", + "/usr/lib/gtk-3.0/3.0.0/immodules.cache", + ] + .iter() + .copied() + .find(|path| std::path::Path::new(path).is_file()) +} + #[cfg_attr(not(target_os = "linux"), allow(dead_code))] fn linux_appimage_wayland_backend_override( appimage: Option<&std::ffi::OsStr>, @@ -101,6 +114,33 @@ fn linux_appimage_wayland_backend_override( } } +#[cfg_attr(not(target_os = "linux"), allow(dead_code))] +fn linux_appimage_system_gtk_immodules_cache( + appimage: Option<&std::ffi::OsStr>, + appdir: Option<&std::ffi::OsStr>, + gtk_im_module: Option<&std::ffi::OsStr>, + gtk_im_module_file: Option<&std::ffi::OsStr>, + system_cache_path: Option<&'static str>, +) -> Option<&'static str> { + let system_cache_path = system_cache_path?; + if appimage.is_none() || gtk_im_module.is_none() { + return None; + } + + let Some(gtk_im_module_file) = gtk_im_module_file else { + return Some(system_cache_path); + }; + let Some(appdir) = appdir else { + return None; + }; + + if std::path::Path::new(gtk_im_module_file).starts_with(std::path::Path::new(appdir)) { + Some(system_cache_path) + } else { + None + } +} + #[cfg(target_os = "linux")] fn apply_linux_webkit_rendering_workarounds() { for (key, value) in linux_webkit_rendering_workarounds() { @@ -115,6 +155,18 @@ fn apply_linux_webkit_rendering_workarounds() { ) { std::env::set_var("GDK_BACKEND", gdk_backend); } + if let Some(gtk_im_module_file) = linux_appimage_system_gtk_immodules_cache( + std::env::var_os("APPIMAGE").as_deref(), + std::env::var_os("APPDIR").as_deref(), + std::env::var_os("GTK_IM_MODULE").as_deref(), + std::env::var_os("GTK_IM_MODULE_FILE").as_deref(), + linux_system_gtk3_immodules_cache_path(), + ) { + // linuxdeploy-plugin-gtk points GTK_IM_MODULE_FILE at the bundled + // cache. That hides host IM modules such as fcitx5/ibus, so prefer the + // host GTK cache when the user has configured a GTK input method. + std::env::set_var("GTK_IM_MODULE_FILE", gtk_im_module_file); + } } fn show_main_window(app: &tauri::AppHandle) { @@ -235,12 +287,14 @@ pub(crate) fn apply_desktop_settings(app: &tauri::AppHandle, desktop_settings: & #[allow(clippy::items_after_test_module)] mod tests { use super::{ - linux_appimage_wayland_backend_override, linux_webkit_rendering_workarounds, - native_window_decorations_override, should_hide_window_on_close, should_setup_desktop_tray, - should_show_main_window_after_setup, + linux_appimage_system_gtk_immodules_cache, linux_appimage_wayland_backend_override, + linux_webkit_rendering_workarounds, native_window_decorations_override, should_hide_window_on_close, + should_setup_desktop_tray, should_show_main_window_after_setup, }; use std::ffi::OsStr; + const TEST_GTK3_IMMODULES_CACHE: &str = "/usr/lib/test/gtk-3.0/3.0.0/immodules.cache"; + #[test] fn hides_window_on_close_for_windows_and_macos() { assert!(should_hide_window_on_close("windows")); @@ -302,6 +356,78 @@ mod tests { assert_eq!(linux_appimage_wayland_backend_override(Some(OsStr::new("/tmp/DBX.AppImage")), None, None), None); assert_eq!(linux_appimage_wayland_backend_override(None, Some(OsStr::new("wayland-0")), None), None); } + + #[test] + fn prefers_system_gtk_immodules_cache_for_appimage_input_methods() { + assert_eq!( + linux_appimage_system_gtk_immodules_cache( + Some(OsStr::new("/tmp/DBX.AppImage")), + Some(OsStr::new("/tmp/.mount_DBX123")), + Some(OsStr::new("fcitx5")), + Some(OsStr::new("/tmp/.mount_DBX123/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules.cache")), + Some(TEST_GTK3_IMMODULES_CACHE), + ), + Some(TEST_GTK3_IMMODULES_CACHE) + ); + assert_eq!( + linux_appimage_system_gtk_immodules_cache( + Some(OsStr::new("/tmp/DBX.AppImage")), + Some(OsStr::new("/tmp/.mount_DBX123")), + Some(OsStr::new("ibus")), + None, + Some(TEST_GTK3_IMMODULES_CACHE), + ), + Some(TEST_GTK3_IMMODULES_CACHE) + ); + } + + #[test] + fn preserves_external_gtk_immodules_cache_overrides() { + assert_eq!( + linux_appimage_system_gtk_immodules_cache( + Some(OsStr::new("/tmp/DBX.AppImage")), + Some(OsStr::new("/tmp/.mount_DBX123")), + Some(OsStr::new("fcitx5")), + Some(OsStr::new("/opt/custom/immodules.cache")), + Some(TEST_GTK3_IMMODULES_CACHE), + ), + None + ); + } + + #[test] + fn skips_system_gtk_immodules_cache_without_required_context() { + assert_eq!( + linux_appimage_system_gtk_immodules_cache( + None, + Some(OsStr::new("/tmp/.mount_DBX123")), + Some(OsStr::new("fcitx5")), + Some(OsStr::new("/tmp/.mount_DBX123/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules.cache")), + Some(TEST_GTK3_IMMODULES_CACHE), + ), + None + ); + assert_eq!( + linux_appimage_system_gtk_immodules_cache( + Some(OsStr::new("/tmp/DBX.AppImage")), + Some(OsStr::new("/tmp/.mount_DBX123")), + None, + Some(OsStr::new("/tmp/.mount_DBX123/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules.cache")), + Some(TEST_GTK3_IMMODULES_CACHE), + ), + None + ); + assert_eq!( + linux_appimage_system_gtk_immodules_cache( + Some(OsStr::new("/tmp/DBX.AppImage")), + Some(OsStr::new("/tmp/.mount_DBX123")), + Some(OsStr::new("fcitx5")), + Some(OsStr::new("/tmp/.mount_DBX123/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules.cache")), + None, + ), + None + ); + } } #[cfg_attr(mobile, tauri::mobile_entry_point)]