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 keyboard_layout(&self) -> String {
51 "unknown".to_string()
52 }
53
54 fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
55 vec![]
56 }
57
58 fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
59 None
60 }
61
62 fn display(&self, _id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
63 None
64 }
65
66 fn active_window(&self) -> Option<AnyWindowHandle> {
67 None
68 }
69
70 fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
71 None
72 }
73
74 fn open_window(
75 &self,
76 _handle: AnyWindowHandle,
77 _params: WindowParams,
78 ) -> anyhow::Result<Box<dyn PlatformWindow>> {
79 Err(anyhow::anyhow!(
80 "neither DISPLAY nor WAYLAND_DISPLAY is set. You can run in headless mode"
81 ))
82 }
83
84 fn compositor_name(&self) -> &'static str {
85 "headless"
86 }
87
88 fn set_cursor_style(&self, _style: CursorStyle) {}
89
90 fn open_uri(&self, _uri: &str) {}
91
92 fn reveal_path(&self, _path: std::path::PathBuf) {}
93
94 fn write_to_primary(&self, _item: crate::ClipboardItem) {}
95
96 fn write_to_clipboard(&self, _item: crate::ClipboardItem) {}
97
98 fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
99 None
100 }
101
102 fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
103 None
104 }
105
106 fn run(&self) {
107 let mut event_loop = self
108 .0
109 .borrow_mut()
110 .event_loop
111 .take()
112 .expect("App is already running");
113
114 event_loop.run(None, &mut self.clone(), |_| {}).log_err();
115 }
116}