rust-windowing / rust-windowing/winit
macOS: IME insertText panics during window teardown with active marked text
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 6.2k
- Forks
- 1.3k
- Avg merge
- 2d 19h
- Merged PRs (30d)
- 9
Description
Description
With an active marked-text composition, dropping a macOS winit 0.30.13 window can synchronously call NSTextInputClient::insertText while the WinitView no longer has a WinitWindow. insert_text calls queue_event, whose weak window lookup panics with view to have a window. Because the Rust method is an Objective-C callback, the panic cannot unwind and the process aborts.
This is the insertText variant of the lifecycle family reported in #4333. The guard added by #4334 covers firstRectForCharacterRange, not this path.
Calling Window::set_ime_allowed(false) while the window is still alive, immediately before requesting event-loop exit, prevents the abort in 20/20 runs because winit clears its marked-text state. That is a usable application workaround, but a late insertText callback should not be able to abort inside winit after the view detaches.
Minimal reproduction
use std::time::{Duration, Instant};
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::{Window, WindowId};
struct App {
started: Instant,
window: Option<Window>,
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window = event_loop
.create_window(Window::default_attributes().with_title("winit IME teardown repro"))
.unwrap();
window.set_ime_allowed(true);
window.request_redraw();
self.window = Some(window);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::RedrawRequested => self.window.as_ref().unwrap().focus_window(),
WindowEvent::CloseRequested => event_loop.exit(),
_ => {}
}
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if self.started.elapsed() >= Duration::from_millis(1800) {
// Deliberately exit with IME enabled and marked text active.
event_loop.exit();
} else {
event_loop.set_control_flow(ControlFlow::WaitUntil(
self.started + Duration::from_millis(1800),
));
}
}
}
fn main() {
let event_loop = EventLoop::new().unwrap();
event_loop.set_control_flow(ControlFlow::Wait);
let mut app = App { started: Instant::now(), window: None };
event_loop.run_app(&mut app).unwrap();
}
Use winit = "=0.30.13", run the program, focus its window, switch to a Pinyin IME, and type zhongwen without committing before the 1.8-second exit. The process aborts deterministically on the tested input source.
Panic
thread 'main' panicked at .../winit-0.30.13/src/platform_impl/macos/view.rs:835:40:
view to have a window
...
WinitView::queue_event
WinitView::insert_text
...
WindowDelegate::dealloc
drop_glue<winit::window::Window>
thread caused non-unwinding panic. aborting.
Environment
- macOS 15.7.5 (24G612), arm64
- winit 0.30.13
- Tencent Wetype Pinyin (
com.tencent.inputmethod.wetype.pinyin) - Rust 1.97.0
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/platform_impl/macos/view.rs around insert_text and queue_event, then run the minimal reproduction with winit 0.30.13 and an active Pinyin composition. Trace the teardown path where WinitView no longer has a WinitWindow; done means the late insertText callback no longer panics or aborts the process.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, rust
- Domain
- desktop, operating-systems
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100