gpui(linux): Fix RefCell borrow panic when callbacks register new callbacks (#49533)
## Summary
Fixes RefCell borrow panic on Linux (Wayland and X11) when callbacks try
to register new callbacks.
**Root cause:** Linux GPUI backends invoked callbacks while still
holding a `RefCell` borrow on the `Callbacks` struct. If a callback
tried to register a new
callback (e.g., `on_window_should_close`), it would panic with "already
borrowed: BorrowMutError".
**Bug pattern:**
```rust
// Callback runs while borrow is held - panics if callback borrows
callbacks
if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
fun(input);
}
Fix: Apply the take-call-restore pattern (already used in macOS
backend):
// Take callback out, release borrow, call, restore
let callback = self.callbacks.borrow_mut().input.take();
if let Some(mut fun) = callback {
let result = fun(input);
self.callbacks.borrow_mut().input = Some(fun);
}
Changes
- Wayland (window.rs): Fixed 6 callback invocations
- X11 (window.rs): Fixed 4 callback invocations
Test plan
- cargo check -p gpui compiles successfully
- Tested on Linux (Wayland) - no more RefCell panic
Release Notes:
- Fixed a crash on Linux when window callbacks attempted to register new
callbacks
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6256824c65
commit
8fcdbf41cd
3 files changed
+48
-30
No files matched your search
@@ -25,7 +25,6 @@ mod ui;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
// Another comment
|
||||
use agent_settings::{AgentProfileId, AgentSettings};
|
||||
use assistant_slash_command::SlashCommandRegistry;
|
||||
use client::Client;
|
||||
|
||||
@@ -640,19 +640,19 @@ impl WaylandWindowStatePtr {
|
||||
match mode {
|
||||
WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
|
||||
self.state.borrow_mut().decorations = WindowDecorations::Server;
|
||||
if let Some(appearance_changed) =
|
||||
self.callbacks.borrow_mut().appearance_changed.as_mut()
|
||||
{
|
||||
appearance_changed();
|
||||
let callback = self.callbacks.borrow_mut().appearance_changed.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun();
|
||||
self.callbacks.borrow_mut().appearance_changed = Some(fun);
|
||||
}
|
||||
}
|
||||
WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
|
||||
self.state.borrow_mut().decorations = WindowDecorations::Client;
|
||||
// Update background to be transparent
|
||||
if let Some(appearance_changed) =
|
||||
self.callbacks.borrow_mut().appearance_changed.as_mut()
|
||||
{
|
||||
appearance_changed();
|
||||
let callback = self.callbacks.borrow_mut().appearance_changed.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun();
|
||||
self.callbacks.borrow_mut().appearance_changed = Some(fun);
|
||||
}
|
||||
}
|
||||
WEnum::Value(_) => {
|
||||
@@ -924,8 +924,10 @@ impl WaylandWindowStatePtr {
|
||||
(state.bounds.size, state.scale)
|
||||
};
|
||||
|
||||
if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
|
||||
let callback = self.callbacks.borrow_mut().resize.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun(size, scale);
|
||||
self.callbacks.borrow_mut().resize = Some(fun);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -971,10 +973,13 @@ impl WaylandWindowStatePtr {
|
||||
if self.is_blocked() {
|
||||
return;
|
||||
}
|
||||
if let Some(ref mut fun) = self.callbacks.borrow_mut().input
|
||||
&& !fun(input.clone()).propagate
|
||||
{
|
||||
return;
|
||||
let callback = self.callbacks.borrow_mut().input.take();
|
||||
if let Some(mut fun) = callback {
|
||||
let result = fun(input.clone());
|
||||
self.callbacks.borrow_mut().input = Some(fun);
|
||||
if !result.propagate {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let PlatformInput::KeyDown(event) = input
|
||||
&& event.keystroke.modifiers.is_subset_of(&Modifiers::shift())
|
||||
@@ -991,23 +996,28 @@ impl WaylandWindowStatePtr {
|
||||
|
||||
pub fn set_focused(&self, focus: bool) {
|
||||
self.state.borrow_mut().active = focus;
|
||||
if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
|
||||
let callback = self.callbacks.borrow_mut().active_status_change.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun(focus);
|
||||
self.callbacks.borrow_mut().active_status_change = Some(fun);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_hovered(&self, focus: bool) {
|
||||
if let Some(ref mut fun) = self.callbacks.borrow_mut().hover_status_change {
|
||||
let callback = self.callbacks.borrow_mut().hover_status_change.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun(focus);
|
||||
self.callbacks.borrow_mut().hover_status_change = Some(fun);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_appearance(&mut self, appearance: WindowAppearance) {
|
||||
self.state.borrow_mut().appearance = appearance;
|
||||
|
||||
let mut callbacks = self.callbacks.borrow_mut();
|
||||
if let Some(ref mut fun) = callbacks.appearance_changed {
|
||||
(fun)()
|
||||
let callback = self.callbacks.borrow_mut().appearance_changed.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun();
|
||||
self.callbacks.borrow_mut().appearance_changed = Some(fun);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1045,9 +1045,10 @@ impl X11WindowStatePtr {
|
||||
}
|
||||
|
||||
pub fn refresh(&self, request_frame_options: RequestFrameOptions) {
|
||||
let mut cb = self.callbacks.borrow_mut();
|
||||
if let Some(ref mut fun) = cb.request_frame {
|
||||
let callback = self.callbacks.borrow_mut().request_frame.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun(request_frame_options);
|
||||
self.callbacks.borrow_mut().request_frame = Some(fun);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1055,10 +1056,13 @@ impl X11WindowStatePtr {
|
||||
if self.is_blocked() {
|
||||
return;
|
||||
}
|
||||
if let Some(ref mut fun) = self.callbacks.borrow_mut().input
|
||||
&& !fun(input.clone()).propagate
|
||||
{
|
||||
return;
|
||||
let callback = self.callbacks.borrow_mut().input.take();
|
||||
if let Some(mut fun) = callback {
|
||||
let result = fun(input.clone());
|
||||
self.callbacks.borrow_mut().input = Some(fun);
|
||||
if !result.propagate {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let PlatformInput::KeyDown(event) = input {
|
||||
// only allow shift modifier when inserting text
|
||||
@@ -1191,14 +1195,18 @@ impl X11WindowStatePtr {
|
||||
}
|
||||
|
||||
pub fn set_active(&self, focus: bool) {
|
||||
if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
|
||||
let callback = self.callbacks.borrow_mut().active_status_change.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun(focus);
|
||||
self.callbacks.borrow_mut().active_status_change = Some(fun);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_hovered(&self, focus: bool) {
|
||||
if let Some(ref mut fun) = self.callbacks.borrow_mut().hovered_status_change {
|
||||
let callback = self.callbacks.borrow_mut().hovered_status_change.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun(focus);
|
||||
self.callbacks.borrow_mut().hovered_status_change = Some(fun);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1209,9 +1217,10 @@ impl X11WindowStatePtr {
|
||||
state.renderer.update_transparency(is_transparent);
|
||||
state.appearance = appearance;
|
||||
drop(state);
|
||||
let mut callbacks = self.callbacks.borrow_mut();
|
||||
if let Some(ref mut fun) = callbacks.appearance_changed {
|
||||
(fun)()
|
||||
let callback = self.callbacks.borrow_mut().appearance_changed.take();
|
||||
if let Some(mut fun) = callback {
|
||||
fun();
|
||||
self.callbacks.borrow_mut().appearance_changed = Some(fun);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user