client.rs

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