1use std::ffi::{c_char, c_int, c_void};
11
12mod runtime_impl;
13mod shim_loader;
14
15#[cfg(all(not(target_os = "windows"), feature = "stream"))]
16mod stream_unix;
17
18#[cfg(feature = "stream")]
19use std::cell::RefCell;
20
21#[cfg(all(target_os = "windows", feature = "stream"))]
22use std::thread::JoinHandle;
23
24#[cfg(all(target_os = "windows", feature = "stream"))]
25mod stream_windows;
26
27pub(crate) const EXPECTED_CABI_VERSION: u32 = 1;
28
29#[derive(Debug, Clone)]
35#[non_exhaustive]
36pub enum PylonError {
37 Msg(String),
39 ShimCallFailed {
41 op: &'static str,
43 callsite: String,
45 err_str: String,
47 },
48 InvalidShimOutput {
50 op: &'static str,
52 detail: String,
54 },
55 DlOpenFailed {
57 path: std::ffi::OsString,
59 source: String,
60 },
61 ShimError(ShimError),
63}
64
65#[derive(Debug, Clone)]
66#[non_exhaustive]
67pub enum ShimError {
69 SymbolLoadFailed {
71 path: std::ffi::OsString,
73 symbol: String,
75 err_str: String,
77 },
78 NullApi {
80 path: std::ffi::OsString,
82 },
83 ApiTableTooSmall {
85 path: std::ffi::OsString,
87 got: u32,
89 need: u32,
91 },
92 IncompatibleAbiVersion {
94 path: std::ffi::OsString,
96 got: u32,
98 need: u32,
100 },
101}
102
103impl PylonError {
104 fn new(msg: String) -> Self {
105 PylonError::Msg(msg)
106 }
107}
108
109impl From<std::str::Utf8Error> for PylonError {
110 fn from(_: std::str::Utf8Error) -> PylonError {
111 PylonError::new("Cannot convert C++ string to UTF-8".to_string())
112 }
113}
114
115impl From<std::io::Error> for PylonError {
116 fn from(orig: std::io::Error) -> PylonError {
117 PylonError::new(orig.to_string())
118 }
119}
120
121impl std::fmt::Display for PylonError {
122 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
123 match self {
124 PylonError::Msg(msg) => write!(f, "PylonError({})", msg),
125 PylonError::ShimCallFailed {
126 op,
127 callsite,
128 err_str,
129 } => write!(
130 f,
131 "PylonError(ShimCallFailed {op} at {callsite}: {err_str})"
132 ),
133 PylonError::InvalidShimOutput { op, detail } => {
134 write!(f, "PylonError(InvalidShimOutput {op}: {detail})")
135 }
136 PylonError::DlOpenFailed { path, source } => write!(
137 f,
138 "There was a problem opening the `libpylon-cabi` shim library. The path was specified \
139 as {path:?}. You can force a specific path \
140 to the shim library using the `PYLON_CABI` environment variable. Shim libraries can \
141 be downloaded from https://strawlab.org/assets/libpylon-cabi/precompiled/ or built \
142 from source. You need v{EXPECTED_CABI_VERSION} of the shim library for this version of `pylon-shimload`.\
143 \n\nThe source of the error was:\n\n\
144 {source}"
145 ),
146 PylonError::ShimError(err) => write!(f, "PylonError(ShimError: {err:?})"),
147 }
148 }
149}
150
151impl std::error::Error for PylonError {}
152
153pub type PylonResult<T> = Result<T, PylonError>;
155
156#[repr(i32)]
161#[derive(Clone, Copy, Debug, PartialEq, Eq)]
162pub enum TimeoutHandling {
164 Return = 0,
166 ThrowException = 1,
168}
169
170#[repr(i32)]
171#[derive(Clone, Copy, Debug, PartialEq, Eq)]
172pub enum GrabStrategy {
174 OneByOne = 0,
176 LatestImageOnly = 1,
178 LatestImages = 2,
180 UpcomingImage = 3,
182}
183
184#[inline]
189#[track_caller]
190fn shim_check_op(op: &'static str, err: *const c_char) -> PylonResult<()> {
191 shim_loader::check_err(err).map_err(|err_str| {
192 let loc = std::panic::Location::caller();
193 PylonError::ShimCallFailed {
194 op,
195 callsite: format!("{}:{}", loc.file(), loc.line()),
196 err_str,
197 }
198 })
199}
200
201#[inline]
202#[track_caller]
203fn shim_check(err: *const c_char) -> PylonResult<()> {
204 shim_check_op("shim_call", err)
205}
206
207#[inline]
208fn ensure_non_null<T>(op: &'static str, what: &'static str, ptr: *const T) -> PylonResult<()> {
209 if ptr.is_null() {
210 Err(PylonError::InvalidShimOutput {
211 op,
212 detail: format!("{what} was null"),
213 })
214 } else {
215 Ok(())
216 }
217}
218
219macro_rules! shim_call {
220 ($shim:expr, $func:ident($($arg:expr),* $(,)?)) => {{
221 let err = unsafe { (($shim).$func)($($arg),*) };
222 shim_check_for!($func, err)
223 }};
224}
225
226macro_rules! shim_check_for {
227 ($op:ident, $err:expr) => {
228 shim_check_op(stringify!($op), $err)
229 };
230}
231
232macro_rules! ensure_non_null_for {
233 ($op:ident, $what:literal, $ptr:expr) => {
234 ensure_non_null(stringify!($op), $what, $ptr)
235 };
236}
237
238pub mod runtime {
253 use crate::{runtime_impl, PylonResult, PylonVersion};
254
255 pub use crate::runtime_impl::RuntimeGuard;
265
266 pub fn init() -> PylonResult<RuntimeGuard> {
284 RuntimeGuard::new()
285 }
286
287 pub fn version() -> PylonResult<PylonVersion> {
291 runtime_impl::runtime_version()
292 }
293
294 pub fn shutdown() -> PylonResult<()> {
303 runtime_impl::shutdown()
304 }
305}
306
307#[derive(Debug)]
309pub struct PylonVersion {
310 pub major: u32,
312 pub minor: u32,
314 pub subminor: u32,
316 pub build: u32,
318}
319
320pub fn version() -> Result<PylonVersion, PylonError> {
322 runtime::version()
323}
324
325pub fn enumerate_devices() -> PylonResult<Vec<DeviceInfo>> {
327 let runtime = runtime_impl::acquire_runtime()?;
328 let mut raw_arr: *mut *mut c_void = std::ptr::null_mut();
329 let mut count: usize = 0;
330 shim_call!(
331 shim_loader::shim(),
332 tl_factory_enumerate_devices(&mut raw_arr, &mut count)
333 )?;
334 if count > 0 {
335 ensure_non_null_for!(tl_factory_enumerate_devices, "device array", raw_arr)?;
336 }
337 let mut result = Vec::with_capacity(count);
338 unsafe {
339 for i in 0..count {
340 result.push(DeviceInfo {
341 ptr: *raw_arr.add(i),
342 runtime: runtime.clone(),
343 });
344 }
345 (shim_loader::shim().pylon_cxx_free_ptr)(raw_arr as *mut _);
346 }
347 Ok(result)
348}
349
350pub fn create_first_device() -> PylonResult<InstantCamera> {
352 let runtime = runtime_impl::acquire_runtime()?;
353 let mut ptr: *mut c_void = std::ptr::null_mut();
354 shim_call!(
355 shim_loader::shim(),
356 tl_factory_create_first_device(&mut ptr)
357 )?;
358 ensure_non_null_for!(tl_factory_create_first_device, "camera pointer", ptr)?;
359 Ok(InstantCamera::from_ptr(runtime, ptr))
360}
361
362pub fn create_device(device_info: &DeviceInfo) -> PylonResult<InstantCamera> {
364 let runtime = runtime_impl::acquire_runtime()?;
365 let mut ptr: *mut c_void = std::ptr::null_mut();
366 shim_call!(
367 shim_loader::shim(),
368 tl_factory_create_device(device_info.ptr, &mut ptr)
369 )?;
370 ensure_non_null_for!(tl_factory_create_device, "camera pointer", ptr)?;
371 Ok(InstantCamera::from_ptr(runtime, ptr))
372}
373
374pub struct InstantCamera {
380 ptr: *mut c_void,
381 #[cfg(all(not(target_os = "windows"), feature = "stream"))]
382 pub(crate) fd: RefCell<Option<tokio::io::unix::AsyncFd<std::os::unix::io::RawFd>>>,
383 #[cfg(all(target_os = "windows", feature = "stream"))]
384 pub(crate) wait_thread: RefCell<Option<JoinHandle<()>>>,
385 _runtime: runtime_impl::RuntimeLease,
387}
388
389unsafe impl Send for InstantCamera {}
390
391impl InstantCamera {
392 fn from_ptr(runtime: runtime_impl::RuntimeLease, ptr: *mut c_void) -> Self {
393 InstantCamera {
394 ptr,
395 _runtime: runtime,
396 #[cfg(all(not(target_os = "windows"), feature = "stream"))]
397 fd: RefCell::new(None),
398 #[cfg(all(target_os = "windows", feature = "stream"))]
399 wait_thread: RefCell::new(None),
400 }
401 }
402
403 pub fn device_info(&self) -> PylonResult<DeviceInfo> {
405 {
406 let mut out: *mut c_void = std::ptr::null_mut();
407 shim_call!(
408 shim_loader::shim(),
409 instant_camera_get_device_info(self.ptr, &mut out)
410 )?;
411 ensure_non_null_for!(instant_camera_get_device_info, "device info pointer", out)?;
412 Ok(DeviceInfo {
413 ptr: out,
414 runtime: self._runtime.clone(),
415 })
416 }
417 }
418
419 pub fn open(&self) -> PylonResult<()> {
421 shim_call!(shim_loader::shim(), instant_camera_open(self.ptr))
422 }
423
424 pub fn is_open(&self) -> PylonResult<bool> {
426 {
427 let mut out: c_int = 0;
428 shim_call!(
429 shim_loader::shim(),
430 instant_camera_is_open(self.ptr, &mut out)
431 )?;
432 Ok(out != 0)
433 }
434 }
435
436 pub fn close(&self) -> PylonResult<()> {
438 shim_call!(shim_loader::shim(), instant_camera_close(self.ptr))
439 }
440
441 pub fn start_grabbing(&self, options: &GrabOptions) -> PylonResult<()> {
443 {
444 #[cfg(all(not(target_os = "windows"), feature = "stream"))]
446 {
447 if tokio::runtime::Handle::try_current().is_ok() {
448 self.fd.replace(Some(tokio::io::unix::AsyncFd::new(
449 self.get_grab_result_fd()?,
450 )?));
451 }
452 }
453
454 let s = shim_loader::shim();
455 match (options.count, options.strategy) {
456 (Some(count), Some(strategy)) => shim_call!(
457 s,
458 instant_camera_start_grabbing_with_count_and_strategy(
459 self.ptr,
460 count,
461 strategy as c_int
462 )
463 ),
464 (Some(count), None) => {
465 shim_call!(s, instant_camera_start_grabbing_with_count(self.ptr, count))
466 }
467 (None, Some(strategy)) => shim_call!(
468 s,
469 instant_camera_start_grabbing_with_strategy(self.ptr, strategy as c_int)
470 ),
471 (None, None) => shim_call!(s, instant_camera_start_grabbing(self.ptr)),
472 }
473 }
474 }
475
476 pub fn stop_grabbing(&self) -> PylonResult<()> {
478 {
479 shim_call!(shim_loader::shim(), instant_camera_stop_grabbing(self.ptr))?;
480 #[cfg(all(not(target_os = "windows"), feature = "stream"))]
481 self.fd.replace(None);
482 #[cfg(all(target_os = "windows", feature = "stream"))]
483 self.wait_thread.replace(None);
484 Ok(())
485 }
486 }
487
488 pub fn is_grabbing(&self) -> bool {
490 unsafe { (shim_loader::shim().instant_camera_is_grabbing)(self.ptr) != 0 }
491 }
492
493 pub fn retrieve_result(
497 &self,
498 timeout_ms: u32,
499 grab_result: &mut GrabResult,
500 timeout_handling: TimeoutHandling,
501 ) -> PylonResult<bool> {
502 {
503 let mut grabbed: c_int = 0;
504 let err = unsafe {
505 (shim_loader::shim().instant_camera_retrieve_result)(
506 self.ptr,
507 timeout_ms,
508 grab_result.ptr,
509 timeout_handling as c_int,
510 &mut grabbed,
511 )
512 };
513 shim_check_for!(instant_camera_retrieve_result, err)?;
514 Ok(grabbed != 0)
515 }
516 }
517
518 #[cfg(all(not(target_os = "windows"), feature = "stream"))]
519 pub fn get_grab_result_fd(&self) -> PylonResult<std::os::unix::io::RawFd> {
521 {
522 let mut fd: c_int = -1;
523 shim_call!(
524 shim_loader::shim(),
525 instant_camera_wait_object_fd(self.ptr, &mut fd)
526 )?;
527 Ok(fd)
528 }
529 }
530
531 #[cfg(all(target_os = "windows", feature = "stream"))]
532 pub(crate) fn get_grab_result_wait_object(&self) -> PylonResult<WaitObject> {
533 {
534 let mut ptr: *mut c_void = std::ptr::null_mut();
535 shim_call!(
536 shim_loader::shim(),
537 instant_camera_wait_object(self.ptr, &mut ptr)
538 )?;
539 ensure_non_null_for!(instant_camera_wait_object, "wait object pointer", ptr)?;
540 Ok(WaitObject(ptr))
541 }
542 }
543}
544
545impl Drop for InstantCamera {
546 fn drop(&mut self) {
547 if !self.ptr.is_null() {
548 unsafe { (shim_loader::shim().instant_camera_destroy)(self.ptr) }
549 }
550 }
551}
552
553pub struct NodeMap<'parent> {
557 ptr: *const c_void,
558 _marker: std::marker::PhantomData<&'parent ()>,
559}
560
561impl InstantCamera {
562 pub fn node_map<'a>(&'a self) -> PylonResult<NodeMap<'a>> {
564 {
565 let mut ptr: *const c_void = std::ptr::null();
566 let err =
567 unsafe { (shim_loader::shim().instant_camera_get_node_map)(self.ptr, &mut ptr) };
568 shim_check_for!(instant_camera_get_node_map, err)?;
569 ensure_non_null_for!(instant_camera_get_node_map, "node map pointer", ptr)?;
570 Ok(NodeMap {
571 ptr,
572 _marker: std::marker::PhantomData,
573 })
574 }
575 }
576 pub fn tl_node_map<'a>(&'a self) -> PylonResult<NodeMap<'a>> {
578 {
579 let mut ptr: *const c_void = std::ptr::null();
580 let err =
581 unsafe { (shim_loader::shim().instant_camera_get_tl_node_map)(self.ptr, &mut ptr) };
582 shim_check_for!(instant_camera_get_tl_node_map, err)?;
583 ensure_non_null_for!(instant_camera_get_tl_node_map, "tl node map pointer", ptr)?;
584 Ok(NodeMap {
585 ptr,
586 _marker: std::marker::PhantomData,
587 })
588 }
589 }
590 pub fn stream_grabber_node_map<'a>(&'a self) -> PylonResult<NodeMap<'a>> {
592 {
593 let mut ptr: *const c_void = std::ptr::null();
594 let err = unsafe {
595 (shim_loader::shim().instant_camera_get_stream_grabber_node_map)(self.ptr, &mut ptr)
596 };
597 shim_check_for!(instant_camera_get_stream_grabber_node_map, err)?;
598 ensure_non_null(
599 stringify!(instant_camera_get_stream_grabber_node_map),
600 "stream grabber node map pointer",
601 ptr,
602 )?;
603 Ok(NodeMap {
604 ptr,
605 _marker: std::marker::PhantomData,
606 })
607 }
608 }
609 pub fn event_grabber_node_map<'a>(&'a self) -> PylonResult<NodeMap<'a>> {
611 {
612 let mut ptr: *const c_void = std::ptr::null();
613 let err = unsafe {
614 (shim_loader::shim().instant_camera_get_event_grabber_node_map)(self.ptr, &mut ptr)
615 };
616 shim_check_for!(instant_camera_get_event_grabber_node_map, err)?;
617 ensure_non_null(
618 stringify!(instant_camera_get_event_grabber_node_map),
619 "event grabber node map pointer",
620 ptr,
621 )?;
622 Ok(NodeMap {
623 ptr,
624 _marker: std::marker::PhantomData,
625 })
626 }
627 }
628 pub fn instant_camera_node_map<'a>(&'a self) -> PylonResult<NodeMap<'a>> {
630 {
631 let mut ptr: *const c_void = std::ptr::null();
632 let err = unsafe {
633 (shim_loader::shim().instant_camera_get_instant_camera_node_map)(self.ptr, &mut ptr)
634 };
635 shim_check_for!(instant_camera_get_instant_camera_node_map, err)?;
636 ensure_non_null(
637 stringify!(instant_camera_get_instant_camera_node_map),
638 "instant camera node map pointer",
639 ptr,
640 )?;
641 Ok(NodeMap {
642 ptr,
643 _marker: std::marker::PhantomData,
644 })
645 }
646 }
647}
648
649impl<'parent> NodeMap<'parent> {
650 pub fn load<P: AsRef<std::path::Path>>(&self, path: P, validate: bool) -> PylonResult<()> {
652 {
653 let filename = path_to_string(path)?;
654 let err = unsafe {
655 (shim_loader::shim().node_map_load)(
656 self.ptr,
657 filename.as_ptr() as *const c_char,
658 filename.len(),
659 validate as c_int,
660 )
661 };
662 shim_check(err)
663 }
664 }
665 pub fn save<P: AsRef<std::path::Path>>(&self, path: P) -> PylonResult<()> {
667 {
668 let filename = path_to_string(path)?;
669 let err = unsafe {
670 (shim_loader::shim().node_map_save)(
671 self.ptr,
672 filename.as_ptr() as *const c_char,
673 filename.len(),
674 )
675 };
676 shim_check(err)
677 }
678 }
679 pub fn load_from_string(&self, features: String, validate: bool) -> PylonResult<()> {
681 {
682 let err = unsafe {
683 (shim_loader::shim().node_map_load_from_string)(
684 self.ptr,
685 features.as_ptr() as *const c_char,
686 features.len(),
687 validate as c_int,
688 )
689 };
690 shim_check(err)
691 }
692 }
693 pub fn save_to_string(&self) -> PylonResult<String> {
695 {
696 let mut out: *mut c_char = std::ptr::null_mut();
697 let err = unsafe { (shim_loader::shim().node_map_save_to_string)(self.ptr, &mut out) };
698 shim_check_for!(node_map_save_to_string, err)?;
699 ensure_non_null_for!(node_map_save_to_string, "serialized node map string", out)?;
700 Ok(unsafe { shim_loader::take_str(out) })
701 }
702 }
703
704 fn get_param_raw(
705 &self,
706 name: &str,
707 getter: unsafe extern "C" fn(
708 *const c_void,
709 *const c_char,
710 usize,
711 *mut *mut c_void,
712 ) -> *const c_char,
713 ) -> PylonResult<*mut c_void> {
714 {
715 let mut out: *mut c_void = std::ptr::null_mut();
716 let err = unsafe {
717 getter(
718 self.ptr,
719 name.as_ptr() as *const c_char,
720 name.len(),
721 &mut out,
722 )
723 };
724 shim_check(err)?;
725 ensure_non_null_for!(node_map_get_parameter, "parameter pointer", out)?;
726 Ok(out)
727 }
728 }
729
730 pub fn boolean_node(&self, name: &str) -> PylonResult<BooleanNode> {
732 Ok(BooleanNode {
733 name: name.to_string(),
734 ptr: self.get_param_raw(name, shim_loader::shim().node_map_get_boolean_parameter)?,
735 })
736 }
737 pub fn integer_node(&self, name: &str) -> PylonResult<IntegerNode> {
739 Ok(IntegerNode {
740 name: name.to_string(),
741 ptr: self.get_param_raw(name, shim_loader::shim().node_map_get_integer_parameter)?,
742 })
743 }
744 pub fn float_node(&self, name: &str) -> PylonResult<FloatNode> {
746 Ok(FloatNode {
747 name: name.to_string(),
748 ptr: self.get_param_raw(name, shim_loader::shim().node_map_get_float_parameter)?,
749 })
750 }
751 pub fn enum_node(&self, name: &str) -> PylonResult<EnumNode> {
753 Ok(EnumNode {
754 name: name.to_string(),
755 ptr: self.get_param_raw(name, shim_loader::shim().node_map_get_enum_parameter)?,
756 })
757 }
758 pub fn command_node(&self, name: &str) -> PylonResult<CommandNode> {
760 Ok(CommandNode {
761 name: name.to_string(),
762 ptr: self.get_param_raw(name, shim_loader::shim().node_map_get_command_parameter)?,
763 })
764 }
765}
766
767#[derive(Default)]
772pub struct GrabOptions {
774 count: Option<u32>,
775 strategy: Option<GrabStrategy>,
776}
777
778impl GrabOptions {
779 pub fn count(self, count: u32) -> GrabOptions {
781 Self {
782 count: Some(count),
783 ..self
784 }
785 }
786 pub fn strategy(self, strategy: GrabStrategy) -> GrabOptions {
788 Self {
789 strategy: Some(strategy),
790 ..self
791 }
792 }
793}
794
795pub struct BooleanNode {
801 name: String,
802 ptr: *mut c_void,
803}
804
805impl BooleanNode {
806 pub fn name(&self) -> &str {
808 &self.name
809 }
810
811 pub fn value(&self) -> PylonResult<bool> {
813 {
814 let mut out: c_int = 0;
815 let err = unsafe { (shim_loader::shim().boolean_node_get_value)(self.ptr, &mut out) };
816 shim_check(err)?;
817 Ok(out != 0)
818 }
819 }
820 pub fn set_value(&mut self, value: bool) -> PylonResult<()> {
822 shim_check(unsafe {
823 (shim_loader::shim().boolean_node_set_value)(self.ptr, value as c_int)
824 })
825 }
826}
827
828impl Drop for BooleanNode {
829 fn drop(&mut self) {
830 if !self.ptr.is_null() {
831 unsafe { (shim_loader::shim().boolean_parameter_destroy)(self.ptr) }
832 }
833 }
834}
835
836pub struct IntegerNode {
838 name: String,
839 ptr: *mut c_void,
840}
841
842impl IntegerNode {
843 pub fn name(&self) -> &str {
845 &self.name
846 }
847
848 pub fn unit(&self) -> PylonResult<String> {
850 {
851 let mut out: *mut c_char = std::ptr::null_mut();
852 let err = unsafe { (shim_loader::shim().integer_node_get_unit)(self.ptr, &mut out) };
853 shim_check_for!(integer_node_get_unit, err)?;
854 ensure_non_null_for!(integer_node_get_unit, "unit string", out)?;
855 Ok(unsafe { shim_loader::take_str(out) })
856 }
857 }
858 pub fn value(&self) -> PylonResult<i64> {
860 {
861 let mut out = 0i64;
862 let err = unsafe { (shim_loader::shim().integer_node_get_value)(self.ptr, &mut out) };
863 shim_check(err)?;
864 Ok(out)
865 }
866 }
867 pub fn min(&self) -> PylonResult<i64> {
869 {
870 let mut out = 0i64;
871 let err = unsafe { (shim_loader::shim().integer_node_get_min)(self.ptr, &mut out) };
872 shim_check(err)?;
873 Ok(out)
874 }
875 }
876 pub fn max(&self) -> PylonResult<i64> {
878 {
879 let mut out = 0i64;
880 let err = unsafe { (shim_loader::shim().integer_node_get_max)(self.ptr, &mut out) };
881 shim_check(err)?;
882 Ok(out)
883 }
884 }
885 pub fn set_value(&mut self, value: i64) -> PylonResult<()> {
887 shim_check(unsafe { (shim_loader::shim().integer_node_set_value)(self.ptr, value) })
888 }
889}
890
891impl Drop for IntegerNode {
892 fn drop(&mut self) {
893 if !self.ptr.is_null() {
894 unsafe { (shim_loader::shim().integer_parameter_destroy)(self.ptr) }
895 }
896 }
897}
898
899pub struct FloatNode {
901 name: String,
902 ptr: *mut c_void,
903}
904
905impl FloatNode {
906 pub fn name(&self) -> &str {
908 &self.name
909 }
910
911 pub fn unit(&self) -> PylonResult<String> {
913 {
914 let mut out: *mut c_char = std::ptr::null_mut();
915 let err = unsafe { (shim_loader::shim().float_node_get_unit)(self.ptr, &mut out) };
916 shim_check_for!(float_node_get_unit, err)?;
917 ensure_non_null_for!(float_node_get_unit, "unit string", out)?;
918 Ok(unsafe { shim_loader::take_str(out) })
919 }
920 }
921 pub fn value(&self) -> PylonResult<f64> {
923 {
924 let mut out = 0f64;
925 let err = unsafe { (shim_loader::shim().float_node_get_value)(self.ptr, &mut out) };
926 shim_check(err)?;
927 Ok(out)
928 }
929 }
930 pub fn min(&self) -> PylonResult<f64> {
932 {
933 let mut out = 0f64;
934 let err = unsafe { (shim_loader::shim().float_node_get_min)(self.ptr, &mut out) };
935 shim_check(err)?;
936 Ok(out)
937 }
938 }
939 pub fn max(&self) -> PylonResult<f64> {
941 {
942 let mut out = 0f64;
943 let err = unsafe { (shim_loader::shim().float_node_get_max)(self.ptr, &mut out) };
944 shim_check(err)?;
945 Ok(out)
946 }
947 }
948 pub fn set_value(&mut self, value: f64) -> PylonResult<()> {
950 shim_check(unsafe { (shim_loader::shim().float_node_set_value)(self.ptr, value) })
951 }
952}
953
954impl Drop for FloatNode {
955 fn drop(&mut self) {
956 if !self.ptr.is_null() {
957 unsafe { (shim_loader::shim().float_parameter_destroy)(self.ptr) }
958 }
959 }
960}
961
962pub struct EnumNode {
964 name: String,
965 ptr: *mut c_void,
966}
967
968impl EnumNode {
969 pub fn name(&self) -> &str {
971 &self.name
972 }
973
974 pub fn value(&self) -> PylonResult<String> {
976 {
977 let mut out: *mut c_char = std::ptr::null_mut();
978 let err = unsafe { (shim_loader::shim().enum_node_get_value)(self.ptr, &mut out) };
979 shim_check_for!(enum_node_get_value, err)?;
980 ensure_non_null_for!(enum_node_get_value, "enum value string", out)?;
981 Ok(unsafe { shim_loader::take_str(out) })
982 }
983 }
984 pub fn settable_values(&self) -> PylonResult<Vec<String>> {
986 {
987 let mut arr: *mut *mut c_char = std::ptr::null_mut();
988 let mut count: usize = 0;
989 let err = unsafe {
990 (shim_loader::shim().enum_node_settable_values)(self.ptr, &mut arr, &mut count)
991 };
992 shim_check_for!(enum_node_settable_values, err)?;
993 if count > 0 {
994 ensure_non_null_for!(enum_node_settable_values, "settable values array", arr)?;
995 }
996 let mut result = Vec::with_capacity(count);
997 unsafe {
998 for i in 0..count {
999 let s = std::ffi::CStr::from_ptr(*arr.add(i))
1000 .to_string_lossy()
1001 .into_owned();
1002 result.push(s);
1003 }
1004 (shim_loader::shim().enum_node_free_settable_values)(arr, count);
1005 }
1006 Ok(result)
1007 }
1008 }
1009 pub fn set_value(&mut self, value: &str) -> PylonResult<()> {
1011 shim_check(unsafe {
1012 (shim_loader::shim().enum_node_set_value)(
1013 self.ptr,
1014 value.as_ptr() as *const c_char,
1015 value.len(),
1016 )
1017 })
1018 }
1019}
1020
1021impl Drop for EnumNode {
1022 fn drop(&mut self) {
1023 if !self.ptr.is_null() {
1024 unsafe { (shim_loader::shim().enum_parameter_destroy)(self.ptr) }
1025 }
1026 }
1027}
1028
1029pub struct CommandNode {
1031 name: String,
1032 ptr: *mut c_void,
1033}
1034
1035impl CommandNode {
1036 pub fn name(&self) -> &str {
1038 &self.name
1039 }
1040
1041 pub fn execute(&self, verify: bool) -> PylonResult<()> {
1043 shim_check(unsafe { (shim_loader::shim().command_node_execute)(self.ptr, verify as c_int) })
1044 }
1045}
1046
1047impl Drop for CommandNode {
1048 fn drop(&mut self) {
1049 if !self.ptr.is_null() {
1050 unsafe { (shim_loader::shim().command_parameter_destroy)(self.ptr) }
1051 }
1052 }
1053}
1054
1055pub struct GrabResult {
1061 ptr: *mut c_void,
1062}
1063
1064unsafe impl Send for GrabResult {}
1065
1066impl GrabResult {
1067 pub fn new() -> PylonResult<Self> {
1069 {
1070 let mut ptr: *mut c_void = std::ptr::null_mut();
1071 let err = unsafe { (shim_loader::shim().new_grab_result_ptr)(&mut ptr) };
1072 shim_check(err)?;
1073 Ok(GrabResult { ptr })
1074 }
1075 }
1076
1077 pub fn grab_succeeded(&self) -> PylonResult<bool> {
1079 {
1080 let mut out: c_int = 0;
1081 let err =
1082 unsafe { (shim_loader::shim().grab_result_grab_succeeded)(self.ptr, &mut out) };
1083 shim_check(err)?;
1084 Ok(out != 0)
1085 }
1086 }
1087 pub fn error_description(&self) -> PylonResult<String> {
1089 {
1090 let mut out: *mut c_char = std::ptr::null_mut();
1091 let err =
1092 unsafe { (shim_loader::shim().grab_result_error_description)(self.ptr, &mut out) };
1093 shim_check_for!(grab_result_error_description, err)?;
1094 ensure_non_null_for!(
1095 grab_result_error_description,
1096 "error description string",
1097 out
1098 )?;
1099 Ok(unsafe { shim_loader::take_str(out) })
1100 }
1101 }
1102 pub fn error_code(&self) -> PylonResult<u32> {
1104 {
1105 let mut out = 0u32;
1106 let err = unsafe { (shim_loader::shim().grab_result_error_code)(self.ptr, &mut out) };
1107 shim_check(err)?;
1108 Ok(out)
1109 }
1110 }
1111 pub fn width(&self) -> PylonResult<u32> {
1113 {
1114 let mut out = 0u32;
1115 shim_check(unsafe { (shim_loader::shim().grab_result_width)(self.ptr, &mut out) })?;
1116 Ok(out)
1117 }
1118 }
1119 pub fn height(&self) -> PylonResult<u32> {
1121 {
1122 let mut out = 0u32;
1123 shim_check(unsafe { (shim_loader::shim().grab_result_height)(self.ptr, &mut out) })?;
1124 Ok(out)
1125 }
1126 }
1127 pub fn offset_x(&self) -> PylonResult<u32> {
1129 {
1130 let mut out = 0u32;
1131 shim_check(unsafe { (shim_loader::shim().grab_result_offset_x)(self.ptr, &mut out) })?;
1132 Ok(out)
1133 }
1134 }
1135 pub fn offset_y(&self) -> PylonResult<u32> {
1137 {
1138 let mut out = 0u32;
1139 shim_check(unsafe { (shim_loader::shim().grab_result_offset_y)(self.ptr, &mut out) })?;
1140 Ok(out)
1141 }
1142 }
1143 pub fn padding_x(&self) -> PylonResult<u32> {
1145 {
1146 let mut out = 0u32;
1147 shim_check(unsafe { (shim_loader::shim().grab_result_padding_x)(self.ptr, &mut out) })?;
1148 Ok(out)
1149 }
1150 }
1151 pub fn padding_y(&self) -> PylonResult<u32> {
1153 {
1154 let mut out = 0u32;
1155 shim_check(unsafe { (shim_loader::shim().grab_result_padding_y)(self.ptr, &mut out) })?;
1156 Ok(out)
1157 }
1158 }
1159 pub fn buffer(&self) -> PylonResult<&[u8]> {
1161 {
1162 let mut buf: *const u8 = std::ptr::null();
1163 let mut len: usize = 0;
1164 let err =
1165 unsafe { (shim_loader::shim().grab_result_buffer)(self.ptr, &mut buf, &mut len) };
1166 shim_check_for!(grab_result_buffer, err)?;
1167 if len > 0 {
1168 ensure_non_null_for!(grab_result_buffer, "image buffer", buf)?;
1169 }
1170 Ok(unsafe { std::slice::from_raw_parts(buf, len) })
1171 }
1172 }
1173 pub fn payload_size(&self) -> PylonResult<u32> {
1175 {
1176 let mut out = 0u32;
1177 shim_check(unsafe {
1178 (shim_loader::shim().grab_result_payload_size)(self.ptr, &mut out)
1179 })?;
1180 Ok(out)
1181 }
1182 }
1183 pub fn buffer_size(&self) -> PylonResult<u32> {
1185 {
1186 let mut out = 0u32;
1187 shim_check(unsafe {
1188 (shim_loader::shim().grab_result_buffer_size)(self.ptr, &mut out)
1189 })?;
1190 Ok(out)
1191 }
1192 }
1193 pub fn block_id(&self) -> PylonResult<u64> {
1195 {
1196 let mut out = 0u64;
1197 shim_check(unsafe { (shim_loader::shim().grab_result_block_id)(self.ptr, &mut out) })?;
1198 Ok(out)
1199 }
1200 }
1201 pub fn time_stamp(&self) -> PylonResult<u64> {
1203 {
1204 let mut out = 0u64;
1205 shim_check(unsafe {
1206 (shim_loader::shim().grab_result_time_stamp)(self.ptr, &mut out)
1207 })?;
1208 Ok(out)
1209 }
1210 }
1211 pub fn stride(&self) -> PylonResult<usize> {
1213 {
1214 let mut out = 0usize;
1215 shim_check(unsafe { (shim_loader::shim().grab_result_stride)(self.ptr, &mut out) })?;
1216 Ok(out)
1217 }
1218 }
1219 pub fn image_size(&self) -> PylonResult<u32> {
1221 {
1222 let mut out = 0u32;
1223 shim_check(unsafe {
1224 (shim_loader::shim().grab_result_image_size)(self.ptr, &mut out)
1225 })?;
1226 Ok(out)
1227 }
1228 }
1229 pub fn chunk_data_node_map(&self) -> PylonResult<NodeMap<'_>> {
1231 {
1232 let mut ptr: *const c_void = std::ptr::null();
1233 let err = unsafe {
1234 (shim_loader::shim().grab_result_get_chunk_data_node_map)(self.ptr, &mut ptr)
1235 };
1236 shim_check_for!(grab_result_get_chunk_data_node_map, err)?;
1237 ensure_non_null(
1238 stringify!(grab_result_get_chunk_data_node_map),
1239 "chunk data node map pointer",
1240 ptr,
1241 )?;
1242 Ok(NodeMap {
1243 ptr,
1244 _marker: std::marker::PhantomData,
1245 })
1246 }
1247 }
1248}
1249
1250impl Drop for GrabResult {
1251 fn drop(&mut self) {
1252 if !self.ptr.is_null() {
1253 unsafe { (shim_loader::shim().grab_result_ptr_destroy)(self.ptr) }
1254 }
1255 }
1256}
1257
1258pub struct DeviceInfo {
1264 pub(crate) ptr: *mut c_void,
1265 runtime: runtime_impl::RuntimeLease,
1266}
1267
1268unsafe impl Send for DeviceInfo {}
1269
1270impl Clone for DeviceInfo {
1271 fn clone(&self) -> DeviceInfo {
1272 {
1273 let mut out: *mut c_void = std::ptr::null_mut();
1274 let err = unsafe { (shim_loader::shim().device_info_clone)(self.ptr, &mut out) };
1275 shim_loader::check_err(err)
1276 .map_err(PylonError::new)
1277 .expect("device_info_clone should not fail");
1278 DeviceInfo {
1279 ptr: out,
1280 runtime: self.runtime.clone(),
1281 }
1282 }
1283 }
1284}
1285
1286impl Drop for DeviceInfo {
1287 fn drop(&mut self) {
1288 if !self.ptr.is_null() {
1289 unsafe { (shim_loader::shim().device_info_destroy)(self.ptr) }
1290 }
1291 }
1292}
1293
1294impl DeviceInfo {
1295 pub fn model_name(&self) -> PylonResult<String> {
1297 {
1298 let mut out: *mut c_char = std::ptr::null_mut();
1299 let err =
1300 unsafe { (shim_loader::shim().device_info_get_model_name)(self.ptr, &mut out) };
1301 shim_check_for!(device_info_get_model_name, err)?;
1302 ensure_non_null_for!(device_info_get_model_name, "model name string", out)?;
1303 Ok(unsafe { shim_loader::take_str(out) })
1304 }
1305 }
1306}
1307
1308pub trait HasProperties {
1310 fn property_names(&self) -> PylonResult<Vec<String>>;
1312
1313 fn property_value(&self, name: &str) -> PylonResult<String>;
1315}
1316
1317impl HasProperties for DeviceInfo {
1318 fn property_names(&self) -> PylonResult<Vec<String>> {
1319 {
1320 let mut arr: *mut *mut c_char = std::ptr::null_mut();
1321 let mut count: usize = 0;
1322 let err = unsafe {
1323 (shim_loader::shim().device_info_get_property_names)(self.ptr, &mut arr, &mut count)
1324 };
1325 shim_check_for!(device_info_get_property_names, err)?;
1326 if count > 0 {
1327 ensure_non_null_for!(device_info_get_property_names, "property names array", arr)?;
1328 }
1329 let mut result = Vec::with_capacity(count);
1330 unsafe {
1331 for i in 0..count {
1332 let s = std::ffi::CStr::from_ptr(*arr.add(i))
1333 .to_string_lossy()
1334 .into_owned();
1335 result.push(s);
1336 }
1337 (shim_loader::shim().device_info_free_property_names)(arr, count);
1338 }
1339 Ok(result)
1340 }
1341 }
1342
1343 fn property_value(&self, name: &str) -> PylonResult<String> {
1344 {
1345 let mut out: *mut c_char = std::ptr::null_mut();
1346 let err = unsafe {
1347 (shim_loader::shim().device_info_get_property_value)(
1348 self.ptr,
1349 name.as_ptr() as *const c_char,
1350 name.len(),
1351 &mut out,
1352 )
1353 };
1354 shim_check_for!(device_info_get_property_value, err)?;
1355 ensure_non_null_for!(device_info_get_property_value, "property value string", out)?;
1356 Ok(unsafe { shim_loader::take_str(out) })
1357 }
1358 }
1359}
1360
1361#[cfg(all(target_os = "windows", feature = "stream"))]
1366pub struct WaitObject(pub(crate) *mut c_void);
1367
1368#[cfg(all(target_os = "windows", feature = "stream"))]
1369unsafe impl Send for WaitObject {}
1370
1371#[cfg(all(target_os = "windows", feature = "stream"))]
1372impl Drop for WaitObject {
1373 fn drop(&mut self) {
1374 if !self.0.is_null() {
1375 unsafe { (shim_loader::shim().wait_object_destroy)(self.0) }
1376 }
1377 }
1378}
1379
1380#[cfg(all(target_os = "windows", feature = "stream"))]
1381impl WaitObject {
1382 pub fn wait(&self, timeout: u64) -> PylonResult<bool> {
1383 {
1384 let mut out: c_int = 0;
1385 let err = unsafe { (shim_loader::shim().wait_object_wait)(self.0, timeout, &mut out) };
1386 shim_check(err)?;
1387 Ok(out != 0)
1388 }
1389 }
1390}
1391
1392fn path_to_string<P: AsRef<std::path::Path>>(path: P) -> PylonResult<String> {
1397 match path.as_ref().to_str() {
1398 Some(filename) => Ok(filename.into()),
1399 None => Err(PylonError::new("Cannot convert path to UTF-8".to_string())),
1400 }
1401}