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