Skip to content

Commit

Permalink
Python Release 🎉
Browse files Browse the repository at this point in the history
	modified:   src/monitor.rs
	new file:   windows-capture-python/rust-toolchain.toml
	new file:   windows-capture-python/rustfmt.toml
	modified:   windows-capture-python/src/lib.rs
	modified:   windows-capture-python/windows_capture/__init__.py
  • Loading branch information
NiiightmareXD committed Nov 10, 2023
1 parent ec09649 commit 9b85372
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 72 deletions.
9 changes: 6 additions & 3 deletions src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ pub struct Monitor {

impl Monitor {
/// Get The Primary Monitor
#[must_use]
pub fn primary() -> Self {
pub fn primary() -> Result<Self, Box<dyn Error + Send + Sync>> {
let point = POINT { x: 0, y: 0 };
let monitor = unsafe { MonitorFromPoint(point, MONITOR_DEFAULTTOPRIMARY) };

Self { monitor }
if monitor.is_invalid() {
return Err(Box::new(MonitorErrors::NotFound));
}

Ok(Self { monitor })
}

/// Get The Monitor From It's Index
Expand Down
3 changes: 3 additions & 0 deletions windows-capture-python/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[toolchain]
channel = "stable"
targets = ["x86_64-pc-windows-msvc"]
12 changes: 12 additions & 0 deletions windows-capture-python/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
wrap_comments = true
edition = "2021"
format_code_in_doc_comments = true
format_macro_matchers = true
format_strings = true
version = "Two"
use_try_shorthand = true
use_field_init_shorthand = true
unstable_features = true
normalize_doc_attributes = true
normalize_comments = true
imports_granularity = "Crate"
167 changes: 113 additions & 54 deletions windows-capture-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
#![warn(clippy::cargo)]
#![allow(clippy::redundant_pub_crate)]

use std::sync::Arc;
use std::{error::Error, sync::Arc};

use ::windows_capture::{
capture::WindowsCaptureHandler,
frame::Frame,
graphics_capture_api::InternalCaptureControl,
monitor::Monitor,
settings::{ColorFormat, WindowsCaptureSettings},
window::Window,
};
use log::{error, info};
use pyo3::{exceptions::PyException, prelude::*, types::PyList};
Expand All @@ -31,54 +32,113 @@ fn windows_capture(_py: Python, m: &PyModule) -> PyResult<()> {
/// Internal Struct Used For Windows Capture
#[pyclass]
pub struct NativeWindowsCapture {
capture_cursor: bool,
draw_border: bool,
on_frame_arrived_callback: Arc<PyObject>,
on_closed: Arc<PyObject>,
capture_cursor: bool,
draw_border: bool,
monitor_index: Option<usize>,
window_name: Option<String>,
}

#[pymethods]
impl NativeWindowsCapture {
/// Create A New Windows Capture Struct
#[new]
#[must_use]
pub fn new(
capture_cursor: bool,
draw_border: bool,
on_frame_arrived_callback: PyObject,
on_closed: PyObject,
) -> Self {
Self {
capture_cursor,
draw_border,
capture_cursor: bool,
draw_border: bool,
monitor_index: Option<usize>,
window_name: Option<String>,
) -> PyResult<Self> {
if window_name.is_some() && monitor_index.is_some() {
return Err(PyException::new_err(
"You Can't Specify Both The Monitor Index And The Window Name",
));
}

if window_name.is_none() && monitor_index.is_none() {
return Err(PyException::new_err(
"You Should Specify Either The Monitor Index Or The Window Name",
));
}

Ok(Self {
on_frame_arrived_callback: Arc::new(on_frame_arrived_callback),
on_closed: Arc::new(on_closed),
}
capture_cursor,
draw_border,
monitor_index,
window_name,
})
}

/// Start Capture
pub fn start(&mut self) -> PyResult<()> {
let settings = match WindowsCaptureSettings::new(
Monitor::primary(),
Some(self.capture_cursor),
Some(self.draw_border),
ColorFormat::Bgra8,
(
self.on_frame_arrived_callback.clone(),
self.on_closed.clone(),
),
) {
Ok(settings) => settings,
Err(e) => Err(PyException::new_err(format!(
"Failed To Create Windows Capture Settings -> {e}"
)))?,
let settings = if self.window_name.is_some() {
let window = match Window::from_contains_name(self.window_name.as_ref().unwrap()) {
Ok(window) => window,
Err(e) => {
return Err(PyException::new_err(format!(
"Failed To Find Window -> {e}"
)));
}
};

match WindowsCaptureSettings::new(
window,
Some(self.capture_cursor),
Some(self.draw_border),
ColorFormat::Bgra8,
(
self.on_frame_arrived_callback.clone(),
self.on_closed.clone(),
),
) {
Ok(settings) => settings,
Err(e) => {
return Err(PyException::new_err(format!(
"Failed To Create Windows Capture Settings -> {e}"
)));
}
}
} else {
let monitor = match Monitor::from_index(self.monitor_index.unwrap()) {
Ok(monitor) => monitor,
Err(e) => {
return Err(PyException::new_err(format!(
"Failed To Get Monitor From Index -> {e}"
)));
}
};

match WindowsCaptureSettings::new(
monitor,
Some(self.capture_cursor),
Some(self.draw_border),
ColorFormat::Bgra8,
(
self.on_frame_arrived_callback.clone(),
self.on_closed.clone(),
),
) {
Ok(settings) => settings,
Err(e) => {
return Err(PyException::new_err(format!(
"Failed To Create Windows Capture Settings -> {e}"
)));
}
}
};

match InnerNativeWindowsCapture::start(settings) {
Ok(_) => (),
Err(e) => Err(PyException::new_err(format!(
"Capture Session Threw An Exception -> {e}"
)))?,
Err(e) => {
return Err(PyException::new_err(format!(
"Capture Session Threw An Exception -> {e}"
)));
}
}

Ok(())
Expand All @@ -93,14 +153,20 @@ struct InnerNativeWindowsCapture {
impl WindowsCaptureHandler for InnerNativeWindowsCapture {
type Flags = (Arc<PyObject>, Arc<PyObject>);

fn new((on_frame_arrived_callback, on_closed): Self::Flags) -> Self {
Self {
fn new(
(on_frame_arrived_callback, on_closed): Self::Flags,
) -> Result<Self, Box<(dyn Error + Send + Sync)>> {
Ok(Self {
on_frame_arrived_callback,
on_closed,
}
})
}

fn on_frame_arrived(&mut self, mut frame: Frame, capture_control: InternalCaptureControl) {
fn on_frame_arrived(
&mut self,
mut frame: Frame,
capture_control: InternalCaptureControl,
) -> Result<(), Box<(dyn Error + Send + Sync)>> {
let width = frame.width();
let height = frame.height();
let buf = match frame.buffer() {
Expand All @@ -110,48 +176,41 @@ impl WindowsCaptureHandler for InnerNativeWindowsCapture {
"Failed To Get Frame Buffer -> {e} -> Gracefully Stopping The Capture Thread"
);
capture_control.stop();
return;
return Ok(());
}
};

let buf = buf.as_raw_buffer();

Python::with_gil(|py| {
Python::with_gil(|py| -> PyResult<()> {
match py.check_signals() {
Ok(_) => (),
Err(_) => {
info!("KeyboardInterrupt Detected -> Gracefully Stopping The Capture Thread");
capture_control.stop();
return;
return Ok(());
}
}

let stop_list = PyList::new(py, [false]);
match self.on_frame_arrived_callback.call1(
self.on_frame_arrived_callback.call1(
py,
(buf.as_ptr() as isize, buf.len(), width, height, stop_list),
) {
Ok(_) => (),
Err(e) => {
error!(
"on_frame_arrived Threw An Exception -> {e} -> Gracefully Stopping The \
Capture Thread"
);
capture_control.stop();
return;
}
};
)?;

if stop_list[0].is_true().unwrap_or(false) {
if stop_list[0].is_true()? {
capture_control.stop();
}
});

Ok(())
})?;

Ok(())
}

fn on_closed(&mut self) {
Python::with_gil(|py| match self.on_closed.call0(py) {
Ok(_) => (),
Err(e) => error!("on_closed Threw An Exception -> {e}"),
});
fn on_closed(&mut self) -> Result<(), Box<(dyn Error + Send + Sync)>> {
Python::with_gil(|py| self.on_closed.call0(py))?;

Ok(())
}
}
Loading

0 comments on commit 9b85372

Please sign in to comment.