From 57c6dbd71e483646cd0409894547e97f664ebed3 Mon Sep 17 00:00:00 2001 From: Smit Barmase Date: Fri, 5 Sep 2025 09:10:50 +0530 Subject: [PATCH] linux: Fix IME positioning on scaled display on Wayland (#37600) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- crates/gpui/src/platform.rs | 8 +++---- .../gpui/src/platform/linux/wayland/client.rs | 6 ++--- .../gpui/src/platform/linux/wayland/window.rs | 7 +++--- crates/gpui/src/platform/linux/x11/client.rs | 22 +++++++++---------- crates/gpui/src/platform/linux/x11/window.rs | 7 +++--- crates/gpui/src/platform/mac/window.rs | 9 ++++---- crates/gpui/src/platform/test/window.rs | 6 ++--- crates/gpui/src/platform/windows/window.rs | 2 +- crates/gpui/src/window.rs | 4 +--- 9 files changed, 34 insertions(+), 37 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index d3425c8835..444b60ac15 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -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; - fn update_ime_position(&self, _bounds: Bounds); + fn update_ime_position(&self, _bounds: Bounds); #[cfg(any(test, feature = "test-support"))] fn as_test(&mut self) -> Option<&mut TestWindow> { diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 189cfa1954..8596bddc8d 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -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) { + pub fn update_ime_position(&self, bounds: Bounds) { 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() { diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index 7570c58c09..76dd89c940 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -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) { + fn update_ime_position(&self, bounds: Bounds) { let state = self.borrow(); state.client.update_ime_position(bounds); } diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 9a43bd6470..42c59701d3 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -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) { + pub fn update_ime_position(&self, bounds: Bounds) { 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, }, ); }) diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 6af943b317..79a4383725 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -1019,8 +1019,9 @@ impl X11WindowStatePtr { } } - pub fn get_ime_area(&self) -> Option> { + pub fn get_ime_area(&self) -> Option> { let mut state = self.state.borrow_mut(); + let scale_factor = state.scale_factor; let mut bounds: Option> = 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) -> anyhow::Result<()> { @@ -1618,7 +1619,7 @@ impl PlatformWindow for X11Window { } } - fn update_ime_position(&self, bounds: Bounds) { + fn update_ime_position(&self, bounds: Bounds) { let mut state = self.0.state.borrow_mut(); let client = state.client.clone(); drop(state); diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 686cfb314e..1230a70406 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -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) { + fn update_ime_position(&self, _bounds: Bounds) { let executor = self.0.lock().executor.clone(); executor .spawn(async move { diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index e15bd7aeec..9e87f4504d 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -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) {} + fn update_ime_position(&self, _bounds: Bounds) {} fn gpu_specs(&self) -> Option { None diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 9d001da822..aa907c8d73 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -839,7 +839,7 @@ impl PlatformWindow for WindowsWindow { self.0.state.borrow().renderer.gpu_specs().log_err() } - fn update_ime_position(&self, _bounds: Bounds) { + fn update_ime_position(&self, _bounds: Bounds) { // There is no such thing on Windows. } } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 0ec73c4b00..61d15cb3ed 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -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); }