1//! Screen capture for Linux and Windows
  2use crate::{
  3    DevicePixels, ForegroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream,
  4    Size, SourceMetadata, size,
  5};
  6use anyhow::{Context as _, Result, anyhow};
  7use futures::channel::oneshot;
  8use scap::Target;
  9use std::rc::Rc;
 10use std::sync::Arc;
 11use std::sync::atomic::{self, AtomicBool};
 12
 13/// Populates the receiver with the screens that can be captured.
 14///
 15/// `scap_default_target_source` should be used instead on Wayland, since `scap_screen_sources`
 16/// won't return any results.
 17#[allow(dead_code)]
 18pub(crate) fn scap_screen_sources(
 19    foreground_executor: &ForegroundExecutor,
 20) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
 21    let (sources_tx, sources_rx) = oneshot::channel();
 22    get_screen_targets(sources_tx);
 23    to_dyn_screen_capture_sources(sources_rx, foreground_executor)
 24}
 25
 26/// Starts screen capture for the default target, and populates the receiver with a single source
 27/// for it. The first frame of the screen capture is used to determine the size of the stream.
 28///
 29/// On Wayland (Linux), prompts the user to select a target, and populates the receiver with a
 30/// single screen capture source for their selection.
 31#[allow(dead_code)]
 32pub(crate) fn start_scap_default_target_source(
 33    foreground_executor: &ForegroundExecutor,
 34) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
 35    let (sources_tx, sources_rx) = oneshot::channel();
 36    start_default_target_screen_capture(sources_tx);
 37    to_dyn_screen_capture_sources(sources_rx, foreground_executor)
 38}
 39
 40struct ScapCaptureSource {
 41    target: scap::Display,
 42    size: Size<DevicePixels>,
 43}
 44
 45/// Populates the sender with the screens available for capture.
 46fn get_screen_targets(sources_tx: oneshot::Sender<Result<Vec<ScapCaptureSource>>>) {
 47    // Due to use of blocking APIs, a new thread is used.
 48    std::thread::spawn(|| {
 49        let targets = match scap::get_all_targets() {
 50            Ok(targets) => targets,
 51            Err(err) => {
 52                sources_tx.send(Err(err)).ok();
 53                return;
 54            }
 55        };
 56        let sources = targets
 57            .into_iter()
 58            .filter_map(|target| match target {
 59                scap::Target::Display(display) => {
 60                    let size = Size {
 61                        width: DevicePixels(display.width as i32),
 62                        height: DevicePixels(display.height as i32),
 63                    };
 64                    Some(ScapCaptureSource {
 65                        target: display,
 66                        size,
 67                    })
 68                }
 69                scap::Target::Window(_) => None,
 70            })
 71            .collect::<Vec<_>>();
 72        sources_tx.send(Ok(sources)).ok();
 73    });
 74}
 75
 76impl ScreenCaptureSource for ScapCaptureSource {
 77    fn metadata(&self) -> Result<SourceMetadata> {
 78        Ok(SourceMetadata {
 79            resolution: self.size,
 80            label: Some(self.target.title.clone().into()),
 81            is_main: None,
 82            id: self.target.id as u64,
 83        })
 84    }
 85
 86    fn stream(
 87        &self,
 88        foreground_executor: &ForegroundExecutor,
 89        frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
 90    ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
 91        let (stream_tx, stream_rx) = oneshot::channel();
 92        let target = self.target.clone();
 93
 94        // Due to use of blocking APIs, a dedicated thread is used.
 95        std::thread::spawn(move || {
 96            match new_scap_capturer(Some(scap::Target::Display(target.clone()))) {
 97                Ok(mut capturer) => {
 98                    capturer.start_capture();
 99                    run_capture(capturer, target.clone(), frame_callback, stream_tx);
100                }
101                Err(e) => {
102                    stream_tx.send(Err(e)).ok();
103                }
104            }
105        });
106
107        to_dyn_screen_capture_stream(stream_rx, foreground_executor)
108    }
109}
110
111struct ScapDefaultTargetCaptureSource {
112    // Sender populated by single call to `ScreenCaptureSource::stream`.
113    stream_call_tx: std::sync::mpsc::SyncSender<(
114        // Provides the result of `ScreenCaptureSource::stream`.
115        oneshot::Sender<Result<ScapStream>>,
116        // Callback for frames.
117        Box<dyn Fn(ScreenCaptureFrame) + Send>,
118    )>,
119    target: scap::Display,
120    size: Size<DevicePixels>,
121}
122
123/// Starts screen capture on the default capture target, and populates the sender with the source.
124fn start_default_target_screen_capture(
125    sources_tx: oneshot::Sender<Result<Vec<ScapDefaultTargetCaptureSource>>>,
126) {
127    // Due to use of blocking APIs, a dedicated thread is used.
128    std::thread::spawn(|| {
129        let start_result = util::maybe!({
130            let mut capturer = new_scap_capturer(None)?;
131            capturer.start_capture();
132            let first_frame = capturer
133                .get_next_frame()
134                .context("Failed to get first frame of screenshare to get the size.")?;
135            let size = frame_size(&first_frame);
136            let target = capturer
137                .target()
138                .context("Unable to determine the target display.")?;
139            let target = target.clone();
140            Ok((capturer, size, target))
141        });
142
143        match start_result {
144            Ok((capturer, size, Target::Display(display))) => {
145                let (stream_call_tx, stream_rx) = std::sync::mpsc::sync_channel(1);
146                sources_tx
147                    .send(Ok(vec![ScapDefaultTargetCaptureSource {
148                        stream_call_tx,
149                        size,
150                        target: display.clone(),
151                    }]))
152                    .ok();
153                let Ok((stream_tx, frame_callback)) = stream_rx.recv() else {
154                    return;
155                };
156                run_capture(capturer, display, frame_callback, stream_tx);
157            }
158            Err(e) => {
159                sources_tx.send(Err(e)).ok();
160            }
161            _ => {
162                sources_tx
163                    .send(Err(anyhow!("The screen capture source is not a display")))
164                    .ok();
165            }
166        }
167    });
168}
169
170impl ScreenCaptureSource for ScapDefaultTargetCaptureSource {
171    fn metadata(&self) -> Result<SourceMetadata> {
172        Ok(SourceMetadata {
173            resolution: self.size,
174            label: None,
175            is_main: None,
176            id: self.target.id as u64,
177        })
178    }
179
180    fn stream(
181        &self,
182        foreground_executor: &ForegroundExecutor,
183        frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
184    ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
185        let (tx, rx) = oneshot::channel();
186        match self.stream_call_tx.try_send((tx, frame_callback)) {
187            Ok(()) => {}
188            Err(std::sync::mpsc::TrySendError::Full((tx, _)))
189            | Err(std::sync::mpsc::TrySendError::Disconnected((tx, _))) => {
190                // Note: support could be added for being called again after end of prior stream.
191                tx.send(Err(anyhow!(
192                    "Can't call ScapDefaultTargetCaptureSource::stream multiple times."
193                )))
194                .ok();
195            }
196        }
197        to_dyn_screen_capture_stream(rx, foreground_executor)
198    }
199}
200
201fn new_scap_capturer(target: Option<scap::Target>) -> Result<scap::capturer::Capturer> {
202    scap::capturer::Capturer::build(scap::capturer::Options {
203        fps: 60,
204        show_cursor: true,
205        show_highlight: true,
206        // Note that the actual frame output type may differ.
207        output_type: scap::frame::FrameType::YUVFrame,
208        output_resolution: scap::capturer::Resolution::Captured,
209        crop_area: None,
210        target,
211        excluded_targets: None,
212    })
213}
214
215fn run_capture(
216    mut capturer: scap::capturer::Capturer,
217    display: scap::Display,
218    frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
219    stream_tx: oneshot::Sender<Result<ScapStream>>,
220) {
221    let cancel_stream = Arc::new(AtomicBool::new(false));
222    let size = Size {
223        width: DevicePixels(display.width as i32),
224        height: DevicePixels(display.height as i32),
225    };
226    let stream_send_result = stream_tx.send(Ok(ScapStream {
227        cancel_stream: cancel_stream.clone(),
228        display,
229        size,
230    }));
231    if stream_send_result.is_err() {
232        return;
233    }
234    while !cancel_stream.load(std::sync::atomic::Ordering::SeqCst) {
235        match capturer.get_next_frame() {
236            Ok(frame) => frame_callback(ScreenCaptureFrame(frame)),
237            Err(err) => {
238                log::error!("Halting screen capture due to error: {err}");
239                break;
240            }
241        }
242    }
243    capturer.stop_capture();
244}
245
246struct ScapStream {
247    cancel_stream: Arc<AtomicBool>,
248    display: scap::Display,
249    size: Size<DevicePixels>,
250}
251
252impl ScreenCaptureStream for ScapStream {
253    fn metadata(&self) -> Result<SourceMetadata> {
254        Ok(SourceMetadata {
255            resolution: self.size,
256            label: Some(self.display.title.clone().into()),
257            is_main: None,
258            id: self.display.id as u64,
259        })
260    }
261}
262
263impl Drop for ScapStream {
264    fn drop(&mut self) {
265        self.cancel_stream.store(true, atomic::Ordering::SeqCst);
266    }
267}
268
269fn frame_size(frame: &scap::frame::Frame) -> Size<DevicePixels> {
270    let (width, height) = match frame {
271        scap::frame::Frame::YUVFrame(frame) => (frame.width, frame.height),
272        scap::frame::Frame::RGB(frame) => (frame.width, frame.height),
273        scap::frame::Frame::RGBx(frame) => (frame.width, frame.height),
274        scap::frame::Frame::XBGR(frame) => (frame.width, frame.height),
275        scap::frame::Frame::BGRx(frame) => (frame.width, frame.height),
276        scap::frame::Frame::BGR0(frame) => (frame.width, frame.height),
277        scap::frame::Frame::BGRA(frame) => (frame.width, frame.height),
278    };
279    size(DevicePixels(width), DevicePixels(height))
280}
281
282/// This is used by `get_screen_targets` and `start_default_target_screen_capture` to turn their
283/// results into `Rc<dyn ScreenCaptureSource>`. They need to `Send` their capture source, and so
284/// the capture source structs are used as `Rc<dyn ScreenCaptureSource>` is not `Send`.
285fn to_dyn_screen_capture_sources<T: ScreenCaptureSource + 'static>(
286    sources_rx: oneshot::Receiver<Result<Vec<T>>>,
287    foreground_executor: &ForegroundExecutor,
288) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
289    let (dyn_sources_tx, dyn_sources_rx) = oneshot::channel();
290    foreground_executor
291        .spawn(async move {
292            match sources_rx.await {
293                Ok(Ok(results)) => dyn_sources_tx
294                    .send(Ok(results
295                        .into_iter()
296                        .map(|source| Rc::new(source) as Rc<dyn ScreenCaptureSource>)
297                        .collect::<Vec<_>>()))
298                    .ok(),
299                Ok(Err(err)) => dyn_sources_tx.send(Err(err)).ok(),
300                Err(oneshot::Canceled) => None,
301            }
302        })
303        .detach();
304    dyn_sources_rx
305}
306
307/// Same motivation as `to_dyn_screen_capture_sources` above.
308fn to_dyn_screen_capture_stream<T: ScreenCaptureStream + 'static>(
309    sources_rx: oneshot::Receiver<Result<T>>,
310    foreground_executor: &ForegroundExecutor,
311) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
312    let (dyn_sources_tx, dyn_sources_rx) = oneshot::channel();
313    foreground_executor
314        .spawn(async move {
315            match sources_rx.await {
316                Ok(Ok(stream)) => dyn_sources_tx
317                    .send(Ok(Box::new(stream) as Box<dyn ScreenCaptureStream>))
318                    .ok(),
319                Ok(Err(err)) => dyn_sources_tx.send(Err(err)).ok(),
320                Err(oneshot::Canceled) => None,
321            }
322        })
323        .detach();
324    dyn_sources_rx
325}