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, 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}