client.rs

  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                    runnable.run();
 35                }
 36            })
 37            .ok();
 38
 39        HeadlessClient(Rc::new(RefCell::new(HeadlessClientState {
 40            event_loop: Some(event_loop),
 41            _loop_handle: handle,
 42            common,
 43        })))
 44    }
 45}
 46
 47impl LinuxClient for HeadlessClient {
 48    fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
 49        f(&mut self.0.borrow_mut().common)
 50    }
 51
 52    fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
 53        Box::new(LinuxKeyboardLayout::new("unknown".into()))
 54    }
 55
 56    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
 57        vec![]
 58    }
 59
 60    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
 61        None
 62    }
 63
 64    fn display(&self, _id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
 65        None
 66    }
 67
 68    #[cfg(feature = "screen-capture")]
 69    fn is_screen_capture_supported(&self) -> bool {
 70        false
 71    }
 72
 73    #[cfg(feature = "screen-capture")]
 74    fn screen_capture_sources(
 75        &self,
 76    ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn crate::ScreenCaptureSource>>>>
 77    {
 78        let (mut tx, rx) = futures::channel::oneshot::channel();
 79        tx.send(Err(anyhow::anyhow!(
 80            "Headless mode does not support screen capture."
 81        )))
 82        .ok();
 83        rx
 84    }
 85
 86    fn active_window(&self) -> Option<AnyWindowHandle> {
 87        None
 88    }
 89
 90    fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
 91        None
 92    }
 93
 94    fn open_window(
 95        &self,
 96        _handle: AnyWindowHandle,
 97        _params: WindowParams,
 98    ) -> anyhow::Result<Box<dyn PlatformWindow>> {
 99        anyhow::bail!("neither DISPLAY nor WAYLAND_DISPLAY is set. You can run in headless mode");
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}