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