Skip to content

Commit

Permalink
Merge branch 'qdlmcfresh-stylus_windows2' into websocket_rework_2
Browse files Browse the repository at this point in the history
Additionally, bump some deps and fix a typo in the Readme.
  • Loading branch information
H-M-H committed Sep 24, 2024
2 parents 5db9cc6 + b332ec3 commit 6b60ec8
Show file tree
Hide file tree
Showing 18 changed files with 521 additions and 35 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ List of Contributors:
**************************
Robert Schroll
Daniel Rutz
Philipp Urlbauer
OmegaRogue
**************************

Expand Down
42 changes: 42 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ tracing = "^0.1"
tracing-subscriber = { version = "^0.3", features = ["ansi", "json"], default-features = false }
url = "^2.5"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["d3d11", "d3dcommon", "dxgi", "dxgi1_2", "dxgitype"] }
wio = "0.2.2"
captrs = "^0.3.1"

[build-dependencies]
cc = "^1.0"
num_cpus = "^1.13"
Expand Down
2 changes: 1 addition & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ After npm is installed, typescript must be installed by:
sudo npm install typescript -g
```

Note that building for the first time may take a while as by default ffmpeg needs to be build. On
Note that building for the first time may take a while as by default ffmpeg needs to be built. On
Windows only msvc is supported as C compiler; it is, however, possible to cross compile on Linux for
Windows using minGW.

Expand Down
6 changes: 3 additions & 3 deletions src/capturable/autopilot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::error::Error;

use image_autopilot::GenericImageView;

use crate::capturable::{Capturable, Recorder};
use crate::capturable::{Capturable, Geometry, Recorder};

#[derive(Clone)]
pub struct AutoPilotCapturable {}
Expand All @@ -18,8 +18,8 @@ impl Capturable for AutoPilotCapturable {
fn name(&self) -> String {
"Desktop (autopilot)".into()
}
fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box<dyn Error>> {
Ok((0.0, 0.0, 1.0, 1.0))
fn geometry(&self) -> Result<Geometry, Box<dyn Error>> {
Ok(Geometry::Relative(0.0, 0.0, 1.0, 1.0))
}
fn before_input(&mut self) -> Result<(), Box<dyn Error>> {
Ok(())
Expand Down
84 changes: 84 additions & 0 deletions src/capturable/captrs_capture.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use crate::capturable::{Capturable, Recorder};
use captrs::Capturer;
use std::boxed::Box;
use std::error::Error;
use winapi::shared::windef::RECT;

use super::Geometry;

#[derive(Clone)]
pub struct CaptrsCapturable {
id: u8,
name: String,
screen: RECT,
virtual_screen: RECT,
}

impl CaptrsCapturable {
pub fn new(id: u8, name: String, screen: RECT, virtual_screen: RECT) -> CaptrsCapturable {
CaptrsCapturable {
id,
name,
screen,
virtual_screen,
}
}
}

impl Capturable for CaptrsCapturable {
fn name(&self) -> String {
format!("Desktop {} (captrs)", self.name).into()
}
fn before_input(&mut self) -> Result<(), Box<dyn Error>> {
Ok(())
}
fn recorder(&self, _capture_cursor: bool) -> Result<Box<dyn Recorder>, Box<dyn Error>> {
Ok(Box::new(CaptrsRecorder::new(self.id)?))
}
fn geometry(&self) -> Result<Geometry, Box<dyn Error>> {
Ok(Geometry::VirtualScreen(
self.screen.left - self.virtual_screen.left,
self.screen.top - self.virtual_screen.top,
(self.screen.right - self.screen.left) as u32,
(self.screen.bottom - self.screen.top) as u32,
self.screen.left,
self.screen.top,
))
}
}
#[derive(Debug)]
pub struct CaptrsError(String);

impl std::fmt::Display for CaptrsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self(s) = self;
write!(f, "{}", s)
}
}

impl Error for CaptrsError {}
pub struct CaptrsRecorder {
capturer: Capturer,
}

impl CaptrsRecorder {
pub fn new(id: u8) -> Result<CaptrsRecorder, Box<dyn Error>> {
Ok(CaptrsRecorder {
capturer: Capturer::new(id.into())?,
})
}
}

impl Recorder for CaptrsRecorder {
fn capture(&mut self) -> Result<crate::video::PixelProvider, Box<dyn Error>> {
self.capturer
.capture_store_frame()
.map_err(|_e| CaptrsError("Captrs failed to capture frame".into()))?;
let (w, h) = self.capturer.geometry();
Ok(crate::video::PixelProvider::BGR0(
w as usize,
h as usize,
unsafe { std::mem::transmute(self.capturer.get_stored_frame().unwrap()) },
))
}
}
11 changes: 6 additions & 5 deletions src/capturable/core_graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use core_graphics::{
window::CGWindowID,
};

use crate::capturable::{Capturable, Recorder};
use crate::capturable::{Capturable, Geometry, Recorder};

#[derive(Debug)]
pub struct CGError(String);
Expand Down Expand Up @@ -52,10 +52,10 @@ impl Capturable for CGDisplayCapturable {
self.display.pixels_high()
)
}
fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box<dyn Error>> {
fn geometry(&self) -> Result<Geometry, Box<dyn Error>> {
let bounds = self.display.bounds();
let (x0, y0, w, h) = screen_coordsys()?;
Ok((
Ok(Geometry::Relative(
(bounds.origin.x - x0) / w,
(bounds.origin.y - y0) / h,
bounds.size.width / w,
Expand Down Expand Up @@ -175,8 +175,9 @@ impl Capturable for CGWindowCapturable {
fn name(&self) -> String {
self.name.clone()
}
fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box<dyn Error>> {
Ok(self.geometry_relative)
fn geometry(&self) -> Result<Geometry, Box<dyn Error>> {
let (x, y, w, h) = self.geometry_relative;
Ok(Geometry::Relative(x, y, w, h))
}
fn before_input(&mut self) -> Result<(), Box<dyn Error>> {
self.update_geometry()
Expand Down
36 changes: 30 additions & 6 deletions src/capturable/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod autopilot;

use std::boxed::Box;
use std::error::Error;
use tracing::warn;
Expand All @@ -10,9 +11,13 @@ pub mod pipewire;
#[cfg(target_os = "linux")]
pub mod pipewire_dbus;
pub mod testsrc;

#[cfg(target_os = "windows")]
pub mod captrs_capture;
#[cfg(target_os = "windows")]
pub mod win_ctx;
#[cfg(target_os = "linux")]
pub mod x11;

pub trait Recorder {
fn capture(&mut self) -> Result<crate::video::PixelProvider, Box<dyn Error>>;
}
Expand All @@ -29,14 +34,20 @@ where
Box::new(self.clone())
}
}
/// Relative: x, y, width, height of the Capturable as floats relative to the absolute size of the
/// screen. For example x=0.5, y=0.0, width=0.5, height=1.0 means the right half of the screen.
/// VirtualScreen: offset_x, offset_y, width, height for a capturable using a virtual screen. (Windows)
pub enum Geometry {
Relative(f64, f64, f64, f64),
VirtualScreen(i32, i32, u32, u32, i32, i32),
}

pub trait Capturable: Send + BoxCloneCapturable {
/// Name of the Capturable, for example the window title, if it is a window.
fn name(&self) -> String;

/// Return x, y, width, height of the Capturable as floats relative to the absolute size of the
/// screen. For example x=0.5, y=0.0, width=0.5, height=1.0 means the right half of the screen.
fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box<dyn Error>>;
/// Return Geometry of the Capturable.
fn geometry(&self) -> Result<Geometry, Box<dyn Error>>;

/// Callback that is called right before input is simulated.
/// Useful to focus the window on input.
Expand Down Expand Up @@ -112,8 +123,21 @@ pub fn get_capturables(
}
}

use crate::capturable::autopilot::AutoPilotCapturable;
capturables.push(Box::new(AutoPilotCapturable::new()));
#[cfg(target_os = "windows")]
{
use crate::capturable::captrs_capture::CaptrsCapturable;
use crate::capturable::win_ctx::WinCtx;
let winctx = WinCtx::new();
for (i, o) in winctx.get_outputs().iter().enumerate() {
let captr = CaptrsCapturable::new(
i as u8,
String::from_utf16_lossy(o.DeviceName.as_ref()),
o.DesktopCoordinates,
winctx.get_union_rect().clone(),
);
capturables.push(Box::new(captr));
}
}

if crate::log::get_log_level() >= tracing::Level::DEBUG {
for (width, height) in [
Expand Down
6 changes: 3 additions & 3 deletions src/capturable/pipewire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use gstreamer as gst;
use gstreamer::prelude::*;
use gstreamer_app::AppSink;

use crate::capturable::{Capturable, Recorder};
use crate::capturable::{Capturable, Geometry, Recorder};
use crate::video::PixelProvider;

use crate::capturable::pipewire_dbus::{
Expand Down Expand Up @@ -96,8 +96,8 @@ impl Capturable for PipeWireCapturable {
format!("Pipewire {}, path: {}", type_str, self.path)
}

fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box<dyn Error>> {
Ok((0.0, 0.0, 1.0, 1.0))
fn geometry(&self) -> Result<Geometry, Box<dyn Error>> {
Ok(Geometry::Relative(0.0, 0.0, 1.0, 1.0))
}

fn before_input(&mut self) -> Result<(), Box<dyn Error>> {
Expand Down
6 changes: 3 additions & 3 deletions src/capturable/testsrc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::capturable::{Capturable, Recorder};
use crate::capturable::{Capturable, Geometry, Recorder};
use crate::video::PixelProvider;
use std::error::Error;

Expand Down Expand Up @@ -43,8 +43,8 @@ impl Capturable for TestCapturable {
fn name(&self) -> String {
format!("Test Source {}x{}", self.width, self.height)
}
fn geometry_relative(&self) -> Result<(f64, f64, f64, f64), Box<dyn Error>> {
Ok((1.0, 1.0, 1.0, 1.0))
fn geometry(&self) -> Result<Geometry, Box<dyn Error>> {
Ok(Geometry::Relative(0.0, 0.0, 1.0, 1.0))
}
fn before_input(&mut self) -> Result<(), Box<dyn Error>> {
Ok(())
Expand Down
Loading

0 comments on commit 6b60ec8

Please sign in to comment.