diff --git a/crates/gpui/src/platform/linux/headless/client.rs b/crates/gpui/src/platform/linux/headless/client.rs index bb066684f8..35d6af821b 100644 --- a/crates/gpui/src/platform/linux/headless/client.rs +++ b/crates/gpui/src/platform/linux/headless/client.rs @@ -22,7 +22,7 @@ impl HeadlessClient { pub(crate) fn new() -> Self { let event_loop = EventLoop::try_new().unwrap(); - let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal()); + let (common, main_receiver) = LinuxCommon::new(Box::new(event_loop.get_signal())); let handle = event_loop.handle(); diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 3c3be3f6cd..040c58cdfb 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -84,6 +84,16 @@ pub(crate) struct PlatformHandlers { pub(crate) validate_app_menu_command: Option bool>>, } +pub trait QuitSignal { + fn quit(&mut self); +} + +impl QuitSignal for LoopSignal { + fn quit(&mut self) { + self.stop(); + } +} + pub(crate) struct LinuxCommon { pub(crate) background_executor: BackgroundExecutor, pub(crate) foreground_executor: ForegroundExecutor, @@ -91,12 +101,12 @@ pub(crate) struct LinuxCommon { pub(crate) appearance: WindowAppearance, pub(crate) auto_hide_scrollbars: bool, pub(crate) callbacks: PlatformHandlers, - pub(crate) signal: LoopSignal, + pub(crate) signal: Box, pub(crate) menus: Vec, } impl LinuxCommon { - pub fn new(signal: LoopSignal) -> (Self, Channel) { + pub fn new(signal: Box) -> (Self, Channel) { let (main_sender, main_receiver) = calloop::channel::channel::(); let text_system = Arc::new(CosmicTextSystem::new()); let callbacks = PlatformHandlers::default(); @@ -146,7 +156,7 @@ impl Platform for P { } fn quit(&self) { - self.with_common(|common| common.signal.stop()); + self.with_common(|common| common.signal.quit()); } fn compositor_name(&self) -> &'static str { diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 250df50875..1ca35ddbd1 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -310,7 +310,7 @@ impl WaylandClientStatePtr { } } if state.windows.is_empty() { - state.common.signal.stop(); + state.common.signal.quit(); } } } @@ -406,7 +406,7 @@ impl WaylandClient { let event_loop = EventLoop::::try_new().unwrap(); - let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal()); + let (common, main_receiver) = LinuxCommon::new(Box::new(event_loop.get_signal())); let handle = event_loop.handle(); handle diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index efb3698f05..ec2d53641f 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -8,10 +8,10 @@ use anyhow::Context; use async_task::Runnable; use calloop::channel::Channel; -use calloop::EventLoop; use collections::HashMap; +use futures::channel::oneshot; use util::ResultExt; use x11rb::connection::{Connection, RequestConnection}; use x11rb::cursor; @@ -32,7 +32,7 @@ use crate::platform::{LinuxCommon, PlatformWindow}; use crate::{ modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, DisplayId, Keystroke, Modifiers, ModifiersChangedEvent, Pixels, PlatformDisplay, PlatformInput, - Point, ScrollDelta, Size, TouchPhase, WindowParams, X11Window, + Point, QuitSignal, ScrollDelta, Size, TouchPhase, WindowParams, X11Window, }; use super::{ @@ -128,6 +128,8 @@ pub struct X11ClientState { pub(crate) clipboard: x11_clipboard::Clipboard, pub(crate) clipboard_item: Option, + quit_signal_rx: oneshot::Receiver<()>, + runnables: Channel, xdp_event_source: XDPEventSource, } @@ -150,7 +152,29 @@ impl X11ClientStatePtr { state.cursor_styles.remove(&x_window); if state.windows.is_empty() { - state.common.signal.stop(); + state.common.signal.quit(); + } + } +} + +struct ChannelQuitSignal { + tx: Option>, +} + +impl ChannelQuitSignal { + fn new() -> (Self, oneshot::Receiver<()>) { + let (tx, rx) = oneshot::channel::<()>(); + + let quit_signal = ChannelQuitSignal { tx: Some(tx) }; + + (quit_signal, rx) + } +} + +impl QuitSignal for ChannelQuitSignal { + fn quit(&mut self) { + if let Some(tx) = self.tx.take() { + tx.send(()).log_err(); } } } @@ -160,11 +184,9 @@ pub(crate) struct X11Client(Rc>); impl X11Client { pub(crate) fn new() -> Self { - // TODO: We don't need this anymore. - let event_loop: calloop::EventLoop = EventLoop::try_new().unwrap(); + let (quit_signal, quit_signal_rx) = ChannelQuitSignal::new(); - // TODO: We need to send a quit signal in there. - let (common, runnables) = LinuxCommon::new(event_loop.get_signal()); + let (common, runnables) = LinuxCommon::new(Box::new(quit_signal)); let (xcb_connection, x_root_index) = XCBConnection::connect(None).unwrap(); xcb_connection @@ -268,6 +290,7 @@ impl X11Client { X11Client(Rc::new(RefCell::new(X11ClientState { runnables, xdp_event_source, + quit_signal_rx, common, @@ -1072,7 +1095,12 @@ impl LinuxClient for X11Client { fn run(&self) { loop { - let mut sleep = true; + { + let mut state = self.0.borrow_mut(); + if let Ok(Some(())) = state.quit_signal_rx.try_recv() { + return; + } + } // Send expose events to windows that need refreshing let mut windows_to_expose = HashSet::new(); @@ -1085,7 +1113,7 @@ impl LinuxClient for X11Client { } } } - sleep = windows_to_expose.is_empty(); + let mut sleep = windows_to_expose.is_empty(); let _ = self.send_window_expose_events(windows_to_expose).log_err(); // Read all X11 events and then handle them in a batch