1use std::cell::RefCell;
2use std::rc::Rc;
3
4use calloop::{EventLoop, LoopHandle};
5use util::ResultExt;
6
7use crate::linux::{LinuxClient, LinuxCommon, LinuxKeyboardLayout};
8use gpui::{
9 AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, PlatformKeyboardLayout,
10 PlatformWindow, WindowParams,
11};
12
13pub struct HeadlessClientState {
14 pub(crate) _loop_handle: LoopHandle<'static, HeadlessClient>,
15 pub(crate) event_loop: Option<calloop::EventLoop<'static, HeadlessClient>>,
16 pub(crate) common: LinuxCommon,
17}
18
19#[derive(Clone)]
20pub(crate) struct HeadlessClient(Rc<RefCell<HeadlessClientState>>);
21
22impl HeadlessClient {
23 pub(crate) fn new() -> Self {
24 let event_loop = EventLoop::try_new().unwrap();
25
26 let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
27
28 let handle = event_loop.handle();
29
30 handle
31 .insert_source(main_receiver, |event, _, _: &mut HeadlessClient| {
32 if let calloop::channel::Event::Msg(runnable) = event {
33 runnable.run();
34 }
35 })
36 .ok();
37
38 HeadlessClient(Rc::new(RefCell::new(HeadlessClientState {
39 event_loop: Some(event_loop),
40 _loop_handle: handle,
41 common,
42 })))
43 }
44}
45
46impl LinuxClient for HeadlessClient {
47 fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
48 f(&mut self.0.borrow_mut().common)
49 }
50
51 fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
52 Box::new(LinuxKeyboardLayout::new("unknown".into()))
53 }
54
55 fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
56 vec![]
57 }
58
59 fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
60 None
61 }
62
63 fn display(&self, _id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
64 None
65 }
66
67 #[cfg(feature = "screen-capture")]
68 fn screen_capture_sources(
69 &self,
70 ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn gpui::ScreenCaptureSource>>>>
71 {
72 let (tx, rx) = futures::channel::oneshot::channel();
73 tx.send(Err(anyhow::anyhow!(
74 "Headless mode does not support screen capture."
75 )))
76 .ok();
77 rx
78 }
79
80 fn active_window(&self) -> Option<AnyWindowHandle> {
81 None
82 }
83
84 fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
85 None
86 }
87
88 fn open_window(
89 &self,
90 _handle: AnyWindowHandle,
91 _params: WindowParams,
92 ) -> anyhow::Result<Box<dyn PlatformWindow>> {
93 anyhow::bail!("neither DISPLAY nor WAYLAND_DISPLAY is set. You can run in headless mode");
94 }
95
96 fn compositor_name(&self) -> &'static str {
97 "headless"
98 }
99
100 fn set_cursor_style(&self, _style: CursorStyle) {}
101
102 fn open_uri(&self, _uri: &str) {}
103
104 fn reveal_path(&self, _path: std::path::PathBuf) {}
105
106 fn write_to_primary(&self, _item: gpui::ClipboardItem) {}
107
108 fn write_to_clipboard(&self, _item: gpui::ClipboardItem) {}
109
110 fn read_from_primary(&self) -> Option<gpui::ClipboardItem> {
111 None
112 }
113
114 fn read_from_clipboard(&self) -> Option<gpui::ClipboardItem> {
115 None
116 }
117
118 fn run(&self) {
119 let mut event_loop = self
120 .0
121 .borrow_mut()
122 .event_loop
123 .take()
124 .expect("App is already running");
125
126 event_loop.run(None, &mut self.clone(), |_| {}).log_err();
127 }
128}