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