client.rs

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