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 fn screen_capture_sources(
68 &self,
69 ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn gpui::ScreenCaptureSource>>>>
70 {
71 let (tx, rx) = futures::channel::oneshot::channel();
72 tx.send(Err(anyhow::anyhow!(
73 "Headless mode does not support screen capture."
74 )))
75 .ok();
76 rx
77 }
78
79 fn active_window(&self) -> Option<AnyWindowHandle> {
80 None
81 }
82
83 fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
84 None
85 }
86
87 fn open_window(
88 &self,
89 _handle: AnyWindowHandle,
90 _params: WindowParams,
91 ) -> anyhow::Result<Box<dyn PlatformWindow>> {
92 anyhow::bail!("neither DISPLAY nor WAYLAND_DISPLAY is set. You can run in headless mode");
93 }
94
95 fn compositor_name(&self) -> &'static str {
96 "headless"
97 }
98
99 fn set_cursor_style(&self, _style: CursorStyle) {}
100
101 fn open_uri(&self, _uri: &str) {}
102
103 fn reveal_path(&self, _path: std::path::PathBuf) {}
104
105 fn write_to_primary(&self, _item: gpui::ClipboardItem) {}
106
107 fn write_to_clipboard(&self, _item: gpui::ClipboardItem) {}
108
109 fn read_from_primary(&self) -> Option<gpui::ClipboardItem> {
110 None
111 }
112
113 fn read_from_clipboard(&self) -> Option<gpui::ClipboardItem> {
114 None
115 }
116
117 fn run(&self) {
118 let mut event_loop = self
119 .0
120 .borrow_mut()
121 .event_loop
122 .take()
123 .expect("App is already running");
124
125 event_loop.run(None, &mut self.clone(), |_| {}).log_err();
126 }
127}