linux: Fix IME positioning on scaled display on Wayland (#37600)

Removes IME bounds scaling on Wayland since it uses logical pixels,
unlike X11. We now scale only on X11. Windows and macOS don’t use these
bounds for IME anyway.

Release Notes:

- Fixed an issue where the IME popover could appear outside the window
or fail to show on Wayland.
This commit is contained in:
Smit Barmase
2025-09-05 09:10:50 +05:30
committed by GitHub
parent fded3fbcdb
commit 57c6dbd71e
9 changed files with 34 additions and 37 deletions
+4 -4
View File
@@ -39,9 +39,9 @@ use crate::{
Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
ForegroundExecutor, GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels, PlatformInput,
Point, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
ShapedGlyph, ShapedRun, SharedString, Size, SvgRenderer, SvgSize, SystemWindowTab, Task,
TaskLabel, Window, WindowControlArea, hash, point, px, size,
Point, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Scene, ShapedGlyph,
ShapedRun, SharedString, Size, SvgRenderer, SvgSize, SystemWindowTab, Task, TaskLabel, Window,
WindowControlArea, hash, point, px, size,
};
use anyhow::Result;
use async_task::Runnable;
@@ -548,7 +548,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
fn set_client_inset(&self, _inset: Pixels) {}
fn gpu_specs(&self) -> Option<GpuSpecs>;
fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>);
fn update_ime_position(&self, _bounds: Bounds<Pixels>);
#[cfg(any(test, feature = "test-support"))]
fn as_test(&mut self) -> Option<&mut TestWindow> {
@@ -75,8 +75,8 @@ use crate::{
FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon,
LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay,
PlatformInput, PlatformKeyboardLayout, Point, SCROLL_LINES, ScaledPixels, ScrollDelta,
ScrollWheelEvent, Size, TouchPhase, WindowParams, point, px, size,
PlatformInput, PlatformKeyboardLayout, Point, SCROLL_LINES, ScrollDelta, ScrollWheelEvent,
Size, TouchPhase, WindowParams, point, px, size,
};
use crate::{
SharedString,
@@ -323,7 +323,7 @@ impl WaylandClientStatePtr {
}
}
pub fn update_ime_position(&self, bounds: Bounds<ScaledPixels>) {
pub fn update_ime_position(&self, bounds: Bounds<Pixels>) {
let client = self.get_client();
let mut state = client.borrow_mut();
if state.composing || state.text_input.is_none() || state.pre_edit_text.is_some() {
@@ -25,9 +25,8 @@ use crate::scene::Scene;
use crate::{
AnyWindowHandle, Bounds, Decorations, Globals, GpuSpecs, Modifiers, Output, Pixels,
PlatformDisplay, PlatformInput, Point, PromptButton, PromptLevel, RequestFrameOptions,
ResizeEdge, ScaledPixels, Size, Tiling, WaylandClientStatePtr, WindowAppearance,
WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations,
WindowParams, px, size,
ResizeEdge, Size, Tiling, WaylandClientStatePtr, WindowAppearance, WindowBackgroundAppearance,
WindowBounds, WindowControlArea, WindowControls, WindowDecorations, WindowParams, px, size,
};
use crate::{
Capslock,
@@ -1078,7 +1077,7 @@ impl PlatformWindow for WaylandWindow {
}
}
fn update_ime_position(&self, bounds: Bounds<ScaledPixels>) {
fn update_ime_position(&self, bounds: Bounds<Pixels>) {
let state = self.borrow();
state.client.update_ime_position(bounds);
}
+11 -11
View File
@@ -62,8 +62,7 @@ use crate::{
AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, DisplayId, FileDropEvent, Keystroke,
LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, Pixels, Platform,
PlatformDisplay, PlatformInput, PlatformKeyboardLayout, Point, RequestFrameOptions,
ScaledPixels, ScrollDelta, Size, TouchPhase, WindowParams, X11Window,
modifiers_from_xinput_info, point, px,
ScrollDelta, Size, TouchPhase, WindowParams, X11Window, modifiers_from_xinput_info, point, px,
};
/// Value for DeviceId parameters which selects all devices.
@@ -252,7 +251,7 @@ impl X11ClientStatePtr {
}
}
pub fn update_ime_position(&self, bounds: Bounds<ScaledPixels>) {
pub fn update_ime_position(&self, bounds: Bounds<Pixels>) {
let Some(client) = self.get_client() else {
return;
};
@@ -270,6 +269,7 @@ impl X11ClientStatePtr {
state.ximc = Some(ximc);
return;
};
let scaled_bounds = bounds.scale(state.scale_factor);
let ic_attributes = ximc
.build_ic_attributes()
.push(
@@ -282,8 +282,8 @@ impl X11ClientStatePtr {
b.push(
xim::AttributeName::SpotLocation,
xim::Point {
x: u32::from(bounds.origin.x + bounds.size.width) as i16,
y: u32::from(bounds.origin.y + bounds.size.height) as i16,
x: u32::from(scaled_bounds.origin.x + scaled_bounds.size.width) as i16,
y: u32::from(scaled_bounds.origin.y + scaled_bounds.size.height) as i16,
},
);
})
@@ -703,14 +703,14 @@ impl X11Client {
state.xim_handler = Some(xim_handler);
return;
};
if let Some(area) = window.get_ime_area() {
if let Some(scaled_area) = window.get_ime_area() {
ic_attributes =
ic_attributes.nested_list(xim::AttributeName::PreeditAttributes, |b| {
b.push(
xim::AttributeName::SpotLocation,
xim::Point {
x: u32::from(area.origin.x + area.size.width) as i16,
y: u32::from(area.origin.y + area.size.height) as i16,
x: u32::from(scaled_area.origin.x + scaled_area.size.width) as i16,
y: u32::from(scaled_area.origin.y + scaled_area.size.height) as i16,
},
);
});
@@ -1351,7 +1351,7 @@ impl X11Client {
drop(state);
window.handle_ime_preedit(text);
if let Some(area) = window.get_ime_area() {
if let Some(scaled_area) = window.get_ime_area() {
let ic_attributes = ximc
.build_ic_attributes()
.push(
@@ -1364,8 +1364,8 @@ impl X11Client {
b.push(
xim::AttributeName::SpotLocation,
xim::Point {
x: u32::from(area.origin.x + area.size.width) as i16,
y: u32::from(area.origin.y + area.size.height) as i16,
x: u32::from(scaled_area.origin.x + scaled_area.size.width) as i16,
y: u32::from(scaled_area.origin.y + scaled_area.size.height) as i16,
},
);
})
+4 -3
View File
@@ -1019,8 +1019,9 @@ impl X11WindowStatePtr {
}
}
pub fn get_ime_area(&self) -> Option<Bounds<Pixels>> {
pub fn get_ime_area(&self) -> Option<Bounds<ScaledPixels>> {
let mut state = self.state.borrow_mut();
let scale_factor = state.scale_factor;
let mut bounds: Option<Bounds<Pixels>> = None;
if let Some(mut input_handler) = state.input_handler.take() {
drop(state);
@@ -1030,7 +1031,7 @@ impl X11WindowStatePtr {
let mut state = self.state.borrow_mut();
state.input_handler = Some(input_handler);
};
bounds
bounds.map(|b| b.scale(scale_factor))
}
pub fn set_bounds(&self, bounds: Bounds<i32>) -> anyhow::Result<()> {
@@ -1618,7 +1619,7 @@ impl PlatformWindow for X11Window {
}
}
fn update_ime_position(&self, bounds: Bounds<ScaledPixels>) {
fn update_ime_position(&self, bounds: Bounds<Pixels>) {
let mut state = self.0.state.borrow_mut();
let client = state.client.clone();
drop(state);
+4 -5
View File
@@ -4,10 +4,9 @@ use crate::{
ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions,
ScaledPixels, SharedString, Size, SystemWindowTab, Timer, WindowAppearance,
WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowKind, WindowParams,
dispatch_get_main_queue, dispatch_sys::dispatch_async_f, platform::PlatformInputHandler, point,
px, size,
SharedString, Size, SystemWindowTab, Timer, WindowAppearance, WindowBackgroundAppearance,
WindowBounds, WindowControlArea, WindowKind, WindowParams, dispatch_get_main_queue,
dispatch_sys::dispatch_async_f, platform::PlatformInputHandler, point, px, size,
};
use block::ConcreteBlock;
use cocoa::{
@@ -1480,7 +1479,7 @@ impl PlatformWindow for MacWindow {
None
}
fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {
fn update_ime_position(&self, _bounds: Bounds<Pixels>) {
let executor = self.0.lock().executor.clone();
executor
.spawn(async move {
+3 -3
View File
@@ -1,8 +1,8 @@
use crate::{
AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DispatchEventResult, GpuSpecs,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow,
Point, PromptButton, RequestFrameOptions, ScaledPixels, Size, TestPlatform, TileId,
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowParams,
Point, PromptButton, RequestFrameOptions, Size, TestPlatform, TileId, WindowAppearance,
WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowParams,
};
use collections::HashMap;
use parking_lot::Mutex;
@@ -289,7 +289,7 @@ impl PlatformWindow for TestWindow {
unimplemented!()
}
fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {}
fn update_ime_position(&self, _bounds: Bounds<Pixels>) {}
fn gpu_specs(&self) -> Option<GpuSpecs> {
None
+1 -1
View File
@@ -839,7 +839,7 @@ impl PlatformWindow for WindowsWindow {
self.0.state.borrow().renderer.gpu_specs().log_err()
}
fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {
fn update_ime_position(&self, _bounds: Bounds<Pixels>) {
// There is no such thing on Windows.
}
}
+1 -3
View File
@@ -4096,9 +4096,7 @@ impl Window {
self.on_next_frame(|window, cx| {
if let Some(mut input_handler) = window.platform_window.take_input_handler() {
if let Some(bounds) = input_handler.selected_bounds(window, cx) {
window
.platform_window
.update_ime_position(bounds.scale(window.scale_factor()));
window.platform_window.update_ime_position(bounds);
}
window.platform_window.set_input_handler(input_handler);
}