├── .gitattributes ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── av-foundation ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples │ └── video_capture.rs └── src │ ├── capture_device.rs │ ├── capture_input.rs │ ├── capture_output_base.rs │ ├── capture_session.rs │ ├── capture_session_preset.rs │ ├── capture_video_data_output.rs │ ├── lib.rs │ ├── media_format.rs │ └── video_settings.rs ├── core-audio-types ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── base_types.rs │ ├── lib.rs │ └── session_types.rs ├── core-graphics ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples │ └── display_stream.rs └── src │ ├── affine_transform.rs │ ├── base.rs │ ├── bitmap_context.rs │ ├── color.rs │ ├── color_conversion_info.rs │ ├── color_space.rs │ ├── context.rs │ ├── data_provider.rs │ ├── direct_display.rs │ ├── direct_display_metal.rs │ ├── display.rs │ ├── display_configuration.rs │ ├── display_fade.rs │ ├── display_stream.rs │ ├── error.rs │ ├── event.rs │ ├── event_source.rs │ ├── event_types.rs │ ├── font.rs │ ├── function.rs │ ├── geometry.rs │ ├── gradient.rs │ ├── image.rs │ ├── layer.rs │ ├── lib.rs │ ├── path.rs │ ├── pattern.rs │ ├── remote_operation.rs │ ├── shading.rs │ ├── window.rs │ └── window_level.rs ├── core-media ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── attachment.rs │ ├── audio_clock.rs │ ├── audio_device_clock.rs │ ├── base.rs │ ├── block_buffer.rs │ ├── buffer_queue.rs │ ├── format_description.rs │ ├── format_description_bridge.rs │ ├── lib.rs │ ├── sample_buffer.rs │ ├── sample_queue.rs │ ├── sync.rs │ ├── time.rs │ └── time_range.rs ├── core-video ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── base.rs │ ├── buffer.rs │ ├── display_link.rs │ ├── host_time.rs │ ├── image_buffer.rs │ ├── lib.rs │ ├── metal_texture.rs │ ├── metal_texture_cache.rs │ ├── opengl_buffer.rs │ ├── opengl_buffer_pool.rs │ ├── opengl_es_texture.rs │ ├── opengl_es_texture_cache.rs │ ├── opengl_texture.rs │ ├── opengl_texture_cache.rs │ ├── pixel_buffer.rs │ ├── pixel_buffer_io_surface.rs │ ├── pixel_buffer_pool.rs │ ├── pixel_format_description.rs │ └── return.rs ├── rustfmt.toml └── screen-capture-kit ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples └── screen_capture.rs └── src ├── encode.rs ├── error.rs ├── lib.rs ├── shareable_content.rs └── stream.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["core-audio-types", "core-graphics", "core-video", "core-media", "av-foundation", "screen-capture-kit"] 3 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Zhou Wei 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # apple-media 2 | 3 | [![License](https://img.shields.io/badge/License-Apache%202-blue.svg)](LICENSE-APACHE) 4 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE-MIT) 5 | 6 | Rust bindings to Apple media frameworks 7 | 8 | ## core-audio-types 9 | 10 | [![Version](https://img.shields.io/crates/v/core-audio-types)](https://crates.io/crates/core-audio-types) 11 | [![Documentation](https://docs.rs/core-audio-types/badge.svg)](https://docs.rs/core-audio-types) 12 | 13 | ## core-video 14 | 15 | [![Version](https://img.shields.io/crates/v/core-video)](https://crates.io/crates/core-video) 16 | [![Documentation](https://docs.rs/core-video/badge.svg)](https://docs.rs/core-video) 17 | 18 | ## core-media 19 | 20 | [![Version](https://img.shields.io/crates/v/core-media)](https://crates.io/crates/core-media) 21 | [![Documentation](https://docs.rs/core-media/badge.svg)](https://docs.rs/core-media) 22 | 23 | ## core-graphics2 24 | 25 | [![Version](https://img.shields.io/crates/v/core-graphics2)](https://crates.io/crates/core-graphics2) 26 | [![Documentation](https://docs.rs/core-graphics2/badge.svg)](https://docs.rs/core-graphics2) 27 | 28 | ## av-foundation 29 | 30 | [![Version](https://img.shields.io/crates/v/av-foundation)](https://crates.io/crates/av-foundation) 31 | [![Documentation](https://docs.rs/av-foundation/badge.svg)](https://docs.rs/av-foundation) 32 | 33 | ## screen-capture-kit 34 | 35 | [![Version](https://img.shields.io/crates/v/screen-capture-kit)](https://crates.io/crates/screen-capture-kit) 36 | [![Documentation](https://docs.rs/screen-capture-kit/badge.svg)](https://docs.rs/screen-capture-kit) 37 | 38 | ## Crates 39 | 40 | - [x] core-audio-types 41 | - [ ] core-audio 42 | - [x] core-video 43 | - [x] core-media 44 | - [x] core-graphics2 45 | - [ ] core-text2 46 | - [x] av-foundation 47 | - [x] screen-capture-kit 48 | -------------------------------------------------------------------------------- /av-foundation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "av-foundation" 3 | description = "Bindings to AVFoundation framework" 4 | homepage = "https://github.com/rust-media/apple-media-rs" 5 | repository = "https://github.com/rust-media/apple-media-rs" 6 | documentation = "https://docs.rs/av-foundation" 7 | version = "0.5.2" 8 | authors = ["Zhou Wei "] 9 | license = "MIT OR Apache-2.0" 10 | categories = ["multimedia::video", "os::macos-apis"] 11 | keywords = ["avfoundation", "camera", "capture"] 12 | edition = "2018" 13 | 14 | [dependencies] 15 | block2 = "0.5" 16 | core-foundation = { version = "0.10", default-features = false } 17 | core-media = { version = "0.5", path = "../core-media", default-features = false, features = ["objc"] } 18 | core-graphics2 = { version = "0.4", path = "../core-graphics", default-features = false, features = ["objc"] } 19 | core-video = { version = "0.4", path = "../core-video", default-features = false, features = ["objc"] } 20 | dispatch2 = "0.1" 21 | libc = "0.2" 22 | objc2 = "0.5" 23 | objc2-foundation = { version = "0.2", features = ["NSArray", "NSDictionary", "NSEnumerator", "NSError", "NSRange", "NSString", "NSValue"] } 24 | 25 | [dev-dependencies] 26 | os-ver = "0.2" 27 | 28 | [features] 29 | default = ["capture", "link"] 30 | audio = [] 31 | capture = [] 32 | editing = [] 33 | link = ["core-foundation/link", "core-media/link", "core-video/link"] 34 | playback = [] 35 | 36 | [[example]] 37 | name = "video_capture" 38 | 39 | [package.metadata.docs.rs] 40 | no-default-features = true 41 | features = ["capture"] 42 | default-target = "x86_64-apple-darwin" 43 | targets = [ 44 | "aarch64-apple-darwin", 45 | "aarch64-apple-ios", 46 | "x86_64-apple-darwin", 47 | "x86_64-apple-ios", 48 | ] 49 | -------------------------------------------------------------------------------- /av-foundation/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /av-foundation/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /av-foundation/README.md: -------------------------------------------------------------------------------- 1 | # av-foundation 2 | 3 | [![Version](https://img.shields.io/crates/v/av-foundation)](https://crates.io/crates/av-foundation) 4 | [![Documentation](https://docs.rs/av-foundation/badge.svg)](https://docs.rs/av-foundation) 5 | [![License](https://img.shields.io/badge/License-Apache%202-blue.svg)](LICENSE-APACHE) 6 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE-MIT) 7 | 8 | Rust bindings to AVFoundation framework 9 | -------------------------------------------------------------------------------- /av-foundation/examples/video_capture.rs: -------------------------------------------------------------------------------- 1 | use av_foundation::{ 2 | capture_device::{ 3 | AVCaptureDevice, AVCaptureDeviceDiscoverySession, AVCaptureDevicePositionUnspecified, AVCaptureDeviceTypeBuiltInWideAngleCamera, 4 | AVCaptureDeviceTypeExternal, AVCaptureDeviceTypeExternalUnknown, 5 | }, 6 | capture_input::AVCaptureDeviceInput, 7 | capture_output_base::AVCaptureOutput, 8 | capture_session::{AVCaptureConnection, AVCaptureSession}, 9 | capture_video_data_output::{AVCaptureVideoDataOutput, AVCaptureVideoDataOutputSampleBufferDelegate}, 10 | media_format::AVMediaTypeVideo, 11 | }; 12 | use core_foundation::base::TCFType; 13 | use core_media::sample_buffer::{CMSampleBuffer, CMSampleBufferRef}; 14 | use core_video::pixel_buffer::CVPixelBuffer; 15 | use dispatch2::Queue; 16 | use objc2::{ 17 | declare_class, extern_methods, msg_send_id, mutability, 18 | rc::{Allocated, Id}, 19 | runtime::ProtocolObject, 20 | ClassType, DeclaredClass, 21 | }; 22 | use objc2_foundation::{NSMutableArray, NSObject, NSObjectProtocol}; 23 | use os_ver::if_greater_than; 24 | 25 | pub struct DelegateIvars {} 26 | 27 | declare_class!( 28 | struct Delegate; 29 | 30 | unsafe impl ClassType for Delegate { 31 | type Super = NSObject; 32 | type Mutability = mutability::Mutable; 33 | const NAME: &'static str = "OutputSampleBufferDelegate"; 34 | } 35 | 36 | impl DeclaredClass for Delegate { 37 | type Ivars = DelegateIvars; 38 | } 39 | 40 | unsafe impl NSObjectProtocol for Delegate {} 41 | 42 | unsafe impl AVCaptureVideoDataOutputSampleBufferDelegate for Delegate { 43 | #[method(captureOutput:didOutputSampleBuffer:fromConnection:)] 44 | unsafe fn capture_output_did_output_sample_buffer( 45 | &self, 46 | _capture_output: &AVCaptureOutput, 47 | sample_buffer: CMSampleBufferRef, 48 | _connection: &AVCaptureConnection, 49 | ) { 50 | let sample_buffer = CMSampleBuffer::wrap_under_get_rule(sample_buffer); 51 | if let Some(image_buffer) = sample_buffer.get_image_buffer() { 52 | if let Some(pixel_buffer) = image_buffer.downcast::() { 53 | println!("pixel buffer: {:?}", pixel_buffer); 54 | } 55 | } 56 | } 57 | } 58 | 59 | unsafe impl Delegate { 60 | #[method_id(init)] 61 | fn init(this: Allocated) -> Option> { 62 | let this = this.set_ivars(DelegateIvars {}); 63 | unsafe { msg_send_id![super(this), init] } 64 | } 65 | } 66 | ); 67 | 68 | extern_methods!( 69 | unsafe impl Delegate { 70 | #[method_id(new)] 71 | pub fn new() -> Id; 72 | } 73 | ); 74 | 75 | fn main() { 76 | let devices = unsafe { 77 | if cfg!(target_os = "macos") { 78 | if_greater_than! {(10, 15) => { 79 | let mut device_types = NSMutableArray::new(); 80 | 81 | device_types.addObject(AVCaptureDeviceTypeBuiltInWideAngleCamera); 82 | if_greater_than!{(14) => { 83 | device_types.addObject(AVCaptureDeviceTypeExternal); 84 | } else { 85 | device_types.addObject(AVCaptureDeviceTypeExternalUnknown); 86 | }}; 87 | device_types.addObject(AVCaptureDeviceTypeExternalUnknown); 88 | 89 | AVCaptureDeviceDiscoverySession::discovery_session_with_device_types( 90 | &device_types, 91 | AVMediaTypeVideo, 92 | AVCaptureDevicePositionUnspecified, 93 | ).devices() 94 | } else { 95 | AVCaptureDevice::devices_with_media_type(AVMediaTypeVideo) 96 | }} 97 | } else if cfg!(target_os = "ios") { 98 | if_greater_than! {(10) => { 99 | let mut device_types = NSMutableArray::new(); 100 | 101 | device_types.addObject(AVCaptureDeviceTypeBuiltInWideAngleCamera); 102 | 103 | AVCaptureDeviceDiscoverySession::discovery_session_with_device_types( 104 | &device_types, 105 | AVMediaTypeVideo, 106 | AVCaptureDevicePositionUnspecified, 107 | ).devices() 108 | } else { 109 | AVCaptureDevice::devices_with_media_type(AVMediaTypeVideo) 110 | }} 111 | } else { 112 | println!("Unsupported platform"); 113 | return; 114 | } 115 | }; 116 | 117 | for device in devices.iter() { 118 | println!("name: {:?}", device.localized_name()); 119 | println!("id: {:?}", device.unique_id()); 120 | println!("model: {:?}", device.model_id()); 121 | println!("manufacturer: {:?}", device.manufacturer()); 122 | println!("device type: {:?}", device.device_type()); 123 | println!("transport type: {:?}", String::from_utf8_lossy(&device.transport_type().to_be_bytes())); 124 | println!("position: {:?}", device.position()); 125 | } 126 | 127 | let device = match devices.first() { 128 | Some(device) => device, 129 | None => { 130 | println!("No video capture device found"); 131 | return; 132 | } 133 | }; 134 | 135 | let session = AVCaptureSession::new(); 136 | let input = AVCaptureDeviceInput::from_device(device).unwrap(); 137 | let output = AVCaptureVideoDataOutput::new(); 138 | let delegate = Delegate::new(); 139 | let delegate = ProtocolObject::from_ref(&*delegate); 140 | let queue = Queue::new("com.video_capture.queue", dispatch2::QueueAttribute::Serial); 141 | 142 | output.set_sample_buffer_delegate(delegate, &queue); 143 | output.set_always_discards_late_video_frames(true); 144 | 145 | session.begin_configuration(); 146 | session.add_input(&input); 147 | session.add_output(&output); 148 | session.commit_configuration(); 149 | 150 | session.start_running(); 151 | std::thread::sleep(std::time::Duration::from_secs(10)); 152 | session.stop_running(); 153 | } 154 | -------------------------------------------------------------------------------- /av-foundation/src/capture_input.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::base::TCFType; 2 | use core_media::format_description::{CMFormatDescription, CMFormatDescriptionRef}; 3 | use objc2::{ 4 | extern_class, msg_send, msg_send_id, 5 | mutability::InteriorMutable, 6 | rc::{Allocated, Id}, 7 | ClassType, 8 | }; 9 | use objc2_foundation::{NSArray, NSError, NSObject, NSObjectProtocol, NSString}; 10 | 11 | use crate::{capture_device::AVCaptureDevice, media_format::AVMediaType}; 12 | 13 | extern_class!( 14 | #[derive(Debug, PartialEq, Eq, Hash)] 15 | pub struct AVCaptureInput; 16 | 17 | unsafe impl ClassType for AVCaptureInput { 18 | type Super = NSObject; 19 | type Mutability = InteriorMutable; 20 | } 21 | ); 22 | 23 | unsafe impl NSObjectProtocol for AVCaptureInput {} 24 | 25 | impl AVCaptureInput { 26 | pub fn ports(&self) -> Id> { 27 | unsafe { msg_send_id![self, ports] } 28 | } 29 | } 30 | 31 | extern "C" { 32 | pub static AVCaptureInputPortFormatDescriptionDidChangeNotification: &'static NSString; 33 | } 34 | 35 | extern_class!( 36 | #[derive(Debug, PartialEq, Eq, Hash)] 37 | pub struct AVCaptureInputPort; 38 | 39 | unsafe impl ClassType for AVCaptureInputPort { 40 | type Super = NSObject; 41 | type Mutability = InteriorMutable; 42 | } 43 | ); 44 | 45 | unsafe impl NSObjectProtocol for AVCaptureInputPort {} 46 | 47 | impl AVCaptureInputPort { 48 | pub fn media_type(&self) -> Id { 49 | unsafe { msg_send_id![self, mediaType] } 50 | } 51 | 52 | pub fn format_description(&self) -> Option { 53 | unsafe { 54 | let format_description: CMFormatDescriptionRef = msg_send![self, formatDescription]; 55 | if format_description.is_null() { 56 | None 57 | } else { 58 | Some(CMFormatDescription::wrap_under_get_rule(format_description)) 59 | } 60 | } 61 | } 62 | 63 | pub fn is_enabled(&self) -> bool { 64 | unsafe { msg_send![self, isEnabled] } 65 | } 66 | 67 | pub fn set_enabled(&self, enabled: bool) { 68 | unsafe { msg_send![self, setEnabled: enabled] } 69 | } 70 | } 71 | 72 | extern_class!( 73 | #[derive(Debug, PartialEq, Eq, Hash)] 74 | pub struct AVCaptureDeviceInput; 75 | 76 | unsafe impl ClassType for AVCaptureDeviceInput { 77 | type Super = AVCaptureInput; 78 | type Mutability = InteriorMutable; 79 | } 80 | ); 81 | 82 | unsafe impl NSObjectProtocol for AVCaptureDeviceInput {} 83 | 84 | impl AVCaptureDeviceInput { 85 | pub fn from_device(device: &AVCaptureDevice) -> Result, Id> { 86 | unsafe { msg_send_id![Self::class(), deviceInputWithDevice: device, error: _] } 87 | } 88 | 89 | pub fn init_with_device(this: Allocated, device: &AVCaptureDevice) -> Result, Id> { 90 | unsafe { msg_send_id![this, initWithDevice: device, error: _] } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /av-foundation/src/capture_output_base.rs: -------------------------------------------------------------------------------- 1 | use objc2::{extern_class, msg_send_id, mutability::InteriorMutable, rc::Id, ClassType}; 2 | use objc2_foundation::{NSArray, NSInteger, NSObject, NSObjectProtocol, NSString}; 3 | 4 | use crate::capture_session::AVCaptureConnection; 5 | 6 | extern_class!( 7 | #[derive(Debug, PartialEq, Eq, Hash)] 8 | pub struct AVCaptureOutput; 9 | 10 | unsafe impl ClassType for AVCaptureOutput { 11 | type Super = NSObject; 12 | type Mutability = InteriorMutable; 13 | } 14 | ); 15 | 16 | unsafe impl NSObjectProtocol for AVCaptureOutput {} 17 | 18 | impl AVCaptureOutput { 19 | pub fn connections(&self) -> Id> { 20 | unsafe { msg_send_id![self, connections] } 21 | } 22 | 23 | pub fn connection_with_media_type(&self, media_type: &NSString) -> Option> { 24 | unsafe { msg_send_id![self, connectionWithMediaType: media_type] } 25 | } 26 | } 27 | 28 | pub type AVCaptureOutputDataDroppedReason = NSInteger; 29 | 30 | pub const AVCaptureOutputDataDroppedReasonNone: AVCaptureOutputDataDroppedReason = 0; 31 | pub const AVCaptureOutputDataDroppedReasonLateData: AVCaptureOutputDataDroppedReason = 1; 32 | pub const AVCaptureOutputDataDroppedReasonOutdatedData: AVCaptureOutputDataDroppedReason = 2; 33 | pub const AVCaptureOutputDataDroppedReasonDiscontinuity: AVCaptureOutputDataDroppedReason = 3; 34 | -------------------------------------------------------------------------------- /av-foundation/src/capture_session.rs: -------------------------------------------------------------------------------- 1 | use objc2::{extern_class, msg_send, msg_send_id, mutability::InteriorMutable, rc::Id, ClassType}; 2 | use objc2_foundation::{NSArray, NSInteger, NSObject, NSObjectProtocol, NSString}; 3 | 4 | use crate::{capture_input::AVCaptureInput, capture_output_base::AVCaptureOutput, capture_session_preset::AVCaptureSessionPreset}; 5 | 6 | extern "C" { 7 | pub static AVCaptureSessionRuntimeErrorNotification: &'static NSString; 8 | pub static AVCaptureSessionErrorKey: &'static NSString; 9 | pub static AVCaptureSessionDidStartRunningNotification: &'static NSString; 10 | pub static AVCaptureSessionDidStopRunningNotification: &'static NSString; 11 | pub static AVCaptureSessionWasInterruptedNotification: &'static NSString; 12 | pub static AVCaptureSessionInterruptionReasonKey: &'static NSString; 13 | pub static AVCaptureSessionInterruptionSystemPressureStateKey: &'static NSString; 14 | pub static AVCaptureSessionInterruptionEndedNotification: &'static NSString; 15 | } 16 | 17 | pub type AVCaptureSessionInterruptionReason = NSInteger; 18 | 19 | pub const AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground: AVCaptureSessionInterruptionReason = 1; 20 | pub const AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient: AVCaptureSessionInterruptionReason = 2; 21 | pub const AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient: AVCaptureSessionInterruptionReason = 3; 22 | pub const AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps: AVCaptureSessionInterruptionReason = 4; 23 | pub const AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure: AVCaptureSessionInterruptionReason = 5; 24 | 25 | pub type AVCaptureVideoOrientation = NSInteger; 26 | 27 | pub const AVCaptureVideoOrientationPortrait: AVCaptureVideoOrientation = 1; 28 | pub const AVCaptureVideoOrientationPortraitUpsideDown: AVCaptureVideoOrientation = 2; 29 | pub const AVCaptureVideoOrientationLandscapeRight: AVCaptureVideoOrientation = 3; 30 | pub const AVCaptureVideoOrientationLandscapeLeft: AVCaptureVideoOrientation = 4; 31 | 32 | extern_class!( 33 | #[derive(Debug, PartialEq, Eq, Hash)] 34 | pub struct AVCaptureSession; 35 | 36 | unsafe impl ClassType for AVCaptureSession { 37 | type Super = NSObject; 38 | type Mutability = InteriorMutable; 39 | } 40 | ); 41 | 42 | unsafe impl NSObjectProtocol for AVCaptureSession {} 43 | 44 | impl AVCaptureSession { 45 | pub fn new() -> Id { 46 | unsafe { msg_send_id![AVCaptureSession::class(), new] } 47 | } 48 | pub fn can_set_session_preset(&self, preset: &AVCaptureSessionPreset) -> bool { 49 | unsafe { msg_send![self, canSetSessionPreset: preset] } 50 | } 51 | 52 | pub fn get_session_preset(&self) -> Id { 53 | unsafe { msg_send_id![self, sessionPreset] } 54 | } 55 | 56 | pub fn set_session_preset(&self, preset: &AVCaptureSessionPreset) { 57 | unsafe { msg_send![self, setSessionPreset: preset] } 58 | } 59 | 60 | pub fn inputs(&self) -> Id> { 61 | unsafe { msg_send_id![self, inputs] } 62 | } 63 | 64 | pub fn can_add_input(&self, input: &AVCaptureInput) -> bool { 65 | unsafe { msg_send![self, canAddInput: input] } 66 | } 67 | 68 | pub fn add_input(&self, input: &AVCaptureInput) { 69 | unsafe { msg_send![self, addInput: input] } 70 | } 71 | 72 | pub fn remove_input(&self, input: &AVCaptureInput) { 73 | unsafe { msg_send![self, removeInput: input] } 74 | } 75 | 76 | pub fn outputs(&self) -> Id> { 77 | unsafe { msg_send_id![self, outputs] } 78 | } 79 | 80 | pub fn can_add_output(&self, output: &AVCaptureOutput) -> bool { 81 | unsafe { msg_send![self, canAddOutput: output] } 82 | } 83 | 84 | pub fn add_output(&self, output: &AVCaptureOutput) { 85 | unsafe { msg_send![self, addOutput: output] } 86 | } 87 | 88 | pub fn remove_output(&self, output: &AVCaptureOutput) { 89 | unsafe { msg_send![self, removeOutput: output] } 90 | } 91 | 92 | pub fn add_input_with_no_connections(&self, input: &AVCaptureInput) { 93 | unsafe { msg_send![self, addInputWithNoConnections: input] } 94 | } 95 | 96 | pub fn add_output_with_no_connections(&self, output: &AVCaptureOutput) { 97 | unsafe { msg_send![self, addOutputWithNoConnections: output] } 98 | } 99 | 100 | pub fn connections(&self) -> Id> { 101 | unsafe { msg_send_id![self, connections] } 102 | } 103 | 104 | pub fn can_add_connection(&self, connection: &AVCaptureConnection) -> bool { 105 | unsafe { msg_send![self, canAddConnection: connection] } 106 | } 107 | 108 | pub fn add_connection(&self, connection: &AVCaptureConnection) { 109 | unsafe { msg_send![self, addConnection: connection] } 110 | } 111 | 112 | pub fn remove_connection(&self, connection: &AVCaptureConnection) { 113 | unsafe { msg_send![self, removeConnection: connection] } 114 | } 115 | 116 | pub fn is_running(&self) -> bool { 117 | unsafe { msg_send![self, isRunning] } 118 | } 119 | 120 | #[cfg(target_os = "ios")] 121 | pub fn is_interrupted(&self) -> bool { 122 | unsafe { msg_send![self, isInterrupted] } 123 | } 124 | 125 | #[cfg(target_os = "ios")] 126 | pub fn is_multitasking_camera_access_supported(&self) -> bool { 127 | unsafe { msg_send![self, isMultitaskingCameraAccessSupported] } 128 | } 129 | 130 | #[cfg(target_os = "ios")] 131 | pub fn get_uses_application_audio_session(&self) -> bool { 132 | unsafe { msg_send![self, usesApplicationAudioSession] } 133 | } 134 | 135 | #[cfg(target_os = "ios")] 136 | pub fn set_uses_application_audio_session(&self, uses_application_audio_session: bool) { 137 | unsafe { msg_send![self, setUsesApplicationAudioSession: uses_application_audio_session] } 138 | } 139 | 140 | pub fn begin_configuration(&self) { 141 | unsafe { msg_send![self, beginConfiguration] } 142 | } 143 | 144 | pub fn commit_configuration(&self) { 145 | unsafe { msg_send![self, commitConfiguration] } 146 | } 147 | 148 | pub fn start_running(&self) { 149 | unsafe { msg_send![self, startRunning] } 150 | } 151 | 152 | pub fn stop_running(&self) { 153 | unsafe { msg_send![self, stopRunning] } 154 | } 155 | } 156 | 157 | extern_class!( 158 | #[derive(Debug, PartialEq, Eq, Hash)] 159 | pub struct AVCaptureConnection; 160 | 161 | unsafe impl ClassType for AVCaptureConnection { 162 | type Super = NSObject; 163 | type Mutability = InteriorMutable; 164 | } 165 | ); 166 | 167 | unsafe impl NSObjectProtocol for AVCaptureConnection {} 168 | -------------------------------------------------------------------------------- /av-foundation/src/capture_session_preset.rs: -------------------------------------------------------------------------------- 1 | use objc2_foundation::NSString; 2 | 3 | pub type AVCaptureSessionPreset = NSString; 4 | 5 | extern "C" { 6 | pub static AVCaptureSessionPresetPhoto: &'static AVCaptureSessionPreset; 7 | pub static AVCaptureSessionPresetHigh: &'static AVCaptureSessionPreset; 8 | pub static AVCaptureSessionPresetMedium: &'static AVCaptureSessionPreset; 9 | pub static AVCaptureSessionPresetLow: &'static AVCaptureSessionPreset; 10 | #[cfg(target_os = "macos")] 11 | pub static AVCaptureSessionPreset320x240: &'static AVCaptureSessionPreset; 12 | pub static AVCaptureSessionPreset352x288: &'static AVCaptureSessionPreset; 13 | pub static AVCaptureSessionPreset640x480: &'static AVCaptureSessionPreset; 14 | #[cfg(target_os = "macos")] 15 | pub static AVCaptureSessionPreset960x540: &'static AVCaptureSessionPreset; 16 | pub static AVCaptureSessionPreset1280x720: &'static AVCaptureSessionPreset; 17 | pub static AVCaptureSessionPreset1920x1080: &'static AVCaptureSessionPreset; 18 | pub static AVCaptureSessionPreset3840x2160: &'static AVCaptureSessionPreset; 19 | pub static AVCaptureSessionPresetiFrame960x540: &'static AVCaptureSessionPreset; 20 | pub static AVCaptureSessionPresetiFrame1280x720: &'static AVCaptureSessionPreset; 21 | #[cfg(target_os = "ios")] 22 | pub static AVCaptureSessionPresetInputPriority: &'static AVCaptureSessionPreset; 23 | } 24 | -------------------------------------------------------------------------------- /av-foundation/src/capture_video_data_output.rs: -------------------------------------------------------------------------------- 1 | use core_media::sample_buffer::CMSampleBufferRef; 2 | use dispatch2::Queue; 3 | use objc2::{extern_class, msg_send, msg_send_id, mutability::InteriorMutable, rc::Id, runtime::ProtocolObject, ClassType, ProtocolType}; 4 | use objc2_foundation::{NSArray, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSString}; 5 | 6 | use crate::{capture_output_base::AVCaptureOutput, capture_session::AVCaptureConnection}; 7 | 8 | extern_class!( 9 | #[derive(Debug, PartialEq, Eq, Hash)] 10 | pub struct AVCaptureVideoDataOutput; 11 | 12 | unsafe impl ClassType for AVCaptureVideoDataOutput { 13 | type Super = AVCaptureOutput; 14 | type Mutability = InteriorMutable; 15 | } 16 | ); 17 | 18 | unsafe impl NSObjectProtocol for AVCaptureVideoDataOutput {} 19 | 20 | impl AVCaptureVideoDataOutput { 21 | pub fn new() -> Id { 22 | unsafe { msg_send_id![AVCaptureVideoDataOutput::class(), new] } 23 | } 24 | 25 | pub fn get_sample_buffer_delegate(&self) -> Option>> { 26 | unsafe { msg_send_id![self, sampleBufferDelegate] } 27 | } 28 | 29 | pub fn set_sample_buffer_delegate(&self, delegate: &ProtocolObject, queue: &Queue) { 30 | unsafe { msg_send![self, setSampleBufferDelegate: delegate queue: queue.as_raw() as *const NSObject] } 31 | } 32 | 33 | pub fn get_video_settings(&self) -> Option>> { 34 | unsafe { msg_send_id![self, videoSettings] } 35 | } 36 | 37 | pub fn set_video_settings(&self, video_settings: &NSDictionary) { 38 | unsafe { msg_send![self, setVideoSettings: video_settings] } 39 | } 40 | 41 | pub fn get_available_video_cv_pixel_format_types(&self) -> Id> { 42 | unsafe { msg_send_id![self, availableVideoCVPixelFormatTypes] } 43 | } 44 | 45 | pub fn get_available_video_data_format_types(&self) -> Id> { 46 | unsafe { msg_send_id![self, availableVideoDataFormatTypes] } 47 | } 48 | 49 | pub fn get_always_discards_late_video_frames(&self) -> bool { 50 | unsafe { msg_send![self, alwaysDiscardsLateVideoFrames] } 51 | } 52 | 53 | pub fn set_always_discards_late_video_frames(&self, always_discards_late_video_frames: bool) { 54 | unsafe { msg_send![self, setAlwaysDiscardsLateVideoFrames: always_discards_late_video_frames] } 55 | } 56 | } 57 | 58 | extern_protocol!( 59 | pub unsafe trait AVCaptureVideoDataOutputSampleBufferDelegate: NSObjectProtocol { 60 | #[method(captureOutput:didOutputSampleBuffer:fromConnection:)] 61 | #[optional] 62 | unsafe fn capture_output_did_output_sample_buffer( 63 | &self, 64 | capture_output: &AVCaptureOutput, 65 | sample_buffer: CMSampleBufferRef, 66 | connection: &AVCaptureConnection, 67 | ); 68 | #[method(captureOutput:didDropSampleBuffer:fromConnection:)] 69 | #[optional] 70 | unsafe fn capture_output_did_drop_sample_buffer( 71 | &self, 72 | capture_output: &AVCaptureOutput, 73 | sample_buffer: CMSampleBufferRef, 74 | connection: &AVCaptureConnection, 75 | ); 76 | } 77 | 78 | unsafe impl ProtocolType for dyn AVCaptureVideoDataOutputSampleBufferDelegate {} 79 | ); 80 | -------------------------------------------------------------------------------- /av-foundation/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, improper_ctypes)] 2 | 3 | extern crate block2; 4 | extern crate core_foundation; 5 | extern crate core_graphics2 as core_graphics; 6 | extern crate core_media; 7 | extern crate core_video; 8 | extern crate dispatch2; 9 | extern crate libc; 10 | #[macro_use] 11 | extern crate objc2; 12 | extern crate objc2_foundation; 13 | 14 | #[cfg(any(target_os = "macos", target_os = "ios"))] 15 | #[link(name = "AVFoundation", kind = "framework")] 16 | extern "C" {} 17 | 18 | #[cfg(feature = "capture")] 19 | pub mod capture_device; 20 | #[cfg(feature = "capture")] 21 | pub mod capture_input; 22 | #[cfg(feature = "capture")] 23 | pub mod capture_output_base; 24 | #[cfg(feature = "capture")] 25 | pub mod capture_session; 26 | #[cfg(feature = "capture")] 27 | pub mod capture_session_preset; 28 | #[cfg(feature = "capture")] 29 | pub mod capture_video_data_output; 30 | pub mod media_format; 31 | pub mod video_settings; 32 | -------------------------------------------------------------------------------- /av-foundation/src/media_format.rs: -------------------------------------------------------------------------------- 1 | use objc2_foundation::NSString; 2 | 3 | pub type AVMediaType = NSString; 4 | 5 | extern "C" { 6 | pub static AVMediaTypeVideo: &'static AVMediaType; 7 | pub static AVMediaTypeAudio: &'static AVMediaType; 8 | pub static AVMediaTypeText: &'static AVMediaType; 9 | pub static AVMediaTypeClosedCaption: &'static AVMediaType; 10 | pub static AVMediaTypeSubtitle: &'static AVMediaType; 11 | pub static AVMediaTypeTimecode: &'static AVMediaType; 12 | pub static AVMediaTypeMetadata: &'static AVMediaType; 13 | pub static AVMediaTypeMuxed: &'static AVMediaType; 14 | pub static AVMediaTypeHaptic: &'static AVMediaType; 15 | pub static AVMediaTypeMetadataObject: &'static AVMediaType; 16 | pub static AVMediaTypeDepthData: &'static AVMediaType; 17 | } 18 | 19 | pub type AVVideoRange = NSString; 20 | 21 | extern "C" { 22 | pub static AVVideoRangeSDR: &'static AVVideoRange; 23 | pub static AVVideoRangeHLG: &'static AVVideoRange; 24 | pub static AVVideoRangePQ: &'static AVVideoRange; 25 | } 26 | 27 | pub type AVMediaCharacteristic = NSString; 28 | 29 | extern "C" { 30 | pub static AVMediaCharacteristicVisual: &'static AVMediaCharacteristic; 31 | pub static AVMediaCharacteristicAudible: &'static AVMediaCharacteristic; 32 | pub static AVMediaCharacteristicLegible: &'static AVMediaCharacteristic; 33 | pub static AVMediaCharacteristicFrameBased: &'static AVMediaCharacteristic; 34 | pub static AVMediaCharacteristicUsesWideGamutColorSpace: &'static AVMediaCharacteristic; 35 | pub static AVMediaCharacteristicContainsHDRVideo: &'static AVMediaCharacteristic; 36 | pub static AVMediaCharacteristicContainsAlphaChannel: &'static AVMediaCharacteristic; 37 | pub static AVMediaCharacteristicIsMainProgramContent: &'static AVMediaCharacteristic; 38 | pub static AVMediaCharacteristicIsAuxiliaryContent: &'static AVMediaCharacteristic; 39 | pub static AVMediaCharacteristicIsOriginalContent: &'static AVMediaCharacteristic; 40 | pub static AVMediaCharacteristicContainsOnlyForcedSubtitles: &'static AVMediaCharacteristic; 41 | pub static AVMediaCharacteristicTranscribesSpokenDialogForAccessibility: &'static AVMediaCharacteristic; 42 | pub static AVMediaCharacteristicDescribesMusicAndSoundForAccessibility: &'static AVMediaCharacteristic; 43 | pub static AVMediaCharacteristicEnhancesSpeechIntelligibility: &'static AVMediaCharacteristic; 44 | pub static AVMediaCharacteristicEasyToRead: &'static AVMediaCharacteristic; 45 | pub static AVMediaCharacteristicDescribesVideoForAccessibility: &'static AVMediaCharacteristic; 46 | pub static AVMediaCharacteristicLanguageTranslation: &'static AVMediaCharacteristic; 47 | pub static AVMediaCharacteristicDubbedTranslation: &'static AVMediaCharacteristic; 48 | pub static AVMediaCharacteristicVoiceOverTranslation: &'static AVMediaCharacteristic; 49 | pub static AVMediaCharacteristicTactileMinimal: &'static AVMediaCharacteristic; 50 | pub static AVMediaCharacteristicContainsStereoMultiviewVideo: &'static AVMediaCharacteristic; 51 | pub static AVMediaCharacteristicCarriesVideoStereoMetadata: &'static AVMediaCharacteristic; 52 | pub static AVMediaCharacteristicIndicatesHorizontalFieldOfView: &'static AVMediaCharacteristic; 53 | } 54 | -------------------------------------------------------------------------------- /av-foundation/src/video_settings.rs: -------------------------------------------------------------------------------- 1 | use objc2_foundation::NSString; 2 | 3 | pub type AVVideoCodecKey = NSString; 4 | 5 | extern "C" { 6 | pub static AVVideoCodecTypeHEVC: &'static AVVideoCodecKey; 7 | pub static AVVideoCodecTypeH264: &'static AVVideoCodecKey; 8 | pub static AVVideoCodecTypeJPEG: &'static AVVideoCodecKey; 9 | pub static AVVideoCodecTypeAppleProRes4444: &'static AVVideoCodecKey; 10 | pub static AVVideoCodecTypeAppleProRes422: &'static AVVideoCodecKey; 11 | pub static AVVideoCodecTypeAppleProRes422HQ: &'static AVVideoCodecKey; 12 | pub static AVVideoCodecTypeAppleProRes422LT: &'static AVVideoCodecKey; 13 | pub static AVVideoCodecTypeAppleProRes422Proxy: &'static AVVideoCodecKey; 14 | pub static AVVideoCodecTypeHEVCWithAlpha: &'static AVVideoCodecKey; 15 | 16 | pub static AVVideoCodecHEVC: &'static NSString; 17 | pub static AVVideoCodecH264: &'static NSString; 18 | pub static AVVideoCodecJPEG: &'static NSString; 19 | #[cfg(target_os = "macos")] 20 | pub static AVVideoCodecAppleProRes4444: &'static NSString; 21 | #[cfg(target_os = "macos")] 22 | pub static AVVideoCodecAppleProRes422: &'static NSString; 23 | 24 | pub static AVVideoWidthKey: &'static NSString; 25 | pub static AVVideoHeightKey: &'static NSString; 26 | 27 | pub static AVVideoPixelAspectRatioKey: &'static NSString; 28 | pub static AVVideoPixelAspectRatioHorizontalSpacingKey: &'static NSString; 29 | pub static AVVideoPixelAspectRatioVerticalSpacingKey: &'static NSString; 30 | 31 | pub static AVVideoCleanApertureKey: &'static NSString; 32 | pub static AVVideoCleanApertureWidthKey: &'static NSString; 33 | pub static AVVideoCleanApertureHeightKey: &'static NSString; 34 | pub static AVVideoCleanApertureHorizontalOffsetKey: &'static NSString; 35 | pub static AVVideoCleanApertureVerticalOffsetKey: &'static NSString; 36 | 37 | pub static AVVideoScalingModeKey: &'static NSString; 38 | pub static AVVideoScalingModeFit: &'static NSString; 39 | pub static AVVideoScalingModeResize: &'static NSString; 40 | pub static AVVideoScalingModeResizeAspect: &'static NSString; 41 | pub static AVVideoScalingModeResizeAspectFill: &'static NSString; 42 | 43 | pub static AVVideoColorPropertiesKey: &'static NSString; 44 | pub static AVVideoColorPrimariesKey: &'static NSString; 45 | pub static AVVideoColorPrimaries_ITU_R_709_2: &'static NSString; 46 | pub static AVVideoColorPrimaries_EBU_3213: &'static NSString; 47 | pub static AVVideoColorPrimaries_SMPTE_C: &'static NSString; 48 | pub static AVVideoColorPrimaries_P3_D65: &'static NSString; 49 | pub static AVVideoColorPrimaries_ITU_R_2020: &'static NSString; 50 | pub static AVVideoTransferFunctionKey: &'static NSString; 51 | pub static AVVideoTransferFunction_ITU_R_709_2: &'static NSString; 52 | pub static AVVideoTransferFunction_SMPTE_240M_1995: &'static NSString; 53 | pub static AVVideoTransferFunction_SMPTE_ST_2084_PQ: &'static NSString; 54 | pub static AVVideoTransferFunction_ITU_R_2100_HLG: &'static NSString; 55 | pub static AVVideoTransferFunction_Linear: &'static NSString; 56 | pub static AVVideoYCbCrMatrixKey: &'static NSString; 57 | pub static AVVideoYCbCrMatrix_ITU_R_709_2: &'static NSString; 58 | pub static AVVideoYCbCrMatrix_ITU_R_601_4: &'static NSString; 59 | pub static AVVideoYCbCrMatrix_SMPTE_240M_1995: &'static NSString; 60 | pub static AVVideoYCbCrMatrix_ITU_R_2020: &'static NSString; 61 | 62 | pub static AVVideoAllowWideColorKey: &'static NSString; 63 | 64 | pub static AVVideoCompressionPropertiesKey: &'static NSString; 65 | pub static AVVideoAverageBitRateKey: &'static NSString; 66 | pub static AVVideoMaxKeyFrameIntervalKey: &'static NSString; 67 | pub static AVVideoMaxKeyFrameIntervalDurationKey: &'static NSString; 68 | pub static AVVideoAppleProRAWBitDepthKey: &'static NSString; 69 | 70 | pub static AVVideoAllowFrameReorderingKey: &'static NSString; 71 | pub static AVVideoProfileLevelKey: &'static NSString; 72 | pub static AVVideoProfileLevelH264Baseline30: &'static NSString; 73 | pub static AVVideoProfileLevelH264Baseline31: &'static NSString; 74 | pub static AVVideoProfileLevelH264Baseline41: &'static NSString; 75 | pub static AVVideoProfileLevelH264BaselineAutoLevel: &'static NSString; 76 | pub static AVVideoProfileLevelH264Main30: &'static NSString; 77 | pub static AVVideoProfileLevelH264Main31: &'static NSString; 78 | pub static AVVideoProfileLevelH264Main32: &'static NSString; 79 | pub static AVVideoProfileLevelH264Main41: &'static NSString; 80 | pub static AVVideoProfileLevelH264MainAutoLevel: &'static NSString; 81 | pub static AVVideoProfileLevelH264High40: &'static NSString; 82 | pub static AVVideoProfileLevelH264High41: &'static NSString; 83 | pub static AVVideoProfileLevelH264HighAutoLevel: &'static NSString; 84 | 85 | pub static AVVideoH264EntropyModeKey: &'static NSString; 86 | pub static AVVideoH264EntropyModeCAVLC: &'static NSString; 87 | pub static AVVideoH264EntropyModeCABAC: &'static NSString; 88 | 89 | pub static AVVideoExpectedSourceFrameRateKey: &'static NSString; 90 | pub static AVVideoAverageNonDroppableFrameRateKey: &'static NSString; 91 | pub static AVVideoDecompressionPropertiesKey: &'static NSString; 92 | pub static AVVideoEncoderSpecificationKey: &'static NSString; 93 | } 94 | 95 | pub type AVVideoApertureMode = NSString; 96 | 97 | extern "C" { 98 | pub static AVVideoApertureModeCleanAperture: &'static AVVideoApertureMode; 99 | pub static AVVideoApertureModeProductionAperture: &'static AVVideoApertureMode; 100 | pub static AVVideoApertureModeEncodedPixels: &'static AVVideoApertureMode; 101 | } 102 | -------------------------------------------------------------------------------- /core-audio-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "core-audio-types" 3 | description = "Bindings to CoreAudioTypes framework" 4 | homepage = "https://github.com/rust-media/apple-media-rs" 5 | repository = "https://github.com/rust-media/apple-media-rs" 6 | documentation = "https://docs.rs/core-audio-types" 7 | version = "0.1.7" 8 | authors = ["Zhou Wei "] 9 | license = "MIT OR Apache-2.0" 10 | categories = ["multimedia::audio", "os::macos-apis"] 11 | keywords = ["coreaudio"] 12 | edition = "2018" 13 | 14 | [dependencies] 15 | cfg-if = "1.0" 16 | core-foundation-sys = { version = "0.8", default-features = false } 17 | libc = "0.2" 18 | 19 | [features] 20 | default = ["link"] 21 | link = ["core-foundation-sys/link"] 22 | prefer-fixed-point = [] 23 | 24 | [package.metadata.docs.rs] 25 | no-default-features = true 26 | default-target = "x86_64-apple-darwin" 27 | targets = [ 28 | "x86_64-apple-darwin", 29 | "x86_64-apple-ios", 30 | "aarch64-apple-darwin", 31 | "aarch64-apple-ios", 32 | ] 33 | -------------------------------------------------------------------------------- /core-audio-types/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /core-audio-types/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /core-audio-types/README.md: -------------------------------------------------------------------------------- 1 | # core-audio-types 2 | 3 | [![Version](https://img.shields.io/crates/v/core-audio-types)](https://crates.io/crates/core-audio-types) 4 | [![Documentation](https://docs.rs/core-audio-types/badge.svg)](https://docs.rs/core-audio-types) 5 | [![License](https://img.shields.io/badge/License-Apache%202-blue.svg)](LICENSE-APACHE) 6 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE-MIT) 7 | 8 | Rust bindings to CoreAudioTypes framework 9 | -------------------------------------------------------------------------------- /core-audio-types/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, improper_ctypes)] 2 | 3 | #[macro_use] 4 | extern crate cfg_if; 5 | extern crate core_foundation_sys; 6 | extern crate libc; 7 | 8 | pub type OSType = u32; 9 | 10 | pub mod base_types; 11 | pub mod session_types; 12 | -------------------------------------------------------------------------------- /core-audio-types/src/session_types.rs: -------------------------------------------------------------------------------- 1 | cfg_if! { 2 | if #[cfg(target_pointer_width = "64")] { 3 | use crate::libc::{c_long, c_ulong}; 4 | pub type AVAudioInteger = c_long; 5 | pub type AVAudioUInteger = c_ulong; 6 | } else { 7 | use crate::libc::{c_int, c_uint}; 8 | pub type AVAudioInteger = c_int; 9 | pub type AVAudioUInteger = c_uint; 10 | } 11 | } 12 | 13 | pub type AudioSessionID = u32; 14 | 15 | pub type AVAudioSessionErrorCode = AVAudioInteger; 16 | 17 | #[inline] 18 | const fn fourcc(code: &[u8; 4]) -> AVAudioInteger { 19 | (((code[0] as u32) << 24) | ((code[1] as u32) << 16) | ((code[2] as u32) << 8) | (code[3] as u32)) as AVAudioInteger 20 | } 21 | 22 | pub const AVAudioSessionErrorCodeNone: AVAudioSessionErrorCode = 0; 23 | pub const AVAudioSessionErrorCodeMediaServicesFailed: AVAudioSessionErrorCode = fourcc(b"msrv"); 24 | pub const AVAudioSessionErrorCodeIsBusy: AVAudioSessionErrorCode = fourcc(b"!act"); 25 | pub const AVAudioSessionErrorCodeIncompatibleCategory: AVAudioSessionErrorCode = fourcc(b"!cat"); 26 | pub const AVAudioSessionErrorCodeCannotInterruptOthers: AVAudioSessionErrorCode = fourcc(b"!int"); 27 | pub const AVAudioSessionErrorCodeMissingEntitlement: AVAudioSessionErrorCode = fourcc(b"ent?"); 28 | pub const AVAudioSessionErrorCodeSiriIsRecording: AVAudioSessionErrorCode = fourcc(b"siri"); 29 | pub const AVAudioSessionErrorCodeCannotStartPlaying: AVAudioSessionErrorCode = fourcc(b"!pla"); 30 | pub const AVAudioSessionErrorCodeCannotStartRecording: AVAudioSessionErrorCode = fourcc(b"!rec"); 31 | pub const AVAudioSessionErrorCodeBadParam: AVAudioSessionErrorCode = -50; 32 | pub const AVAudioSessionErrorCodeInsufficientPriority: AVAudioSessionErrorCode = fourcc(b"!pri"); 33 | pub const AVAudioSessionErrorCodeResourceNotAvailable: AVAudioSessionErrorCode = fourcc(b"!res"); 34 | pub const AVAudioSessionErrorCodeUnspecified: AVAudioSessionErrorCode = fourcc(b"what"); 35 | pub const AVAudioSessionErrorCodeExpiredSession: AVAudioSessionErrorCode = fourcc(b"!ses"); 36 | pub const AVAudioSessionErrorCodeSessionNotActive: AVAudioSessionErrorCode = fourcc(b"inac"); 37 | -------------------------------------------------------------------------------- /core-graphics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "core-graphics2" 3 | description = "Safe bindings to CoreGraphics framework, including display stream" 4 | homepage = "https://github.com/rust-media/apple-media-rs" 5 | repository = "https://github.com/rust-media/apple-media-rs" 6 | documentation = "https://docs.rs/core-graphics2" 7 | version = "0.4.1" 8 | authors = ["Zhou Wei "] 9 | license = "MIT OR Apache-2.0" 10 | categories = ["graphics", "multimedia", "os::macos-apis"] 11 | keywords = ["coregraphics", "display", "displaystream", "screencapture"] 12 | edition = "2018" 13 | 14 | [dependencies] 15 | bitflags = "2.5" 16 | block = "0.1" 17 | cfg-if = "1.0" 18 | core-foundation = { version = "0.10", default-features = false } 19 | dispatch2 = { version = "0.1", optional = true } 20 | io-surface = { version = "0.16", optional = true } 21 | libc = "0.2" 22 | metal = { version = "0.29", optional = true } 23 | objc2 = { version = "0.5", optional = true } 24 | 25 | [dev-dependencies] 26 | core-video = { version = "0.4", path = "../core-video" } 27 | 28 | [features] 29 | default = ["display", "display-stream", "event", "link", "window"] 30 | display = [] 31 | display-stream = ["dispatch2", "display", "io-surface", "objc"] 32 | event = [] 33 | link = ["core-foundation/link"] 34 | objc = ["objc2"] 35 | window = [] 36 | 37 | [[example]] 38 | name = "display_stream" 39 | 40 | [package.metadata.docs.rs] 41 | no-default-features = true 42 | features = ["display", "display-stream", "event", "objc", "window"] 43 | default-target = "x86_64-apple-darwin" 44 | targets = [ 45 | "x86_64-apple-darwin", 46 | "x86_64-apple-ios", 47 | "aarch64-apple-darwin", 48 | "aarch64-apple-ios", 49 | ] 50 | -------------------------------------------------------------------------------- /core-graphics/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /core-graphics/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /core-graphics/README.md: -------------------------------------------------------------------------------- 1 | # core-graphics2 2 | 3 | [![Version](https://img.shields.io/crates/v/core-graphics2)](https://crates.io/crates/core-graphics2) 4 | [![Documentation](https://docs.rs/core-graphics2/badge.svg)](https://docs.rs/core-graphics2) 5 | [![License](https://img.shields.io/badge/License-Apache%202-blue.svg)](LICENSE-APACHE) 6 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE-MIT) 7 | 8 | Rust safe bindings to CoreGraphics framework 9 | 10 | ## Example 11 | 12 | ```toml 13 | [dependencies] 14 | core-graphics2 = { version = "0.1", default-features = false, features = ["display", "display-stream", "link"] } 15 | ``` 16 | 17 | ```rust 18 | use core_foundation::{ 19 | base::{CFType, TCFType}, 20 | boolean::CFBoolean, 21 | dictionary::CFMutableDictionary, 22 | number::CFNumber, 23 | string::CFString, 24 | }; 25 | use core_graphics2::{ 26 | color_space::{CGColorSpace, CGColorSpaceNames}, 27 | display::CGDisplay, 28 | display_stream::*, 29 | }; 30 | use core_video::pixel_buffer::{self, CVPixelBuffer}; 31 | use dispatch2::{Queue, QueueAttribute}; 32 | use io_surface::IOSurface; 33 | 34 | let display = CGDisplay::main(); 35 | let output_width = display.pixels_wide(); 36 | let output_height = display.pixels_high(); 37 | let pixel_format = pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; 38 | let mut properties = CFMutableDictionary::::new(); 39 | properties.add(&CGDisplayStreamProperties::ShowCursor.into(), &CFBoolean::true_value().as_CFType()); 40 | if let Some(color_space) = CGColorSpace::from_name(&CGColorSpaceNames::SRGB.into()) { 41 | properties.add(&CGDisplayStreamProperties::ColorSpace.into(), &color_space.as_CFType()); 42 | } 43 | properties.add(&CGDisplayStreamProperties::MinimumFrameTime.into(), &CFNumber::from(1.0 / 60.0).as_CFType()); 44 | let ycbcr_matrix: CFString = CGDisplayStreamYCbCrMatrices::ITU_R_709_2.into(); 45 | properties.add(&CGDisplayStreamProperties::YCbCrMatrix.into(), &ycbcr_matrix.as_CFType()); 46 | 47 | let closure = move |status: CGDisplayStreamFrameStatus, timestamp: u64, surface: Option, update: Option| match status { 48 | CGDisplayStreamFrameStatus::Stopped => { 49 | println!("status: Stopped"); 50 | } 51 | CGDisplayStreamFrameStatus::FrameComplete => { 52 | println!("status: {:?}, timestamp: {}", status, timestamp); 53 | if let Some(update) = update { 54 | println!("refreshed rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::RefreshedRects)); 55 | println!("moved rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::MovedRects)); 56 | println!("dirty rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::DirtyRects)); 57 | println!("reduced dirty rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::ReducedDirtyRects)); 58 | } 59 | if let Some(surface) = surface { 60 | if let Ok(pixel_buffer) = CVPixelBuffer::from_io_surface(&surface, None) { 61 | println!("pixel_buffer: {:?}", pixel_buffer); 62 | } 63 | } 64 | } 65 | _ => {} 66 | }; 67 | 68 | let queue = Queue::new("com.screen_capture.queue", QueueAttribute::Serial); 69 | if let Ok(display_stream) = CGDisplayStream::new_with_dispatch_queue( 70 | display.id, 71 | output_width, 72 | output_height, 73 | pixel_format as i32, 74 | &properties.to_immutable(), 75 | &queue, 76 | closure, 77 | ) { 78 | display_stream.start(); 79 | } 80 | ``` 81 | -------------------------------------------------------------------------------- /core-graphics/examples/display_stream.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::{ 2 | base::{CFType, TCFType}, 3 | boolean::CFBoolean, 4 | dictionary::CFMutableDictionary, 5 | number::CFNumber, 6 | string::CFString, 7 | }; 8 | use core_graphics2::{ 9 | color_space::{CGColorSpace, CGColorSpaceNames}, 10 | display::CGDisplay, 11 | display_stream::*, 12 | }; 13 | use core_video::pixel_buffer::{self, CVPixelBuffer}; 14 | use dispatch2::{Queue, QueueAttribute}; 15 | use io_surface::IOSurface; 16 | 17 | fn main() { 18 | let display = CGDisplay::main(); 19 | let output_width = display.pixels_wide(); 20 | let output_height = display.pixels_high(); 21 | let pixel_format = pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; 22 | let mut properties = CFMutableDictionary::::new(); 23 | properties.add(&CGDisplayStreamProperties::ShowCursor.into(), &CFBoolean::true_value().as_CFType()); 24 | if let Some(color_space) = CGColorSpace::from_name(&CGColorSpaceNames::SRGB.into()) { 25 | properties.add(&CGDisplayStreamProperties::ColorSpace.into(), &color_space.as_CFType()); 26 | } 27 | properties.add(&CGDisplayStreamProperties::MinimumFrameTime.into(), &CFNumber::from(1.0 / 60.0).as_CFType()); 28 | let ycbcr_matrix: CFString = CGDisplayStreamYCbCrMatrices::ITU_R_709_2.into(); 29 | properties.add(&CGDisplayStreamProperties::YCbCrMatrix.into(), &ycbcr_matrix.as_CFType()); 30 | let closure = 31 | move |status: CGDisplayStreamFrameStatus, timestamp: u64, surface: Option, update: Option| match status { 32 | CGDisplayStreamFrameStatus::Stopped => { 33 | println!("status: Stopped"); 34 | } 35 | CGDisplayStreamFrameStatus::FrameComplete => { 36 | println!("status: {:?}, timestamp: {}", status, timestamp); 37 | if let Some(update) = update { 38 | println!("refreshed rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::RefreshedRects)); 39 | println!("moved rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::MovedRects)); 40 | println!("dirty rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::DirtyRects)); 41 | println!("reduced dirty rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::ReducedDirtyRects)); 42 | } 43 | if let Some(surface) = surface { 44 | if let Ok(pixel_buffer) = CVPixelBuffer::from_io_surface(&surface, None) { 45 | println!("pixel_buffer: {:?}", pixel_buffer); 46 | } 47 | } 48 | } 49 | _ => {} 50 | }; 51 | let queue = Queue::new("com.screen_capture.queue", QueueAttribute::Serial); 52 | if let Ok(display_stream) = CGDisplayStream::new_with_dispatch_queue( 53 | display.id, 54 | output_width, 55 | output_height, 56 | pixel_format as i32, 57 | &properties.to_immutable(), 58 | &queue, 59 | closure, 60 | ) { 61 | display_stream.start(); 62 | std::thread::sleep(std::time::Duration::from_secs(10)); 63 | display_stream.stop(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core-graphics/src/affine_transform.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | base::CGFloat, 3 | geometry::{CGPoint, CGRect, CGSize, CGVector}, 4 | }; 5 | 6 | #[repr(C)] 7 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 8 | pub struct CGAffineTransform { 9 | pub a: CGFloat, 10 | pub b: CGFloat, 11 | pub c: CGFloat, 12 | pub d: CGFloat, 13 | pub tx: CGFloat, 14 | pub ty: CGFloat, 15 | } 16 | 17 | #[repr(C)] 18 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 19 | pub struct CGAffineTransformComponents { 20 | pub scale: CGSize, 21 | pub horizontal_shear: CGFloat, 22 | pub rotation: CGFloat, 23 | pub translation: CGVector, 24 | } 25 | 26 | pub const CGAffineTransformIdentity: CGAffineTransform = CGAffineTransform { 27 | a: 1.0, 28 | b: 0.0, 29 | c: 0.0, 30 | d: 1.0, 31 | tx: 0.0, 32 | ty: 0.0, 33 | }; 34 | 35 | extern "C" { 36 | pub fn CGAffineTransformMake(a: CGFloat, b: CGFloat, c: CGFloat, d: CGFloat, tx: CGFloat, ty: CGFloat) -> CGAffineTransform; 37 | pub fn CGAffineTransformMakeTranslation(tx: CGFloat, ty: CGFloat) -> CGAffineTransform; 38 | pub fn CGAffineTransformMakeScale(sx: CGFloat, sy: CGFloat) -> CGAffineTransform; 39 | pub fn CGAffineTransformMakeRotation(angle: CGFloat) -> CGAffineTransform; 40 | pub fn CGAffineTransformIsIdentity(t: CGAffineTransform) -> bool; 41 | pub fn CGAffineTransformTranslate(t: CGAffineTransform, tx: CGFloat, ty: CGFloat) -> CGAffineTransform; 42 | pub fn CGAffineTransformScale(t: CGAffineTransform, sx: CGFloat, sy: CGFloat) -> CGAffineTransform; 43 | pub fn CGAffineTransformRotate(t: CGAffineTransform, angle: CGFloat) -> CGAffineTransform; 44 | pub fn CGAffineTransformInvert(t: CGAffineTransform) -> CGAffineTransform; 45 | pub fn CGAffineTransformConcat(t1: CGAffineTransform, t2: CGAffineTransform) -> CGAffineTransform; 46 | pub fn CGPointApplyAffineTransform(point: CGPoint, t: CGAffineTransform) -> CGPoint; 47 | pub fn CGSizeApplyAffineTransform(size: CGSize, t: CGAffineTransform) -> CGSize; 48 | pub fn CGRectApplyAffineTransform(rect: CGRect, t: CGAffineTransform) -> CGRect; 49 | pub fn CGAffineTransformDecompose(transform: CGAffineTransform) -> CGAffineTransformComponents; 50 | pub fn CGAffineTransformMakeWithComponents(components: CGAffineTransformComponents) -> CGAffineTransform; 51 | } 52 | 53 | impl CGAffineTransform { 54 | pub fn new(a: CGFloat, b: CGFloat, c: CGFloat, d: CGFloat, tx: CGFloat, ty: CGFloat) -> CGAffineTransform { 55 | unsafe { CGAffineTransformMake(a, b, c, d, tx, ty) } 56 | } 57 | 58 | pub fn new_translate(tx: CGFloat, ty: CGFloat) -> CGAffineTransform { 59 | unsafe { CGAffineTransformMakeTranslation(tx, ty) } 60 | } 61 | 62 | pub fn new_scale(sx: CGFloat, sy: CGFloat) -> CGAffineTransform { 63 | unsafe { CGAffineTransformMakeScale(sx, sy) } 64 | } 65 | 66 | pub fn new_rotate(angle: CGFloat) -> CGAffineTransform { 67 | unsafe { CGAffineTransformMakeRotation(angle) } 68 | } 69 | 70 | pub fn from_components(components: CGAffineTransformComponents) -> CGAffineTransform { 71 | unsafe { CGAffineTransformMakeWithComponents(components) } 72 | } 73 | 74 | pub fn translate(&self, tx: CGFloat, ty: CGFloat) -> CGAffineTransform { 75 | unsafe { CGAffineTransformTranslate(*self, tx, ty) } 76 | } 77 | 78 | pub fn scale(&self, sx: CGFloat, sy: CGFloat) -> CGAffineTransform { 79 | unsafe { CGAffineTransformScale(*self, sx, sy) } 80 | } 81 | 82 | pub fn rotate(&self, angle: CGFloat) -> CGAffineTransform { 83 | unsafe { CGAffineTransformRotate(*self, angle) } 84 | } 85 | 86 | pub fn is_identity(&self) -> bool { 87 | unsafe { CGAffineTransformIsIdentity(*self) } 88 | } 89 | 90 | pub fn invert(&self) -> CGAffineTransform { 91 | unsafe { CGAffineTransformInvert(*self) } 92 | } 93 | 94 | pub fn concat(&self, other: &CGAffineTransform) -> CGAffineTransform { 95 | unsafe { CGAffineTransformConcat(*self, *other) } 96 | } 97 | 98 | pub fn decompose(&self) -> CGAffineTransformComponents { 99 | unsafe { CGAffineTransformDecompose(*self) } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /core-graphics/src/base.rs: -------------------------------------------------------------------------------- 1 | cfg_if! { 2 | if #[cfg(target_arch = "x86_64")] { 3 | #[allow(non_camel_case_types)] 4 | pub type boolean_t = libc::c_uint; 5 | } else { 6 | #[allow(non_camel_case_types)] 7 | pub type boolean_t = libc::c_int; 8 | } 9 | } 10 | 11 | cfg_if! { 12 | if #[cfg(target_pointer_width = "64")] { 13 | pub type CGFloat = libc::c_double; 14 | } else { 15 | pub type CGFloat = libc::c_float; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core-graphics/src/bitmap_context.rs: -------------------------------------------------------------------------------- 1 | use std::{mem, ptr::null_mut, slice}; 2 | 3 | use core_foundation::base::TCFType; 4 | use libc::{c_void, size_t}; 5 | 6 | use crate::{ 7 | color_space::{CGColorSpace, CGColorSpaceRef}, 8 | context::{CGContext, CGContextRef}, 9 | image::{CGBitmapInfo, CGImage, CGImageAlphaInfo, CGImageRef}, 10 | }; 11 | 12 | pub type CGBitmapContextReleaseDataCallback = extern "C" fn(*mut c_void, *mut c_void); 13 | 14 | extern "C" { 15 | pub fn CGBitmapContextCreateWithData( 16 | data: *mut c_void, 17 | width: size_t, 18 | height: size_t, 19 | bitsPerComponent: size_t, 20 | bytesPerRow: size_t, 21 | space: CGColorSpaceRef, 22 | bitmapInfo: u32, 23 | releaseCallback: CGBitmapContextReleaseDataCallback, 24 | releaseInfo: *mut c_void, 25 | ) -> CGContextRef; 26 | pub fn CGBitmapContextCreate( 27 | data: *mut c_void, 28 | width: size_t, 29 | height: size_t, 30 | bitsPerComponent: size_t, 31 | bytesPerRow: size_t, 32 | space: CGColorSpaceRef, 33 | bitmapInfo: u32, 34 | ) -> CGContextRef; 35 | pub fn CGBitmapContextGetData(context: CGContextRef) -> *mut c_void; 36 | pub fn CGBitmapContextGetWidth(context: CGContextRef) -> size_t; 37 | pub fn CGBitmapContextGetHeight(context: CGContextRef) -> size_t; 38 | pub fn CGBitmapContextGetBitsPerComponent(context: CGContextRef) -> size_t; 39 | pub fn CGBitmapContextGetBitsPerPixel(context: CGContextRef) -> size_t; 40 | pub fn CGBitmapContextGetBytesPerRow(context: CGContextRef) -> size_t; 41 | pub fn CGBitmapContextGetColorSpace(context: CGContextRef) -> CGColorSpaceRef; 42 | pub fn CGBitmapContextGetAlphaInfo(context: CGContextRef) -> CGImageAlphaInfo; 43 | pub fn CGBitmapContextGetBitmapInfo(context: CGContextRef) -> CGBitmapInfo; 44 | pub fn CGBitmapContextCreateImage(context: CGContextRef) -> CGImageRef; 45 | } 46 | 47 | pub trait BitmapData { 48 | unsafe fn ptr(&self) -> *const u8; 49 | unsafe fn mut_ptr(&self) -> *mut u8; 50 | fn width(&self) -> usize; 51 | fn height(&self) -> usize; 52 | fn bytes_per_row(&self) -> usize; 53 | } 54 | 55 | impl CGContext { 56 | pub fn new_bitmap_context( 57 | width: size_t, 58 | height: size_t, 59 | bits_per_component: size_t, 60 | bytes_per_row: size_t, 61 | space: Option<&CGColorSpace>, 62 | bitmap_info: u32, 63 | ) -> Option { 64 | unsafe { 65 | let context = CGBitmapContextCreate( 66 | null_mut(), 67 | width, 68 | height, 69 | bits_per_component, 70 | bytes_per_row, 71 | space.map_or(null_mut(), |s| s.as_concrete_TypeRef()), 72 | bitmap_info, 73 | ); 74 | if context.is_null() { 75 | None 76 | } else { 77 | Some(TCFType::wrap_under_create_rule(context)) 78 | } 79 | } 80 | } 81 | 82 | pub unsafe fn new_bitmap_context_with_data( 83 | data: &mut [u8], 84 | width: size_t, 85 | height: size_t, 86 | bits_per_component: size_t, 87 | bytes_per_row: size_t, 88 | space: Option<&CGColorSpace>, 89 | bitmap_info: u32, 90 | ) -> Option { 91 | if data.len() < height * bytes_per_row { 92 | return None; 93 | } 94 | unsafe { 95 | let context = CGBitmapContextCreate( 96 | data.as_mut_ptr() as *mut c_void, 97 | width, 98 | height, 99 | bits_per_component, 100 | bytes_per_row, 101 | space.map_or(null_mut(), |s| s.as_concrete_TypeRef()), 102 | bitmap_info, 103 | ); 104 | if context.is_null() { 105 | None 106 | } else { 107 | Some(TCFType::wrap_under_create_rule(context)) 108 | } 109 | } 110 | } 111 | 112 | pub fn new_bitmap_context_with_bitmap_data( 113 | bitmap_data: Box>, 114 | bits_per_component: size_t, 115 | space: Option<&CGColorSpace>, 116 | bitmap_info: u32, 117 | ) -> Option { 118 | let (width, height, bytes_per_row) = (bitmap_data.width() as size_t, bitmap_data.height() as size_t, bitmap_data.bytes_per_row() as size_t); 119 | unsafe { 120 | let ptr = bitmap_data.mut_ptr() as *mut c_void; 121 | let info = mem::transmute::>, &mut c_void>(bitmap_data); 122 | let context = CGBitmapContextCreateWithData( 123 | ptr, 124 | width, 125 | height, 126 | bits_per_component, 127 | bytes_per_row, 128 | space.map_or(null_mut(), |s| s.as_concrete_TypeRef()), 129 | bitmap_info, 130 | release, 131 | info, 132 | ); 133 | if context.is_null() { 134 | drop(mem::transmute::<*mut c_void, Box>>(info)); 135 | return None; 136 | } else { 137 | return Some(TCFType::wrap_under_create_rule(context)); 138 | } 139 | } 140 | 141 | extern "C" fn release(release_info: *mut c_void, _: *mut c_void) { 142 | unsafe { drop(mem::transmute::<*mut c_void, Box>>(release_info)) } 143 | } 144 | } 145 | 146 | pub fn new_image(&self) -> Option { 147 | unsafe { 148 | let image = CGBitmapContextCreateImage(self.as_concrete_TypeRef()); 149 | if image.is_null() { 150 | None 151 | } else { 152 | Some(TCFType::wrap_under_create_rule(image)) 153 | } 154 | } 155 | } 156 | 157 | pub fn data(&mut self) -> &mut [u8] { 158 | unsafe { slice::from_raw_parts_mut(CGBitmapContextGetData(self.as_concrete_TypeRef()) as *mut u8, self.height() * self.bytes_per_row()) } 159 | } 160 | 161 | pub fn width(&self) -> usize { 162 | unsafe { CGBitmapContextGetWidth(self.as_concrete_TypeRef()) } 163 | } 164 | 165 | pub fn height(&self) -> usize { 166 | unsafe { CGBitmapContextGetHeight(self.as_concrete_TypeRef()) } 167 | } 168 | 169 | pub fn bits_per_component(&self) -> usize { 170 | unsafe { CGBitmapContextGetBitsPerComponent(self.as_concrete_TypeRef()) } 171 | } 172 | 173 | pub fn bits_per_pixel(&self) -> usize { 174 | unsafe { CGBitmapContextGetBitsPerPixel(self.as_concrete_TypeRef()) } 175 | } 176 | 177 | pub fn bytes_per_row(&self) -> usize { 178 | unsafe { CGBitmapContextGetBytesPerRow(self.as_concrete_TypeRef()) } 179 | } 180 | 181 | pub fn color_space(&self) -> CGColorSpace { 182 | unsafe { TCFType::wrap_under_get_rule(CGBitmapContextGetColorSpace(self.as_concrete_TypeRef())) } 183 | } 184 | 185 | pub fn alpha_info(&self) -> CGImageAlphaInfo { 186 | unsafe { CGBitmapContextGetAlphaInfo(self.as_concrete_TypeRef()) } 187 | } 188 | 189 | pub fn bitmap_info(&self) -> CGBitmapInfo { 190 | unsafe { CGBitmapContextGetBitmapInfo(self.as_concrete_TypeRef()) } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /core-graphics/src/color_conversion_info.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | base::{CFType, CFTypeID, TCFType}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::{CFString, CFStringRef}, 7 | }; 8 | use libc::c_void; 9 | 10 | use crate::color_space::{CGColorSpace, CGColorSpaceRef}; 11 | 12 | #[repr(C)] 13 | pub struct __CGColorConversionInfo(c_void); 14 | 15 | pub type CGColorConversionInfoRef = *const __CGColorConversionInfo; 16 | 17 | #[repr(u32)] 18 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 19 | pub enum CGColorConversionInfoTransformType { 20 | #[doc(alias = "kCGColorConversionTransformFromSpace")] 21 | FromSpace = 0, 22 | #[doc(alias = "kCGColorConversionTransformToSpace")] 23 | ToSpace = 1, 24 | #[doc(alias = "kCGColorConversionTransformApplySpace")] 25 | ApplySpace = 2, 26 | } 27 | 28 | extern "C" { 29 | pub fn CGColorConversionInfoGetTypeID() -> CFTypeID; 30 | pub fn CGColorConversionInfoCreate(src: CGColorSpaceRef, dst: CGColorSpaceRef) -> CGColorConversionInfoRef; 31 | pub fn CGColorConversionInfoCreateWithOptions(src: CGColorSpaceRef, dst: CGColorSpaceRef, options: CFDictionaryRef) -> CGColorConversionInfoRef; 32 | 33 | pub static kCGColorConversionBlackPointCompensation: CFStringRef; 34 | pub static kCGColorConversionTRCSize: CFStringRef; 35 | } 36 | 37 | declare_TCFType!(CGColorConversionInfo, CGColorConversionInfoRef); 38 | impl_TCFType!(CGColorConversionInfo, CGColorConversionInfoRef, CGColorConversionInfoGetTypeID); 39 | impl_CFTypeDescription!(CGColorConversionInfo); 40 | 41 | impl CGColorConversionInfo { 42 | pub fn new(src: Option<&CGColorSpace>, dst: Option<&CGColorSpace>) -> Option { 43 | unsafe { 44 | let info = 45 | CGColorConversionInfoCreate(src.map_or(null_mut(), |s| s.as_concrete_TypeRef()), dst.map_or(null_mut(), |d| d.as_concrete_TypeRef())); 46 | if info.is_null() { 47 | None 48 | } else { 49 | Some(TCFType::wrap_under_create_rule(info)) 50 | } 51 | } 52 | } 53 | 54 | pub fn new_with_options( 55 | src: Option<&CGColorSpace>, 56 | dst: Option<&CGColorSpace>, 57 | options: Option<&CFDictionary>, 58 | ) -> Option { 59 | unsafe { 60 | let info = CGColorConversionInfoCreateWithOptions( 61 | src.map_or(null_mut(), |s| s.as_concrete_TypeRef()), 62 | dst.map_or(null_mut(), |d| d.as_concrete_TypeRef()), 63 | options.map_or(null(), |o| o.as_concrete_TypeRef()), 64 | ); 65 | if info.is_null() { 66 | None 67 | } else { 68 | Some(TCFType::wrap_under_create_rule(info)) 69 | } 70 | } 71 | } 72 | } 73 | 74 | pub enum CGColorConversionKeys { 75 | BlackPointCompensation, 76 | TRCSize, 77 | } 78 | 79 | impl From for CFStringRef { 80 | fn from(key: CGColorConversionKeys) -> Self { 81 | unsafe { 82 | match key { 83 | CGColorConversionKeys::BlackPointCompensation => kCGColorConversionBlackPointCompensation, 84 | CGColorConversionKeys::TRCSize => kCGColorConversionTRCSize, 85 | } 86 | } 87 | } 88 | } 89 | 90 | impl From for CFString { 91 | fn from(key: CGColorConversionKeys) -> Self { 92 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /core-graphics/src/data_provider.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::CString, ptr::null_mut, sync::Arc}; 2 | 3 | use core_foundation::{ 4 | base::{CFTypeID, TCFType}, 5 | data::{CFData, CFDataRef}, 6 | url::{CFURLRef, CFURL}, 7 | }; 8 | use libc::{c_char, c_void, off_t, size_t}; 9 | 10 | #[repr(C)] 11 | pub struct __CGDataProvider(c_void); 12 | 13 | pub type CGDataProviderRef = *const __CGDataProvider; 14 | 15 | pub type CGDataProviderGetBytesCallback = extern "C" fn(*mut c_void, *mut c_void, size_t) -> size_t; 16 | pub type CGDataProviderSkipForwardCallback = extern "C" fn(*mut c_void, off_t) -> off_t; 17 | pub type CGDataProviderRewindCallback = extern "C" fn(*mut c_void); 18 | pub type CGDataProviderReleaseInfoCallback = extern "C" fn(*mut c_void); 19 | 20 | #[repr(C)] 21 | pub struct CGDataProviderSequentialCallbacks { 22 | pub version: u32, 23 | pub getBytes: CGDataProviderGetBytesCallback, 24 | pub skipForward: CGDataProviderSkipForwardCallback, 25 | pub rewind: CGDataProviderRewindCallback, 26 | pub releaseInfo: CGDataProviderReleaseInfoCallback, 27 | } 28 | 29 | pub type CGDataProviderGetBytePointerCallback = extern "C" fn(*mut c_void) -> *mut c_void; 30 | pub type CGDataProviderReleaseBytePointerCallback = extern "C" fn(*mut c_void, *const c_void); 31 | pub type CGDataProviderGetBytesAtPositionCallback = extern "C" fn(*mut c_void, *mut c_void, off_t, size_t); 32 | 33 | #[repr(C)] 34 | pub struct CGDataProviderDirectCallbacks { 35 | pub version: u32, 36 | pub getBytePointer: CGDataProviderGetBytePointerCallback, 37 | pub releaseBytePointer: CGDataProviderReleaseBytePointerCallback, 38 | pub getBytesAtPosition: CGDataProviderGetBytesAtPositionCallback, 39 | pub releaseInfo: CGDataProviderReleaseInfoCallback, 40 | } 41 | 42 | pub type CGDataProviderReleaseDataCallback = extern "C" fn(*mut c_void, *const c_void, size_t); 43 | 44 | extern "C" { 45 | pub fn CGDataProviderGetTypeID() -> CFTypeID; 46 | pub fn CGDataProviderCreateSequential(info: *mut c_void, callbacks: *const CGDataProviderSequentialCallbacks) -> CGDataProviderRef; 47 | pub fn CGDataProviderCreateDirect(info: *mut c_void, size: off_t, callbacks: *const CGDataProviderDirectCallbacks) -> CGDataProviderRef; 48 | pub fn CGDataProviderCreateWithData( 49 | info: *mut c_void, 50 | data: *const c_void, 51 | size: size_t, 52 | releaseData: Option, 53 | ) -> CGDataProviderRef; 54 | pub fn CGDataProviderCreateWithCFData(data: CFDataRef) -> CGDataProviderRef; 55 | pub fn CGDataProviderCreateWithURL(url: CFURLRef) -> CGDataProviderRef; 56 | pub fn CGDataProviderCreateWithFilename(filename: *const c_char) -> CGDataProviderRef; 57 | pub fn CGDataProviderRetain(provider: CGDataProviderRef) -> CGDataProviderRef; 58 | pub fn CGDataProviderRelease(provider: CGDataProviderRef); 59 | pub fn CGDataProviderCopyData(provider: CGDataProviderRef) -> CFDataRef; 60 | pub fn CGDataProviderGetInfo(provider: CGDataProviderRef) -> *mut c_void; 61 | } 62 | 63 | pub struct CGDataProvider(CGDataProviderRef); 64 | 65 | impl Drop for CGDataProvider { 66 | fn drop(&mut self) { 67 | unsafe { CGDataProviderRelease(self.0) } 68 | } 69 | } 70 | 71 | impl_TCFType!(CGDataProvider, CGDataProviderRef, CGDataProviderGetTypeID); 72 | impl_CFTypeDescription!(CGDataProvider); 73 | 74 | impl CGDataProvider { 75 | pub unsafe fn new_sequential(info: *mut c_void, callbacks: *const CGDataProviderSequentialCallbacks) -> Option { 76 | let provider = CGDataProviderCreateSequential(info, callbacks); 77 | if provider.is_null() { 78 | None 79 | } else { 80 | Some(TCFType::wrap_under_create_rule(provider)) 81 | } 82 | } 83 | 84 | pub unsafe fn new_direct(info: *mut c_void, size: off_t, callbacks: *const CGDataProviderDirectCallbacks) -> Option { 85 | let provider = CGDataProviderCreateDirect(info, size, callbacks); 86 | if provider.is_null() { 87 | None 88 | } else { 89 | Some(TCFType::wrap_under_create_rule(provider)) 90 | } 91 | } 92 | 93 | pub fn from_buffer(buffer: Arc) -> Option 94 | where 95 | T: AsRef<[u8]> + Sync + Send, 96 | { 97 | unsafe { 98 | let ptr = (*buffer).as_ref().as_ptr() as *const c_void; 99 | let len = (*buffer).as_ref().len() as size_t; 100 | let info = Arc::into_raw(buffer) as *mut c_void; 101 | let data_provider = CGDataProviderCreateWithData(info, ptr, len, Some(release::)); 102 | if data_provider.is_null() { 103 | drop(Arc::from_raw(info)); 104 | return None; 105 | } else { 106 | return Some(TCFType::wrap_under_create_rule(data_provider)); 107 | } 108 | } 109 | 110 | extern "C" fn release(info: *mut c_void, _: *const c_void, _: size_t) { 111 | unsafe { drop(Arc::from_raw(info as *mut T)) } 112 | } 113 | } 114 | 115 | pub unsafe fn from_slice(buffer: &[u8]) -> Option { 116 | let ptr = buffer.as_ptr() as *const c_void; 117 | let len = buffer.len() as size_t; 118 | let data_provider = CGDataProviderCreateWithData(null_mut(), ptr, len, None); 119 | if data_provider.is_null() { 120 | None 121 | } else { 122 | Some(TCFType::wrap_under_create_rule(data_provider)) 123 | } 124 | } 125 | 126 | pub fn from_data(data: &CFData) -> Option { 127 | unsafe { 128 | let data_provider = CGDataProviderCreateWithCFData(data.as_concrete_TypeRef()); 129 | if data_provider.is_null() { 130 | None 131 | } else { 132 | Some(TCFType::wrap_under_create_rule(data_provider)) 133 | } 134 | } 135 | } 136 | 137 | pub fn from_url(url: CFURL) -> Option { 138 | unsafe { 139 | let data_provider = CGDataProviderCreateWithURL(url.as_concrete_TypeRef()); 140 | if data_provider.is_null() { 141 | None 142 | } else { 143 | Some(TCFType::wrap_under_create_rule(data_provider)) 144 | } 145 | } 146 | } 147 | 148 | pub fn from_filename(filename: &str) -> Option { 149 | let c_str = CString::new(filename).ok()?; 150 | unsafe { 151 | let data_provider = CGDataProviderCreateWithFilename(c_str.as_ptr() as *const c_char); 152 | if data_provider.is_null() { 153 | None 154 | } else { 155 | Some(TCFType::wrap_under_create_rule(data_provider)) 156 | } 157 | } 158 | } 159 | 160 | pub fn copy_data(&self) -> Option { 161 | unsafe { 162 | let data = CGDataProviderCopyData(self.as_concrete_TypeRef()); 163 | if data.is_null() { 164 | None 165 | } else { 166 | Some(TCFType::wrap_under_create_rule(data)) 167 | } 168 | } 169 | } 170 | 171 | pub unsafe fn get_info(&self) -> *mut c_void { 172 | unsafe { CGDataProviderGetInfo(self.as_concrete_TypeRef()) } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /core-graphics/src/direct_display.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::{array::CFArrayRef, base::CFTypeID, dictionary::CFDictionaryRef, string::CFStringRef}; 2 | use libc::{c_double, c_float, c_void, size_t}; 3 | 4 | use crate::{ 5 | context::CGContextRef, 6 | error::CGError, 7 | geometry::{CGPoint, CGRect}, 8 | image::CGImageRef, 9 | }; 10 | #[cfg(feature = "window")] 11 | use crate::{window::CGWindowID, window_level::CGWindowLevel}; 12 | 13 | pub type CGDirectDisplayID = u32; 14 | pub type CGOpenGLDisplayMask = u32; 15 | pub type CGRefreshRate = c_double; 16 | pub type CGGammaValue = c_float; 17 | 18 | #[repr(C)] 19 | pub struct __CGDisplayMode(c_void); 20 | 21 | pub type CGDisplayModeRef = *mut __CGDisplayMode; 22 | 23 | pub const kCGNullDirectDisplay: CGDirectDisplayID = 0; 24 | 25 | extern "C" { 26 | pub fn CGMainDisplayID() -> CGDirectDisplayID; 27 | pub fn CGGetDisplaysWithPoint(point: CGPoint, maxDisplays: u32, displays: *mut CGDirectDisplayID, matchingDisplayCount: *mut u32) -> CGError; 28 | pub fn CGGetDisplaysWithRect(rect: CGRect, maxDisplays: u32, displays: *mut CGDirectDisplayID, matchingDisplayCount: *mut u32) -> CGError; 29 | pub fn CGGetDisplaysWithOpenGLDisplayMask( 30 | mask: CGOpenGLDisplayMask, 31 | maxDisplays: u32, 32 | displays: *mut CGDirectDisplayID, 33 | matchingDisplayCount: *mut u32, 34 | ) -> CGError; 35 | pub fn CGGetActiveDisplayList(maxDisplays: u32, activeDisplays: *mut CGDirectDisplayID, displayCount: *mut u32) -> CGError; 36 | pub fn CGGetOnlineDisplayList(maxDisplays: u32, onlineDisplays: *mut CGDirectDisplayID, displayCount: *mut u32) -> CGError; 37 | pub fn CGDisplayIDToOpenGLDisplayMask(display: CGDirectDisplayID) -> CGOpenGLDisplayMask; 38 | pub fn CGOpenGLDisplayMaskToDisplayID(mask: CGOpenGLDisplayMask) -> CGDirectDisplayID; 39 | pub fn CGDisplayBounds(display: CGDirectDisplayID) -> CGRect; 40 | pub fn CGDisplayPixelsWide(display: CGDirectDisplayID) -> size_t; 41 | pub fn CGDisplayPixelsHigh(display: CGDirectDisplayID) -> size_t; 42 | pub fn CGDisplayCopyAllDisplayModes(display: CGDirectDisplayID, options: CFDictionaryRef) -> CFArrayRef; 43 | pub fn CGDisplayCopyDisplayMode(display: CGDirectDisplayID) -> CGDisplayModeRef; 44 | pub fn CGDisplaySetDisplayMode(display: CGDirectDisplayID, mode: CGDisplayModeRef, options: CFDictionaryRef) -> CGError; 45 | pub fn CGDisplayModeGetWidth(mode: CGDisplayModeRef) -> size_t; 46 | pub fn CGDisplayModeGetHeight(mode: CGDisplayModeRef) -> size_t; 47 | pub fn CGDisplayModeCopyPixelEncoding(mode: CGDisplayModeRef) -> CFStringRef; 48 | pub fn CGDisplayModeGetRefreshRate(mode: CGDisplayModeRef) -> CGRefreshRate; 49 | pub fn CGDisplayModeGetIOFlags(mode: CGDisplayModeRef) -> u32; 50 | pub fn CGDisplayModeGetIODisplayModeID(mode: CGDisplayModeRef) -> i32; 51 | pub fn CGDisplayModeIsUsableForDesktopGUI(mode: CGDisplayModeRef) -> bool; 52 | pub fn CGDisplayModeGetTypeID() -> CFTypeID; 53 | pub fn CGDisplayModeRetain(mode: CGDisplayModeRef) -> CGDisplayModeRef; 54 | pub fn CGDisplayModeRelease(mode: CGDisplayModeRef); 55 | pub fn CGDisplayModeGetPixelWidth(mode: CGDisplayModeRef) -> size_t; 56 | pub fn CGDisplayModeGetPixelHeight(mode: CGDisplayModeRef) -> size_t; 57 | pub fn CGSetDisplayTransferByFormula( 58 | display: CGDirectDisplayID, 59 | redMin: CGGammaValue, 60 | redMax: CGGammaValue, 61 | redGamma: CGGammaValue, 62 | greenMin: CGGammaValue, 63 | greenMax: CGGammaValue, 64 | greenGamma: CGGammaValue, 65 | blueMin: CGGammaValue, 66 | blueMax: CGGammaValue, 67 | blueGamma: CGGammaValue, 68 | ) -> CGError; 69 | pub fn CGGetDisplayTransferByFormula( 70 | display: CGDirectDisplayID, 71 | redMin: *mut CGGammaValue, 72 | redMax: *mut CGGammaValue, 73 | redGamma: *mut CGGammaValue, 74 | greenMin: *mut CGGammaValue, 75 | greenMax: *mut CGGammaValue, 76 | greenGamma: *mut CGGammaValue, 77 | blueMin: *mut CGGammaValue, 78 | blueMax: *mut CGGammaValue, 79 | blueGamma: *mut CGGammaValue, 80 | ) -> CGError; 81 | pub fn CGDisplayGammaTableCapacity(display: CGDirectDisplayID) -> u32; 82 | pub fn CGSetDisplayTransferByTable( 83 | display: CGDirectDisplayID, 84 | tableSize: u32, 85 | redTable: *const CGGammaValue, 86 | greenTable: *const CGGammaValue, 87 | blueTable: *const CGGammaValue, 88 | ) -> CGError; 89 | pub fn CGGetDisplayTransferByTable( 90 | display: CGDirectDisplayID, 91 | capacity: u32, 92 | redTable: *mut CGGammaValue, 93 | greenTable: *mut CGGammaValue, 94 | blueTable: *mut CGGammaValue, 95 | sampleCount: *mut u32, 96 | ) -> CGError; 97 | pub fn CGSetDisplayTransferByByteTable( 98 | display: CGDirectDisplayID, 99 | tableSize: u32, 100 | redTable: *const u8, 101 | greenTable: *const u8, 102 | blueTable: *const u8, 103 | ) -> CGError; 104 | pub fn CGDisplayRestoreColorSyncSettings(); 105 | 106 | pub static kCGDisplayShowDuplicateLowResolutionModes: CFDictionaryRef; 107 | } 108 | 109 | bitflags! { 110 | #[repr(C)] 111 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 112 | pub struct CGCaptureOptions: u32 { 113 | #[doc(alias = "kCGCaptureNoOptions")] 114 | const NoOptions = 0; 115 | #[doc(alias = "kCGCaptureNoFill")] 116 | const NoFill = 1; 117 | } 118 | } 119 | 120 | extern "C" { 121 | pub fn CGDisplayCapture(display: CGDirectDisplayID) -> CGError; 122 | pub fn CGDisplayCaptureWithOptions(display: CGDirectDisplayID, options: CGCaptureOptions) -> CGError; 123 | pub fn CGDisplayRelease(display: CGDirectDisplayID) -> CGError; 124 | pub fn CGCaptureAllDisplays() -> CGError; 125 | pub fn CGCaptureAllDisplaysWithOptions(options: CGCaptureOptions) -> CGError; 126 | pub fn CGReleaseAllDisplays() -> CGError; 127 | #[cfg(feature = "window")] 128 | pub fn CGShieldingWindowID(display: CGDirectDisplayID) -> CGWindowID; 129 | #[cfg(feature = "window")] 130 | pub fn CGShieldingWindowLevel() -> CGWindowLevel; 131 | pub fn CGDisplayCreateImage(display: CGDirectDisplayID) -> CGImageRef; 132 | pub fn CGDisplayCreateImageForRect(display: CGDirectDisplayID, rect: CGRect) -> CGImageRef; 133 | pub fn CGDisplayHideCursor(display: CGDirectDisplayID) -> CGError; 134 | pub fn CGDisplayShowCursor(display: CGDirectDisplayID) -> CGError; 135 | pub fn CGDisplayMoveCursorToPoint(display: CGDirectDisplayID, point: CGPoint) -> CGError; 136 | pub fn CGGetLastMouseDelta(deltaX: *mut i32, deltaY: *mut i32) -> CGError; 137 | pub fn CGDisplayGetDrawingContext(display: CGDirectDisplayID) -> CGContextRef; 138 | } 139 | -------------------------------------------------------------------------------- /core-graphics/src/direct_display_metal.rs: -------------------------------------------------------------------------------- 1 | use metal::MTLDevice; 2 | 3 | extern "C" { 4 | pub fn CGDirectDisplayCopyCurrentMetalDevice(display: u32) -> *mut MTLDevice; 5 | } 6 | -------------------------------------------------------------------------------- /core-graphics/src/display_fade.rs: -------------------------------------------------------------------------------- 1 | use libc::c_float; 2 | 3 | use crate::{base::boolean_t, display_configuration::CGDisplayConfigRef, error::CGError}; 4 | 5 | pub type CGDisplayFadeReservationToken = u32; 6 | 7 | pub const kCGDisplayFadeReservationInvalidToken: CGDisplayFadeReservationToken = 0; 8 | 9 | pub type CGDisplayBlendFraction = c_float; 10 | 11 | pub const kCGDisplayBlendNormal: CGDisplayBlendFraction = 0.0; 12 | pub const kCGDisplayBlendSolidColor: CGDisplayBlendFraction = 1.0; 13 | 14 | pub type CGDisplayFadeInterval = c_float; 15 | 16 | pub type CGDisplayReservationInterval = c_float; 17 | 18 | pub const kCGMaxDisplayReservationInterval: CGDisplayReservationInterval = 15.0; 19 | 20 | extern "C" { 21 | pub fn CGConfigureDisplayFadeEffect( 22 | config: CGDisplayConfigRef, 23 | fadeOutSeconds: CGDisplayFadeInterval, 24 | fadeInSeconds: CGDisplayFadeInterval, 25 | fadeRed: c_float, 26 | fadeGreen: c_float, 27 | fadeBlue: c_float, 28 | ) -> CGError; 29 | pub fn CGAcquireDisplayFadeReservation(seconds: CGDisplayReservationInterval, token: *mut CGDisplayFadeReservationToken) -> CGError; 30 | pub fn CGReleaseDisplayFadeReservation(token: CGDisplayFadeReservationToken) -> CGError; 31 | pub fn CGDisplayFade( 32 | token: CGDisplayFadeReservationToken, 33 | duration: CGDisplayFadeInterval, 34 | startBlend: CGDisplayBlendFraction, 35 | endBlend: CGDisplayBlendFraction, 36 | redBlend: c_float, 37 | greenBlend: c_float, 38 | blueBlend: c_float, 39 | synchronous: boolean_t, 40 | ) -> CGError; 41 | pub fn CGDisplayFadeOperationInProgress() -> boolean_t; 42 | } 43 | -------------------------------------------------------------------------------- /core-graphics/src/error.rs: -------------------------------------------------------------------------------- 1 | #[repr(i32)] 2 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 3 | pub enum CGError { 4 | #[doc(alias = "kCGErrorSuccess")] 5 | Success = 0, 6 | #[doc(alias = "kCGErrorFailure")] 7 | Failure = 1000, 8 | #[doc(alias = "kCGErrorIllegalArgument")] 9 | IllegalArgument = 1001, 10 | #[doc(alias = "kCGErrorInvalidConnection")] 11 | InvalidConnection = 1002, 12 | #[doc(alias = "kCGErrorInvalidContext")] 13 | InvalidContext = 1003, 14 | #[doc(alias = "kCGErrorCannotComplete")] 15 | CannotComplete = 1004, 16 | #[doc(alias = "kCGErrorNotImplemented")] 17 | NotImplemented = 1006, 18 | #[doc(alias = "kCGErrorRangeCheck")] 19 | RangeCheck = 1007, 20 | #[doc(alias = "kCGErrorTypeCheck")] 21 | TypeCheck = 1008, 22 | #[doc(alias = "kCGErrorInvalidOperation")] 23 | InvalidOperation = 1010, 24 | #[doc(alias = "kCGErrorNoneAvailable")] 25 | NoneAvailable = 1011, 26 | } 27 | 28 | pub type CGErrorCallback = extern "C" fn(); 29 | 30 | extern "C" { 31 | pub fn CGErrorSetCallback(callback: CGErrorCallback); 32 | } 33 | -------------------------------------------------------------------------------- /core-graphics/src/event_source.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::{ 2 | base::{CFTypeID, TCFType}, 3 | date::CFTimeInterval, 4 | }; 5 | use libc::c_double; 6 | 7 | use crate::{ 8 | event_types::{CGEventFlags, CGEventSourceKeyboardType, CGEventSourceRef, CGEventSourceStateID, CGEventType, CGMouseButton}, 9 | remote_operation::CGKeyCode, 10 | }; 11 | 12 | extern "C" { 13 | pub fn CGEventSourceGetTypeID() -> CFTypeID; 14 | pub fn CGEventSourceCreate(stateID: CGEventSourceStateID) -> CGEventSourceRef; 15 | pub fn CGEventSourceGetKeyboardType(source: CGEventSourceRef) -> CGEventSourceKeyboardType; 16 | pub fn CGEventSourceSetKeyboardType(source: CGEventSourceRef, keyboardType: CGEventSourceKeyboardType); 17 | pub fn CGEventSourceGetPixelsPerLine(source: CGEventSourceRef) -> c_double; 18 | pub fn CGEventSourceSetPixelsPerLine(source: CGEventSourceRef, pixelsPerLine: c_double); 19 | pub fn CGEventSourceGetSourceStateID(source: CGEventSourceRef) -> CGEventSourceStateID; 20 | pub fn CGEventSourceButtonState(stateID: CGEventSourceStateID, button: CGMouseButton) -> bool; 21 | pub fn CGEventSourceKeyState(stateID: CGEventSourceStateID, key: CGKeyCode) -> bool; 22 | pub fn CGEventSourceFlagsState(stateID: CGEventSourceStateID) -> CGEventFlags; 23 | pub fn CGEventSourceSecondsSinceLastEventType(stateID: CGEventSourceStateID, eventType: CGEventType) -> CFTimeInterval; 24 | pub fn CGEventSourceCounterForEventType(stateID: CGEventSourceStateID, eventType: CGEventType) -> u32; 25 | pub fn CGEventSourceSetUserData(source: CGEventSourceRef, userData: i64); 26 | pub fn CGEventSourceGetUserData(source: CGEventSourceRef) -> i64; 27 | pub fn CGEventSourceSetLocalEventsSuppressionInterval(source: CGEventSourceRef, seconds: CFTimeInterval); 28 | pub fn CGEventSourceGetLocalEventsSuppressionInterval(source: CGEventSourceRef) -> CFTimeInterval; 29 | } 30 | 31 | declare_TCFType!(CGEventSource, CGEventSourceRef); 32 | impl_TCFType!(CGEventSource, CGEventSourceRef, CGEventSourceGetTypeID); 33 | impl_CFTypeDescription!(CGEventSource); 34 | 35 | impl CGEventSource { 36 | pub fn new(state_id: CGEventSourceStateID) -> Result { 37 | unsafe { 38 | let event_source = CGEventSourceCreate(state_id); 39 | if event_source.is_null() { 40 | Err(()) 41 | } else { 42 | Ok(TCFType::wrap_under_create_rule(event_source)) 43 | } 44 | } 45 | } 46 | 47 | pub fn get_keyboard_type(&self) -> CGEventSourceKeyboardType { 48 | unsafe { CGEventSourceGetKeyboardType(self.as_concrete_TypeRef()) } 49 | } 50 | 51 | pub fn set_keyboard_type(&self, keyboard_type: CGEventSourceKeyboardType) { 52 | unsafe { CGEventSourceSetKeyboardType(self.as_concrete_TypeRef(), keyboard_type) } 53 | } 54 | 55 | pub fn get_pixels_per_line(&self) -> c_double { 56 | unsafe { CGEventSourceGetPixelsPerLine(self.as_concrete_TypeRef()) } 57 | } 58 | 59 | pub fn set_pixels_per_line(&self, pixels_per_line: c_double) { 60 | unsafe { CGEventSourceSetPixelsPerLine(self.as_concrete_TypeRef(), pixels_per_line) } 61 | } 62 | 63 | pub fn get_source_state_id(&self) -> CGEventSourceStateID { 64 | unsafe { CGEventSourceGetSourceStateID(self.as_concrete_TypeRef()) } 65 | } 66 | 67 | pub fn button_state(state_id: CGEventSourceStateID, button: CGMouseButton) -> bool { 68 | unsafe { CGEventSourceButtonState(state_id, button) } 69 | } 70 | 71 | pub fn key_state(state_id: CGEventSourceStateID, key: CGKeyCode) -> bool { 72 | unsafe { CGEventSourceKeyState(state_id, key) } 73 | } 74 | 75 | pub fn flags_state(state_id: CGEventSourceStateID) -> CGEventFlags { 76 | unsafe { CGEventSourceFlagsState(state_id) } 77 | } 78 | 79 | pub fn seconds_since_last_event_type(state_id: CGEventSourceStateID, event_type: CGEventType) -> CFTimeInterval { 80 | unsafe { CGEventSourceSecondsSinceLastEventType(state_id, event_type) } 81 | } 82 | 83 | pub fn counter_for_event_type(state_id: CGEventSourceStateID, event_type: CGEventType) -> u32 { 84 | unsafe { CGEventSourceCounterForEventType(state_id, event_type) } 85 | } 86 | 87 | pub fn set_user_data(&self, user_data: i64) { 88 | unsafe { CGEventSourceSetUserData(self.as_concrete_TypeRef(), user_data) } 89 | } 90 | 91 | pub fn get_user_data(&self) -> i64 { 92 | unsafe { CGEventSourceGetUserData(self.as_concrete_TypeRef()) } 93 | } 94 | 95 | pub fn set_local_events_suppression_interval(&self, seconds: CFTimeInterval) { 96 | unsafe { CGEventSourceSetLocalEventsSuppressionInterval(self.as_concrete_TypeRef(), seconds) } 97 | } 98 | 99 | pub fn get_local_events_suppression_interval(&self) -> CFTimeInterval { 100 | unsafe { CGEventSourceGetLocalEventsSuppressionInterval(self.as_concrete_TypeRef()) } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /core-graphics/src/function.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::null; 2 | 3 | use core_foundation::base::{CFTypeID, TCFType}; 4 | use libc::c_void; 5 | 6 | use crate::base::CGFloat; 7 | 8 | #[repr(C)] 9 | pub struct __CGFunction(c_void); 10 | 11 | pub type CGFunctionRef = *const __CGFunction; 12 | 13 | pub type CGFunctionEvaluateCallback = extern "C" fn(*const c_void, *const CGFloat, *mut CGFloat); 14 | pub type CGFunctionReleaseInfoCallback = extern "C" fn(*mut c_void); 15 | 16 | #[repr(C)] 17 | pub struct CGFunctionCallbacks { 18 | pub version: u32, 19 | pub evaluate: CGFunctionEvaluateCallback, 20 | pub releaseInfo: CGFunctionReleaseInfoCallback, 21 | } 22 | 23 | extern "C" { 24 | pub fn CGFunctionGetTypeID() -> CFTypeID; 25 | pub fn CGFunctionCreate( 26 | info: *mut c_void, 27 | domainDimension: usize, 28 | domain: *const CGFloat, 29 | rangeDimension: usize, 30 | range: *const CGFloat, 31 | callbacks: *const CGFunctionCallbacks, 32 | ) -> CGFunctionRef; 33 | pub fn CGFunctionRetain(function: CGFunctionRef) -> CGFunctionRef; 34 | pub fn CGFunctionRelease(function: CGFunctionRef); 35 | } 36 | 37 | pub struct CGFunction(CGFunctionRef); 38 | 39 | impl Drop for CGFunction { 40 | fn drop(&mut self) { 41 | unsafe { CGFunctionRelease(self.0) } 42 | } 43 | } 44 | 45 | impl_TCFType!(CGFunction, CGFunctionRef, CGFunctionGetTypeID); 46 | impl_CFTypeDescription!(CGFunction); 47 | 48 | impl CGFunction { 49 | pub unsafe fn new( 50 | info: *mut c_void, 51 | domain: Option<&[CGFloat]>, 52 | range: Option<&[CGFloat]>, 53 | callbacks: Option<&CGFunctionCallbacks>, 54 | ) -> Option { 55 | let domain_dimension = domain.map_or(0, |d| d.len()); 56 | let domain = domain.map_or(null(), |d| d.as_ptr()); 57 | let range_dimension = range.map_or(0, |r| r.len()); 58 | let range = range.map_or(null(), |r| r.as_ptr()); 59 | let callbacks = callbacks.map_or(null(), |c| c as *const _); 60 | let function = CGFunctionCreate(info, domain_dimension, domain, range_dimension, range, callbacks); 61 | if function.is_null() { 62 | None 63 | } else { 64 | Some(TCFType::wrap_under_create_rule(function)) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /core-graphics/src/gradient.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | array::{CFArray, CFArrayRef}, 5 | base::{CFTypeID, TCFType}, 6 | }; 7 | use libc::{c_void, size_t}; 8 | 9 | use crate::{ 10 | base::CGFloat, 11 | color::CGColor, 12 | color_space::{CGColorSpace, CGColorSpaceRef}, 13 | }; 14 | 15 | #[repr(C)] 16 | pub struct __CGGradient(c_void); 17 | 18 | pub type CGGradientRef = *const __CGGradient; 19 | 20 | bitflags! { 21 | #[repr(C)] 22 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 23 | pub struct CGGradientDrawingOptions: u32 { 24 | #[doc(alias = "kCGGradientDrawsBeforeStartLocation")] 25 | const BeforeStartLocation = (1 << 0); 26 | #[doc(alias = "kCGGradientDrawsAfterEndLocation")] 27 | const AfterEndLocation = (1 << 1); 28 | } 29 | } 30 | 31 | extern "C" { 32 | pub fn CGGradientGetTypeID() -> CFTypeID; 33 | pub fn CGGradientCreateWithColorComponents( 34 | space: CGColorSpaceRef, 35 | components: *const CGFloat, 36 | locations: *const CGFloat, 37 | count: size_t, 38 | ) -> CGGradientRef; 39 | pub fn CGGradientCreateWithColors(space: CGColorSpaceRef, colors: CFArrayRef, locations: *const CGFloat) -> CGGradientRef; 40 | pub fn CGGradientRetain(gradient: CGGradientRef) -> CGGradientRef; 41 | pub fn CGGradientRelease(gradient: CGGradientRef); 42 | } 43 | 44 | pub struct CGGradient(CGGradientRef); 45 | 46 | impl Drop for CGGradient { 47 | fn drop(&mut self) { 48 | unsafe { CGGradientRelease(self.0) } 49 | } 50 | } 51 | 52 | impl_TCFType!(CGGradient, CGGradientRef, CGGradientGetTypeID); 53 | impl_CFTypeDescription!(CGGradient); 54 | 55 | impl CGGradient { 56 | pub fn from_color_components( 57 | color_space: Option<&CGColorSpace>, 58 | components: Option<&[CGFloat]>, 59 | locations: Option<&[CGFloat]>, 60 | ) -> Option { 61 | unsafe { 62 | let gradient = CGGradientCreateWithColorComponents( 63 | color_space.map_or(null_mut(), |cs| cs.as_concrete_TypeRef()), 64 | components.map_or(null(), |c| c.as_ptr()), 65 | locations.map_or(null(), |l| l.as_ptr()), 66 | locations.map_or(0, |l| l.len() as size_t), 67 | ); 68 | if gradient.is_null() { 69 | None 70 | } else { 71 | Some(TCFType::wrap_under_create_rule(gradient)) 72 | } 73 | } 74 | } 75 | 76 | pub fn from_colors(color_space: Option<&CGColorSpace>, colors: Option<&CFArray>, locations: Option<&[CGFloat]>) -> Option { 77 | unsafe { 78 | let gradient = CGGradientCreateWithColors( 79 | color_space.map_or(null_mut(), |cs| cs.as_concrete_TypeRef()), 80 | colors.map_or(null(), |c| c.as_concrete_TypeRef()), 81 | locations.map_or(null(), |l| l.as_ptr()), 82 | ); 83 | if gradient.is_null() { 84 | None 85 | } else { 86 | Some(TCFType::wrap_under_create_rule(gradient)) 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /core-graphics/src/layer.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::null; 2 | 3 | use core_foundation::{ 4 | base::{CFType, CFTypeID, TCFType}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::CFString, 7 | }; 8 | use libc::c_void; 9 | 10 | use crate::{ 11 | context::{CGContext, CGContextRef}, 12 | geometry::{CGPoint, CGRect, CGSize}, 13 | }; 14 | 15 | #[repr(C)] 16 | pub struct __CGLayer(c_void); 17 | 18 | pub type CGLayerRef = *mut __CGLayer; 19 | 20 | extern "C" { 21 | pub fn CGLayerCreateWithContext(context: CGContextRef, size: CGSize, auxiliaryInfo: CFDictionaryRef) -> CGLayerRef; 22 | pub fn CGLayerRetain(layer: CGLayerRef) -> CGLayerRef; 23 | pub fn CGLayerRelease(layer: CGLayerRef); 24 | pub fn CGLayerGetSize(layer: CGLayerRef) -> CGSize; 25 | pub fn CGLayerGetContext(layer: CGLayerRef) -> CGContextRef; 26 | pub fn CGContextDrawLayerInRect(context: CGContextRef, rect: CGRect, layer: CGLayerRef); 27 | pub fn CGContextDrawLayerAtPoint(context: CGContextRef, point: CGPoint, layer: CGLayerRef); 28 | pub fn CGLayerGetTypeID() -> CFTypeID; 29 | } 30 | 31 | pub struct CGLayer(CGLayerRef); 32 | 33 | impl Drop for CGLayer { 34 | fn drop(&mut self) { 35 | unsafe { CGLayerRelease(self.0) } 36 | } 37 | } 38 | 39 | impl_TCFType!(CGLayer, CGLayerRef, CGLayerGetTypeID); 40 | impl_CFTypeDescription!(CGLayer); 41 | 42 | impl CGLayer { 43 | pub fn new_with_context(context: &CGContext, size: CGSize, auxiliary_info: Option<&CFDictionary>) -> Option { 44 | unsafe { 45 | let layer = CGLayerCreateWithContext(context.as_concrete_TypeRef(), size, auxiliary_info.map_or(null(), |ai| ai.as_concrete_TypeRef())); 46 | if layer.is_null() { 47 | None 48 | } else { 49 | Some(CGLayer(layer)) 50 | } 51 | } 52 | } 53 | 54 | pub fn size(&self) -> CGSize { 55 | unsafe { CGLayerGetSize(self.as_concrete_TypeRef()) } 56 | } 57 | 58 | pub fn context(&self) -> Option { 59 | unsafe { 60 | let context = CGLayerGetContext(self.as_concrete_TypeRef()); 61 | if context.is_null() { 62 | None 63 | } else { 64 | Some(TCFType::wrap_under_get_rule(context)) 65 | } 66 | } 67 | } 68 | 69 | pub fn draw_in_rect(&self, context: &CGContext, rect: CGRect) { 70 | unsafe { CGContextDrawLayerInRect(context.as_concrete_TypeRef(), rect, self.as_concrete_TypeRef()) } 71 | } 72 | 73 | pub fn draw_at_point(&self, context: &CGContext, point: CGPoint) { 74 | unsafe { CGContextDrawLayerAtPoint(context.as_concrete_TypeRef(), point, self.as_concrete_TypeRef()) } 75 | } 76 | } 77 | 78 | impl CGContext { 79 | pub fn draw_layer_in_rect(&self, layer: &CGLayer, rect: CGRect) { 80 | layer.draw_in_rect(self, rect) 81 | } 82 | 83 | pub fn draw_layer_at_point(&self, layer: &CGLayer, point: CGPoint) { 84 | layer.draw_at_point(self, point) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /core-graphics/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, improper_ctypes)] 2 | 3 | #[macro_use] 4 | extern crate bitflags; 5 | extern crate block; 6 | #[macro_use] 7 | extern crate cfg_if; 8 | #[macro_use] 9 | extern crate core_foundation; 10 | #[cfg(all(target_os = "macos", feature = "dispatch2"))] 11 | extern crate dispatch2; 12 | #[cfg(all(target_os = "macos", feature = "io-surface"))] 13 | extern crate io_surface; 14 | extern crate libc; 15 | #[cfg(all(target_os = "macos", feature = "metal"))] 16 | extern crate metal; 17 | #[cfg(feature = "objc")] 18 | extern crate objc2; 19 | 20 | #[cfg_attr(feature = "link", link(name = "CoreGraphics", kind = "framework"))] 21 | extern "C" {} 22 | 23 | pub mod affine_transform; 24 | pub mod base; 25 | pub mod bitmap_context; 26 | pub mod color; 27 | pub mod color_conversion_info; 28 | pub mod color_space; 29 | pub mod context; 30 | pub mod data_provider; 31 | cfg_if!( 32 | if #[cfg(all(target_os = "macos", feature = "display"))] { 33 | pub mod direct_display; 34 | pub mod display; 35 | pub mod display_configuration; 36 | pub mod display_fade; 37 | } 38 | ); 39 | #[cfg(all(target_os = "macos", feature = "display", feature = "metal"))] 40 | pub mod direct_display_metal; 41 | #[cfg(all(target_os = "macos", feature = "display-stream", feature = "objc"))] 42 | pub mod display_stream; 43 | pub mod error; 44 | cfg_if!( 45 | if #[cfg(all(target_os = "macos", feature = "event"))] { 46 | pub mod event; 47 | pub mod event_source; 48 | pub mod event_types; 49 | } 50 | ); 51 | pub mod font; 52 | pub mod function; 53 | pub mod geometry; 54 | pub mod gradient; 55 | pub mod image; 56 | pub mod layer; 57 | pub mod path; 58 | pub mod pattern; 59 | #[cfg(all(target_os = "macos", any(feature = "display", feature = "event")))] 60 | pub mod remote_operation; 61 | pub mod shading; 62 | cfg_if!( 63 | if #[cfg(all(target_os = "macos", feature = "window"))] { 64 | pub mod window; 65 | pub mod window_level; 66 | } 67 | ); 68 | -------------------------------------------------------------------------------- /core-graphics/src/pattern.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::base::{CFTypeID, TCFType}; 2 | use libc::c_void; 3 | 4 | use crate::{affine_transform::CGAffineTransform, base::CGFloat, context::CGContextRef, geometry::CGRect}; 5 | 6 | #[repr(C)] 7 | pub struct __CGPattern(c_void); 8 | 9 | pub type CGPatternRef = *const __CGPattern; 10 | 11 | #[repr(i32)] 12 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 13 | pub enum CGPatternTiling { 14 | #[doc(alias = "kCGPatternTilingNoDistortion")] 15 | NoDistortion, 16 | #[doc(alias = "kCGPatternTilingConstantSpacingMinimalDistortion")] 17 | ConstantSpacingMinimalDistortion, 18 | #[doc(alias = "kCGPatternTilingConstantSpacing")] 19 | ConstantSpacing, 20 | } 21 | 22 | pub type CGPatternDrawPatternCallback = extern "C" fn(*mut c_void, CGContextRef); 23 | pub type CGPatternReleaseInfoCallback = extern "C" fn(*mut c_void); 24 | 25 | pub struct CGPatternCallbacks { 26 | pub version: u32, 27 | pub drawPattern: CGPatternDrawPatternCallback, 28 | pub releaseInfo: CGPatternReleaseInfoCallback, 29 | } 30 | 31 | extern "C" { 32 | pub fn CGPatternGetTypeID() -> CFTypeID; 33 | pub fn CGPatternCreate( 34 | info: *mut c_void, 35 | bounds: CGRect, 36 | matrix: CGAffineTransform, 37 | xStep: CGFloat, 38 | yStep: CGFloat, 39 | tiling: CGPatternTiling, 40 | isColored: i32, 41 | callbacks: *const CGPatternCallbacks, 42 | ) -> CGPatternRef; 43 | pub fn CGPatternRetain(pattern: CGPatternRef) -> CGPatternRef; 44 | pub fn CGPatternRelease(pattern: CGPatternRef); 45 | } 46 | 47 | pub struct CGPattern(CGPatternRef); 48 | 49 | impl Drop for CGPattern { 50 | fn drop(&mut self) { 51 | unsafe { CGPatternRelease(self.0) } 52 | } 53 | } 54 | 55 | impl_TCFType!(CGPattern, CGPatternRef, CGPatternGetTypeID); 56 | impl_CFTypeDescription!(CGPattern); 57 | 58 | impl CGPattern { 59 | pub unsafe fn new( 60 | info: *mut c_void, 61 | bounds: CGRect, 62 | matrix: CGAffineTransform, 63 | xStep: CGFloat, 64 | yStep: CGFloat, 65 | tiling: CGPatternTiling, 66 | is_colored: i32, 67 | callbacks: Option<&CGPatternCallbacks>, 68 | ) -> Self { 69 | unsafe { 70 | let pattern = 71 | CGPatternCreate(info, bounds, matrix, xStep, yStep, tiling, is_colored, callbacks.map_or(std::ptr::null(), |c| c as *const _)); 72 | TCFType::wrap_under_create_rule(pattern) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /core-graphics/src/remote_operation.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::mach_port::CFMachPortRef; 2 | use libc::boolean_t; 3 | 4 | use crate::{error::CGError, geometry::CGPoint}; 5 | 6 | pub type CGKeyCode = u16; 7 | 8 | #[repr(C)] 9 | pub struct KeyCode; 10 | impl KeyCode { 11 | pub const A: CGKeyCode = 0x00; 12 | pub const S: CGKeyCode = 0x01; 13 | pub const D: CGKeyCode = 0x02; 14 | pub const F: CGKeyCode = 0x03; 15 | pub const H: CGKeyCode = 0x04; 16 | pub const G: CGKeyCode = 0x05; 17 | pub const Z: CGKeyCode = 0x06; 18 | pub const X: CGKeyCode = 0x07; 19 | pub const C: CGKeyCode = 0x08; 20 | pub const V: CGKeyCode = 0x09; 21 | pub const B: CGKeyCode = 0x0B; 22 | pub const Q: CGKeyCode = 0x0C; 23 | pub const W: CGKeyCode = 0x0D; 24 | pub const E: CGKeyCode = 0x0E; 25 | pub const R: CGKeyCode = 0x0F; 26 | pub const Y: CGKeyCode = 0x10; 27 | pub const T: CGKeyCode = 0x11; 28 | pub const One: CGKeyCode = 0x12; 29 | pub const Two: CGKeyCode = 0x13; 30 | pub const Three: CGKeyCode = 0x14; 31 | pub const Four: CGKeyCode = 0x15; 32 | pub const Six: CGKeyCode = 0x16; 33 | pub const Five: CGKeyCode = 0x17; 34 | pub const Equal: CGKeyCode = 0x18; 35 | pub const Nine: CGKeyCode = 0x19; 36 | pub const Seven: CGKeyCode = 0x1A; 37 | pub const Minus: CGKeyCode = 0x1B; 38 | pub const Eight: CGKeyCode = 0x1C; 39 | pub const Zero: CGKeyCode = 0x1D; 40 | pub const RightBracket: CGKeyCode = 0x1E; 41 | pub const O: CGKeyCode = 0x1F; 42 | pub const U: CGKeyCode = 0x20; 43 | pub const LeftBracket: CGKeyCode = 0x21; 44 | pub const I: CGKeyCode = 0x22; 45 | pub const P: CGKeyCode = 0x23; 46 | pub const Return: CGKeyCode = 0x24; 47 | pub const L: CGKeyCode = 0x25; 48 | pub const J: CGKeyCode = 0x26; 49 | pub const Quote: CGKeyCode = 0x27; 50 | pub const K: CGKeyCode = 0x28; 51 | pub const Semicolon: CGKeyCode = 0x29; 52 | pub const Backslash: CGKeyCode = 0x2A; 53 | pub const Comma: CGKeyCode = 0x2B; 54 | pub const Slash: CGKeyCode = 0x2C; 55 | pub const N: CGKeyCode = 0x2D; 56 | pub const M: CGKeyCode = 0x2E; 57 | pub const Period: CGKeyCode = 0x2F; 58 | pub const Tab: CGKeyCode = 0x30; 59 | pub const Space: CGKeyCode = 0x31; 60 | pub const Grave: CGKeyCode = 0x32; 61 | pub const Delete: CGKeyCode = 0x33; 62 | pub const Escape: CGKeyCode = 0x35; 63 | pub const RightCommand: CGKeyCode = 0x36; 64 | pub const Command: CGKeyCode = 0x37; 65 | pub const Shift: CGKeyCode = 0x38; 66 | pub const CapsLock: CGKeyCode = 0x39; 67 | pub const Option: CGKeyCode = 0x3A; 68 | pub const Control: CGKeyCode = 0x3B; 69 | pub const RightShift: CGKeyCode = 0x3C; 70 | pub const RightOption: CGKeyCode = 0x3D; 71 | pub const RightControl: CGKeyCode = 0x3E; 72 | pub const Function: CGKeyCode = 0x3F; 73 | pub const F17: CGKeyCode = 0x40; 74 | pub const KeypadDecimal: CGKeyCode = 0x41; 75 | pub const KeypadMultiply: CGKeyCode = 0x43; 76 | pub const KeypadPlus: CGKeyCode = 0x45; 77 | pub const KeypadClear: CGKeyCode = 0x47; 78 | pub const VolumeUp: CGKeyCode = 0x48; 79 | pub const VolumeDown: CGKeyCode = 0x49; 80 | pub const Mute: CGKeyCode = 0x4A; 81 | pub const KeypadDivide: CGKeyCode = 0x4B; 82 | pub const KeypadEnter: CGKeyCode = 0x4C; 83 | pub const KeypadMinus: CGKeyCode = 0x4E; 84 | pub const F18: CGKeyCode = 0x4F; 85 | pub const F19: CGKeyCode = 0x50; 86 | pub const KeypadEquals: CGKeyCode = 0x51; 87 | pub const Keypad0: CGKeyCode = 0x52; 88 | pub const Keypad1: CGKeyCode = 0x53; 89 | pub const Keypad2: CGKeyCode = 0x54; 90 | pub const Keypad3: CGKeyCode = 0x55; 91 | pub const Keypad4: CGKeyCode = 0x56; 92 | pub const Keypad5: CGKeyCode = 0x57; 93 | pub const Keypad6: CGKeyCode = 0x58; 94 | pub const Keypad7: CGKeyCode = 0x59; 95 | pub const F20: CGKeyCode = 0x5A; 96 | pub const Keypad8: CGKeyCode = 0x5B; 97 | pub const Keypad9: CGKeyCode = 0x5C; 98 | pub const F5: CGKeyCode = 0x60; 99 | pub const F6: CGKeyCode = 0x61; 100 | pub const F7: CGKeyCode = 0x62; 101 | pub const F3: CGKeyCode = 0x63; 102 | pub const F8: CGKeyCode = 0x64; 103 | pub const F9: CGKeyCode = 0x65; 104 | pub const F11: CGKeyCode = 0x67; 105 | pub const F13: CGKeyCode = 0x69; 106 | pub const F16: CGKeyCode = 0x6A; 107 | pub const F14: CGKeyCode = 0x6B; 108 | pub const F10: CGKeyCode = 0x6D; 109 | pub const F12: CGKeyCode = 0x6F; 110 | pub const F15: CGKeyCode = 0x71; 111 | pub const Help: CGKeyCode = 0x72; 112 | pub const Home: CGKeyCode = 0x73; 113 | pub const PageUp: CGKeyCode = 0x74; 114 | pub const ForwardDelete: CGKeyCode = 0x75; 115 | pub const F4: CGKeyCode = 0x76; 116 | pub const End: CGKeyCode = 0x77; 117 | pub const F2: CGKeyCode = 0x78; 118 | pub const PageDown: CGKeyCode = 0x79; 119 | pub const F1: CGKeyCode = 0x7A; 120 | pub const LeftArrow: CGKeyCode = 0x7B; 121 | pub const RightArrow: CGKeyCode = 0x7C; 122 | pub const DownArrow: CGKeyCode = 0x7D; 123 | pub const UpArrow: CGKeyCode = 0x7E; 124 | } 125 | 126 | extern "C" { 127 | pub fn CGWarpMouseCursorPosition(newCursorPosition: CGPoint) -> CGError; 128 | pub fn CGAssociateMouseAndMouseCursorPosition(connected: boolean_t) -> CGError; 129 | pub fn CGWindowServerCreateServerPort() -> CFMachPortRef; 130 | } 131 | -------------------------------------------------------------------------------- /core-graphics/src/shading.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::base::{CFTypeID, TCFType}; 4 | use libc::c_void; 5 | 6 | use crate::{ 7 | color_space::{CGColorSpace, CGColorSpaceRef}, 8 | function::{CGFunction, CGFunctionRef}, 9 | geometry::CGPoint, 10 | }; 11 | 12 | #[repr(C)] 13 | pub struct __CGShading(c_void); 14 | 15 | pub type CGShadingRef = *mut __CGShading; 16 | 17 | extern "C" { 18 | pub fn CGShadingGetTypeID() -> CFTypeID; 19 | pub fn CGShadingCreateAxial( 20 | space: CGColorSpaceRef, 21 | start: CGPoint, 22 | end: CGPoint, 23 | function: CGFunctionRef, 24 | extendStart: bool, 25 | extendEnd: bool, 26 | ) -> CGShadingRef; 27 | pub fn CGShadingCreateRadial( 28 | space: CGColorSpaceRef, 29 | start: CGPoint, 30 | startRadius: f64, 31 | end: CGPoint, 32 | endRadius: f64, 33 | function: CGFunctionRef, 34 | extendStart: bool, 35 | extendEnd: bool, 36 | ) -> CGShadingRef; 37 | pub fn CGShadingRetain(shading: CGShadingRef) -> CGShadingRef; 38 | pub fn CGShadingRelease(shading: CGShadingRef); 39 | } 40 | 41 | pub struct CGShading(CGShadingRef); 42 | 43 | impl Drop for CGShading { 44 | fn drop(&mut self) { 45 | unsafe { CGShadingRelease(self.0) } 46 | } 47 | } 48 | 49 | impl_TCFType!(CGShading, CGShadingRef, CGShadingGetTypeID); 50 | impl_CFTypeDescription!(CGShading); 51 | 52 | impl CGShading { 53 | pub fn new_axial( 54 | space: Option<&CGColorSpace>, 55 | start: CGPoint, 56 | end: CGPoint, 57 | function: Option<&CGFunction>, 58 | extend_start: bool, 59 | extend_end: bool, 60 | ) -> Option { 61 | unsafe { 62 | let shading = CGShadingCreateAxial( 63 | space.map_or(null_mut(), |s| s.as_concrete_TypeRef()), 64 | start, 65 | end, 66 | function.map_or(null(), |f| f.as_concrete_TypeRef()), 67 | extend_start, 68 | extend_end, 69 | ); 70 | if shading.is_null() { 71 | None 72 | } else { 73 | Some(TCFType::wrap_under_create_rule(shading)) 74 | } 75 | } 76 | } 77 | 78 | pub fn new_radial( 79 | space: Option<&CGColorSpace>, 80 | start: CGPoint, 81 | start_radius: f64, 82 | end: CGPoint, 83 | end_radius: f64, 84 | function: Option<&CGFunction>, 85 | extend_start: bool, 86 | extend_end: bool, 87 | ) -> Option { 88 | unsafe { 89 | let shading = CGShadingCreateRadial( 90 | space.map_or(null_mut(), |s| s.as_concrete_TypeRef()), 91 | start, 92 | start_radius, 93 | end, 94 | end_radius, 95 | function.map_or(null(), |f| f.as_concrete_TypeRef()), 96 | extend_start, 97 | extend_end, 98 | ); 99 | if shading.is_null() { 100 | None 101 | } else { 102 | Some(TCFType::wrap_under_create_rule(shading)) 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /core-graphics/src/window.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::{ 2 | array::{CFArray, CFArrayRef}, 3 | base::{CFType, TCFType}, 4 | dictionary::CFDictionary, 5 | string::{CFString, CFStringRef}, 6 | }; 7 | 8 | use crate::{ 9 | geometry::CGRect, 10 | image::{CGImage, CGImageRef}, 11 | }; 12 | 13 | pub type CGWindowID = u32; 14 | 15 | #[repr(u32)] 16 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 17 | pub enum CGWindowSharingType { 18 | #[doc(alias = "kCGWindowSharingNone")] 19 | None = 0, 20 | #[doc(alias = "kCGWindowSharingReadOnly")] 21 | ReadOnly = 1, 22 | #[doc(alias = "kCGWindowSharingReadWrite")] 23 | ReadWrite = 2, 24 | } 25 | 26 | #[repr(u32)] 27 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 28 | pub enum CGWindowBackingType { 29 | #[doc(alias = "kCGWindowBackingStoreRetained")] 30 | Retained = 0, 31 | #[doc(alias = "kCGWindowBackingStoreNonretained")] 32 | Nonretained = 1, 33 | #[doc(alias = "kCGWindowBackingStoreBuffered")] 34 | Buffered = 2, 35 | } 36 | 37 | bitflags! { 38 | #[repr(C)] 39 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 40 | pub struct CGWindowListOption: u32 { 41 | #[doc(alias = "kCGWindowListOptionAll")] 42 | const All = 0; 43 | #[doc(alias = "kCGWindowListOptionOnScreenOnly")] 44 | const OnScreenOnly = 1 << 0; 45 | #[doc(alias = "kCGWindowListOptionOnScreenAboveWindow")] 46 | const OnScreenAboveWindow = 1 << 1; 47 | #[doc(alias = "kCGWindowListOptionOnScreenBelowWindow")] 48 | const OnScreenBelowWindow = 1 << 2; 49 | #[doc(alias = "kCGWindowListOptionIncludingWindow")] 50 | const IncludingWindow = 1 << 3; 51 | #[doc(alias = "kCGWindowListExcludeDesktopElements")] 52 | const ExcludeDesktopElements = 1 << 4; 53 | } 54 | } 55 | 56 | bitflags! { 57 | #[repr(C)] 58 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 59 | pub struct CGWindowImageOption: u32 { 60 | #[doc(alias = "kCGWindowImageDefault")] 61 | const Default = 0; 62 | #[doc(alias = "kCGWindowImageBoundsIgnoreFraming")] 63 | const BoundsIgnoreFraming = 1 << 0; 64 | #[doc(alias = "kCGWindowImageShouldBeOpaque")] 65 | const ShouldBeOpaque = 1 << 1; 66 | #[doc(alias = "kCGWindowImageOnlyShadows")] 67 | const OnlyShadows = 1 << 2; 68 | #[doc(alias = "kCGWindowImageBestResolution")] 69 | const BestResolution = 1 << 3; 70 | #[doc(alias = "kCGWindowImageNominalResolution")] 71 | const NominalResolution = 1 << 4; 72 | } 73 | } 74 | 75 | pub const kCGNullWindowID: CGWindowID = 0; 76 | 77 | extern "C" { 78 | pub static kCGWindowNumber: CFStringRef; 79 | pub static kCGWindowStoreType: CFStringRef; 80 | pub static kCGWindowLayer: CFStringRef; 81 | pub static kCGWindowBounds: CFStringRef; 82 | pub static kCGWindowSharingState: CFStringRef; 83 | pub static kCGWindowAlpha: CFStringRef; 84 | pub static kCGWindowOwnerPID: CFStringRef; 85 | pub static kCGWindowMemoryUsage: CFStringRef; 86 | pub static kCGWindowWorkspace: CFStringRef; 87 | pub static kCGWindowOwnerName: CFStringRef; 88 | pub static kCGWindowName: CFStringRef; 89 | pub static kCGWindowIsOnscreen: CFStringRef; 90 | pub static kCGWindowBackingLocationVideoMemory: CFStringRef; 91 | 92 | pub fn CGWindowListCopyWindowInfo(option: CGWindowListOption, relativeToWindow: CGWindowID) -> CFArrayRef; 93 | pub fn CGWindowListCreate(option: CGWindowListOption, relativeToWindow: CGWindowID) -> CFArrayRef; 94 | pub fn CGWindowListCreateDescriptionFromArray(windowArray: CFArrayRef) -> CFArrayRef; 95 | pub fn CGWindowListCreateImage( 96 | screenBounds: CGRect, 97 | listOption: CGWindowListOption, 98 | windowID: CGWindowID, 99 | imageOption: CGWindowImageOption, 100 | ) -> CGImageRef; 101 | pub fn CGWindowListCreateImageFromArray(screenBounds: CGRect, windowArray: CFArrayRef, imageOption: CGWindowImageOption) -> CGImageRef; 102 | } 103 | 104 | extern "C" { 105 | pub fn CGRequestScreenCaptureAccess() -> bool; 106 | pub fn CGPreflightScreenCaptureAccess() -> bool; 107 | } 108 | 109 | pub fn copy_window_info(option: CGWindowListOption, relative_to_window: CGWindowID) -> Option { 110 | unsafe { 111 | let array = CGWindowListCopyWindowInfo(option, relative_to_window); 112 | if array.is_null() { 113 | None 114 | } else { 115 | Some(TCFType::wrap_under_create_rule(array)) 116 | } 117 | } 118 | } 119 | 120 | pub fn new_window_list(option: CGWindowListOption, relative_to_window: CGWindowID) -> Option> { 121 | unsafe { 122 | let array = CGWindowListCreate(option, relative_to_window); 123 | if array.is_null() { 124 | None 125 | } else { 126 | Some(TCFType::wrap_under_create_rule(array)) 127 | } 128 | } 129 | } 130 | 131 | pub fn new_description_from_array(window_array: CFArray) -> Option>> { 132 | unsafe { 133 | let array = CGWindowListCreateDescriptionFromArray(window_array.as_concrete_TypeRef()); 134 | if array.is_null() { 135 | None 136 | } else { 137 | Some(TCFType::wrap_under_create_rule(array)) 138 | } 139 | } 140 | } 141 | 142 | pub fn new_image( 143 | screen_bounds: CGRect, 144 | list_option: CGWindowListOption, 145 | window_id: CGWindowID, 146 | image_option: CGWindowImageOption, 147 | ) -> Option { 148 | unsafe { 149 | let image = CGWindowListCreateImage(screen_bounds, list_option, window_id, image_option); 150 | if image.is_null() { 151 | None 152 | } else { 153 | Some(TCFType::wrap_under_create_rule(image)) 154 | } 155 | } 156 | } 157 | 158 | pub fn new_image_from_array(screen_bounds: CGRect, window_array: CFArray, image_option: CGWindowImageOption) -> Option { 159 | unsafe { 160 | let image = CGWindowListCreateImageFromArray(screen_bounds, window_array.as_concrete_TypeRef(), image_option); 161 | if image.is_null() { 162 | None 163 | } else { 164 | Some(TCFType::wrap_under_create_rule(image)) 165 | } 166 | } 167 | } 168 | 169 | pub fn request_screen_capture_access() -> bool { 170 | unsafe { CGRequestScreenCaptureAccess() } 171 | } 172 | 173 | pub fn preflight_screen_capture_access() -> bool { 174 | unsafe { CGPreflightScreenCaptureAccess() } 175 | } 176 | 177 | pub enum WindowKeys { 178 | Number, 179 | StoreType, 180 | Layer, 181 | Bounds, 182 | SharingState, 183 | Alpha, 184 | OwnerPID, 185 | MemoryUsage, 186 | Workspace, 187 | OwnerName, 188 | Name, 189 | IsOnscreen, 190 | BackingLocationVideoMemory, 191 | } 192 | 193 | impl From for CFStringRef { 194 | fn from(key: WindowKeys) -> Self { 195 | unsafe { 196 | match key { 197 | WindowKeys::Number => kCGWindowNumber, 198 | WindowKeys::StoreType => kCGWindowStoreType, 199 | WindowKeys::Layer => kCGWindowLayer, 200 | WindowKeys::Bounds => kCGWindowBounds, 201 | WindowKeys::SharingState => kCGWindowSharingState, 202 | WindowKeys::Alpha => kCGWindowAlpha, 203 | WindowKeys::OwnerPID => kCGWindowOwnerPID, 204 | WindowKeys::MemoryUsage => kCGWindowMemoryUsage, 205 | WindowKeys::Workspace => kCGWindowWorkspace, 206 | WindowKeys::OwnerName => kCGWindowOwnerName, 207 | WindowKeys::Name => kCGWindowName, 208 | WindowKeys::IsOnscreen => kCGWindowIsOnscreen, 209 | WindowKeys::BackingLocationVideoMemory => kCGWindowBackingLocationVideoMemory, 210 | } 211 | } 212 | } 213 | } 214 | 215 | impl From for CFString { 216 | fn from(key: WindowKeys) -> Self { 217 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /core-graphics/src/window_level.rs: -------------------------------------------------------------------------------- 1 | #[repr(i32)] 2 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 3 | pub enum CGWindowLevelKey { 4 | #[doc(alias = "kCGBaseWindowLevelKey")] 5 | Base = 0, 6 | #[doc(alias = "kCGMinimumWindowLevelKey")] 7 | Minimum = 1, 8 | #[doc(alias = "kCGDesktopWindowLevelKey")] 9 | Desktop = 2, 10 | #[doc(alias = "kCGBackstopMenuLevelKey")] 11 | BackstopMenu = 3, 12 | #[doc(alias = "kCGNormalWindowLevelKey")] 13 | Normal = 4, 14 | #[doc(alias = "kCGFloatingWindowLevelKey")] 15 | Floating = 5, 16 | #[doc(alias = "kCGTornOffMenuWindowLevelKey")] 17 | TornOffMenu = 6, 18 | #[doc(alias = "kCGDockWindowLevelKey")] 19 | Dock = 7, 20 | #[doc(alias = "kCGMainMenuWindowLevelKey")] 21 | MainMenu = 8, 22 | #[doc(alias = "kCGStatusWindowLevelKey")] 23 | Status = 9, 24 | #[doc(alias = "kCGModalPanelWindowLevelKey")] 25 | ModalPanel = 10, 26 | #[doc(alias = "kCGPopUpMenuWindowLevelKey")] 27 | PopUpMenu = 11, 28 | #[doc(alias = "kCGDraggingWindowLevelKey")] 29 | Dragging = 12, 30 | #[doc(alias = "kCGScreenSaverWindowLevelKey")] 31 | ScreenSaver = 13, 32 | #[doc(alias = "kCGMaximumWindowLevelKey")] 33 | Maximum = 14, 34 | #[doc(alias = "kCGOverlayWindowLevelKey")] 35 | Overlay = 15, 36 | #[doc(alias = "kCGHelpWindowLevelKey")] 37 | Help = 16, 38 | #[doc(alias = "kCGUtilityWindowLevelKey")] 39 | Utility = 17, 40 | #[doc(alias = "kCGDesktopIconWindowLevelKey")] 41 | DesktopIcon = 18, 42 | #[doc(alias = "kCGCursorWindowLevelKey")] 43 | Cursor = 19, 44 | #[doc(alias = "kCGAssistiveTechHighWindowLevelKey")] 45 | AssistiveTechHigh = 20, 46 | #[doc(alias = "kCGNumberOfWindowLevelKeys")] 47 | NumberOfKeys = 21, 48 | } 49 | 50 | pub type CGWindowLevel = i32; 51 | 52 | extern "C" { 53 | pub fn CGWindowLevelForKey(key: CGWindowLevelKey) -> CGWindowLevel; 54 | } 55 | 56 | pub const kCGNumReservedWindowLevels: i32 = 16; 57 | pub const kCGNumReservedBaseWindowLevels: i32 = 5; 58 | 59 | pub const kCGBaseWindowLevel: CGWindowLevel = i32::MIN; 60 | pub const kCGMinimumWindowLevel: CGWindowLevel = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels; 61 | pub const kCGMaximumWindowLevel: CGWindowLevel = i32::MAX - kCGNumReservedWindowLevels; 62 | 63 | pub const kCGDesktopWindowLevel: CGWindowLevel = kCGMinimumWindowLevel + 20; 64 | pub const kCGDesktopIconWindowLevel: CGWindowLevel = kCGDesktopWindowLevel + 1; 65 | pub const kCGBackstopMenuLevel: CGWindowLevel = -20; 66 | pub const kCGNormalWindowLevel: CGWindowLevel = 0; 67 | pub const kCGFloatingWindowLevel: CGWindowLevel = 3; 68 | pub const kCGTornOffMenuWindowLevel: CGWindowLevel = 3; 69 | pub const kCGModalPanelWindowLevel: CGWindowLevel = 8; 70 | pub const kCGUtilityWindowLevel: CGWindowLevel = 19; 71 | pub const kCGDockWindowLevel: CGWindowLevel = 20; 72 | pub const kCGMainMenuWindowLevel: CGWindowLevel = 24; 73 | pub const kCGStatusWindowLevel: CGWindowLevel = 25; 74 | pub const kCGPopUpMenuWindowLevel: CGWindowLevel = 101; 75 | pub const kCGOverlayWindowLevel: CGWindowLevel = 102; 76 | pub const kCGHelpWindowLevel: CGWindowLevel = 200; 77 | pub const kCGDraggingWindowLevel: CGWindowLevel = 500; 78 | pub const kCGScreenSaverWindowLevel: CGWindowLevel = 1000; 79 | pub const kCGAssistiveTechHighWindowLevel: CGWindowLevel = 1500; 80 | pub const kCGCursorWindowLevel: CGWindowLevel = kCGMaximumWindowLevel - 1; 81 | 82 | pub fn window_level_for_key(key: CGWindowLevelKey) -> CGWindowLevel { 83 | unsafe { CGWindowLevelForKey(key) } 84 | } 85 | -------------------------------------------------------------------------------- /core-media/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "core-media" 3 | description = "Safe bindings to CoreMedia framework" 4 | homepage = "https://github.com/rust-media/apple-media-rs" 5 | repository = "https://github.com/rust-media/apple-media-rs" 6 | documentation = "https://docs.rs/core-media" 7 | version = "0.5.1" 8 | authors = ["Zhou Wei "] 9 | license = "MIT OR Apache-2.0" 10 | categories = ["multimedia", "os::macos-apis"] 11 | keywords = ["coremedia"] 12 | edition = "2018" 13 | 14 | [dependencies] 15 | block = "0.1" 16 | cfg-if = "1.0" 17 | core-audio-types = { version = "0.1", path = "../core-audio-types", default-features = false} 18 | core-foundation = { version = "0.10", default-features = false } 19 | core-graphics2 = { version = "0.4", path = "../core-graphics", default-features = false } 20 | core-video = { version = "0.4", path = "../core-video", default-features = false } 21 | libc = "0.2" 22 | objc2 = { version = "0.5", optional = true } 23 | 24 | [features] 25 | default = ["link"] 26 | link = ["core-foundation/link", "core-graphics2/link", "core-video/link"] 27 | objc = ["objc2"] 28 | 29 | [package.metadata.docs.rs] 30 | no-default-features = true 31 | features = ["objc"] 32 | default-target = "x86_64-apple-darwin" 33 | targets = [ 34 | "x86_64-apple-darwin", 35 | "x86_64-apple-ios", 36 | "aarch64-apple-darwin", 37 | "aarch64-apple-ios", 38 | ] 39 | -------------------------------------------------------------------------------- /core-media/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /core-media/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /core-media/README.md: -------------------------------------------------------------------------------- 1 | # core-media 2 | 3 | [![Version](https://img.shields.io/crates/v/core-media)](https://crates.io/crates/core-media) 4 | [![Documentation](https://docs.rs/core-media/badge.svg)](https://docs.rs/core-media) 5 | [![License](https://img.shields.io/badge/License-Apache%202-blue.svg)](LICENSE-APACHE) 6 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE-MIT) 7 | 8 | Rust safe bindings to CoreMedia framework 9 | -------------------------------------------------------------------------------- /core-media/src/attachment.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use core_foundation::{ 4 | base::{kCFAllocatorDefault, CFAllocatorRef, CFGetTypeID, CFRetain, CFType, CFTypeID, CFTypeRef, TCFType, TCFTypeRef}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::{CFString, CFStringRef}, 7 | }; 8 | 9 | pub type CMAttachmentBearerRef = CFTypeRef; 10 | 11 | pub type CMAttachmentMode = u32; 12 | 13 | pub const kCMAttachmentMode_ShouldNotPropagate: CMAttachmentMode = 0; 14 | pub const kCMAttachmentMode_ShouldPropagate: CMAttachmentMode = 1; 15 | 16 | extern "C" { 17 | pub fn CMSetAttachment(target: CMAttachmentBearerRef, key: CFStringRef, value: CFTypeRef, attachmentMode: CMAttachmentMode); 18 | pub fn CMGetAttachment(target: CMAttachmentBearerRef, key: CFStringRef, attachmentModeOut: *mut CMAttachmentMode) -> CFTypeRef; 19 | pub fn CMRemoveAttachment(target: CMAttachmentBearerRef, key: CFStringRef); 20 | pub fn CMRemoveAllAttachments(target: CMAttachmentBearerRef); 21 | pub fn CMCopyDictionaryOfAttachments( 22 | allocator: CFAllocatorRef, 23 | target: CMAttachmentBearerRef, 24 | attachmentMode: CMAttachmentMode, 25 | ) -> CFDictionaryRef; 26 | pub fn CMSetAttachments(target: CMAttachmentBearerRef, theAttachments: CFDictionaryRef, attachmentMode: CMAttachmentMode); 27 | pub fn CMPropagateAttachments(source: CMAttachmentBearerRef, destination: CMAttachmentBearerRef); 28 | } 29 | 30 | pub trait CMAttachmentBearerSubClass: TCFType { 31 | #[inline] 32 | fn as_CMAttachmentBearer(&self) -> CMAttachmentBearer { 33 | unsafe { CMAttachmentBearer::wrap_under_get_rule(self.as_concrete_TypeRef().as_void_ptr()) } 34 | } 35 | 36 | #[inline] 37 | fn into_CMAttachmentBearer(self) -> CMAttachmentBearer 38 | where 39 | Self: Sized, 40 | { 41 | let reference = self.as_concrete_TypeRef().as_void_ptr(); 42 | mem::forget(self); 43 | unsafe { CMAttachmentBearer::wrap_under_create_rule(reference) } 44 | } 45 | } 46 | 47 | declare_TCFType! { 48 | CMAttachmentBearer, CMAttachmentBearerRef 49 | } 50 | impl_CFTypeDescription!(CMAttachmentBearer); 51 | 52 | impl CMAttachmentBearer { 53 | #[inline] 54 | pub fn as_concrete_TypeRef(&self) -> CMAttachmentBearerRef { 55 | self.0 56 | } 57 | 58 | #[inline] 59 | pub fn as_CFType(&self) -> CFType { 60 | unsafe { CFType::wrap_under_get_rule(self.as_concrete_TypeRef()) } 61 | } 62 | 63 | #[inline] 64 | pub fn as_CFTypeRef(&self) -> CFTypeRef { 65 | self.as_concrete_TypeRef() as CFTypeRef 66 | } 67 | 68 | #[inline] 69 | pub fn into_CFType(self) -> CFType 70 | where 71 | Self: Sized, 72 | { 73 | let reference = self.as_CFTypeRef(); 74 | mem::forget(self); 75 | unsafe { CFType::wrap_under_create_rule(reference) } 76 | } 77 | 78 | #[inline] 79 | pub unsafe fn wrap_under_create_rule(reference: CMAttachmentBearerRef) -> Self { 80 | CMAttachmentBearer(reference) 81 | } 82 | 83 | #[inline] 84 | pub unsafe fn wrap_under_get_rule(reference: CMAttachmentBearerRef) -> Self { 85 | let reference = CFRetain(reference); 86 | CMAttachmentBearer(reference) 87 | } 88 | 89 | #[inline] 90 | pub fn type_of(&self) -> CFTypeID { 91 | unsafe { CFGetTypeID(self.as_CFTypeRef()) } 92 | } 93 | 94 | #[inline] 95 | pub fn instance_of(&self) -> bool { 96 | self.type_of() == T::type_id() 97 | } 98 | } 99 | 100 | impl Clone for CMAttachmentBearer { 101 | #[inline] 102 | fn clone(&self) -> Self { 103 | unsafe { CMAttachmentBearer::wrap_under_get_rule(self.as_concrete_TypeRef()) } 104 | } 105 | } 106 | 107 | impl PartialEq for CMAttachmentBearer { 108 | #[inline] 109 | fn eq(&self, other: &Self) -> bool { 110 | self.as_CFType().eq(&other.as_CFType()) 111 | } 112 | } 113 | 114 | impl Eq for CMAttachmentBearer {} 115 | 116 | impl CMAttachmentBearer { 117 | #[inline] 118 | pub fn downcast(&self) -> Option { 119 | if self.instance_of::() { 120 | Some(unsafe { T::wrap_under_get_rule(T::Ref::from_void_ptr(self.as_concrete_TypeRef())) }) 121 | } else { 122 | None 123 | } 124 | } 125 | 126 | #[inline] 127 | pub fn downcast_into(self) -> Option { 128 | if self.instance_of::() { 129 | unsafe { 130 | let reference = T::Ref::from_void_ptr(self.as_concrete_TypeRef()); 131 | mem::forget(self); 132 | Some(T::wrap_under_create_rule(reference)) 133 | } 134 | } else { 135 | None 136 | } 137 | } 138 | } 139 | 140 | impl CMAttachmentBearer { 141 | #[inline] 142 | pub fn set_attachment(&self, key: &CFString, value: &CFType, attachment_mode: CMAttachmentMode) { 143 | unsafe { CMSetAttachment(self.as_concrete_TypeRef(), key.as_concrete_TypeRef(), value.as_CFTypeRef(), attachment_mode) } 144 | } 145 | 146 | #[inline] 147 | pub fn get_attachment(&self, key: &CFString) -> Option<(CFType, CMAttachmentMode)> { 148 | unsafe { 149 | let mut attachment_mode = 0; 150 | let value = CMGetAttachment(self.as_concrete_TypeRef(), key.as_concrete_TypeRef(), &mut attachment_mode); 151 | if value.is_null() { 152 | None 153 | } else { 154 | Some((TCFType::wrap_under_create_rule(value), attachment_mode)) 155 | } 156 | } 157 | } 158 | 159 | #[inline] 160 | pub fn remove_attachment(&self, key: &CFString) { 161 | unsafe { CMRemoveAttachment(self.as_concrete_TypeRef(), key.as_concrete_TypeRef()) } 162 | } 163 | 164 | #[inline] 165 | pub fn remove_all_attachments(&self) { 166 | unsafe { CMRemoveAllAttachments(self.as_concrete_TypeRef()) } 167 | } 168 | 169 | #[inline] 170 | pub fn copy_dictionary_of_attachments(&self, attachment_mode: CMAttachmentMode) -> Option> { 171 | unsafe { 172 | let dict = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, self.as_concrete_TypeRef(), attachment_mode); 173 | if dict.is_null() { 174 | None 175 | } else { 176 | Some(TCFType::wrap_under_create_rule(dict)) 177 | } 178 | } 179 | } 180 | 181 | #[inline] 182 | pub fn set_attachments(&self, the_attachments: &CFDictionary, attachment_mode: CMAttachmentMode) { 183 | unsafe { CMSetAttachments(self.as_concrete_TypeRef(), the_attachments.as_concrete_TypeRef(), attachment_mode) } 184 | } 185 | 186 | #[inline] 187 | pub fn propagate_attachments(&self, destination: &mut CMAttachmentBearer) { 188 | unsafe { CMPropagateAttachments(self.as_concrete_TypeRef(), destination.0) } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /core-media/src/audio_clock.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::null_mut; 2 | 3 | use core_foundation::base::{kCFAllocatorDefault, CFAllocatorRef, OSStatus, TCFType}; 4 | 5 | use crate::sync::{CMClock, CMClockRef}; 6 | 7 | extern "C" { 8 | pub fn CMAudioClockCreate(allocator: CFAllocatorRef, clockOut: *mut CMClockRef) -> OSStatus; 9 | } 10 | 11 | impl CMClock { 12 | #[inline] 13 | pub fn new_audio_clock() -> Result { 14 | unsafe { 15 | let mut clock = null_mut(); 16 | let status = CMAudioClockCreate(kCFAllocatorDefault, &mut clock); 17 | if status == 0 { 18 | Ok(TCFType::wrap_under_create_rule(clock)) 19 | } else { 20 | Err(status) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core-media/src/audio_device_clock.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | base::{kCFAllocatorDefault, Boolean, CFAllocatorRef, OSStatus, TCFType}, 5 | string::{CFString, CFStringRef}, 6 | }; 7 | 8 | use crate::sync::{CMClock, CMClockRef}; 9 | 10 | pub type AudioObjectID = u32; 11 | 12 | pub type AudioDeviceID = AudioObjectID; 13 | 14 | pub const kAudioObjectUnknown: AudioObjectID = 0; 15 | pub const kAudioStreamUnknown: AudioObjectID = kAudioObjectUnknown; 16 | 17 | extern "C" { 18 | pub fn CMAudioDeviceClockCreate(allocator: CFAllocatorRef, deviceUID: CFStringRef, clockOut: *mut CMClockRef) -> OSStatus; 19 | pub fn CMAudioDeviceClockCreateFromAudioDeviceID(allocator: CFAllocatorRef, deviceID: AudioDeviceID, clockOut: *mut CMClockRef) -> OSStatus; 20 | pub fn CMAudioDeviceClockSetAudioDeviceUID(clock: CMClockRef, deviceUID: CFStringRef) -> OSStatus; 21 | pub fn CMAudioDeviceClockSetAudioDeviceID(clock: CMClockRef, deviceID: AudioDeviceID) -> OSStatus; 22 | pub fn CMAudioDeviceClockGetAudioDevice( 23 | clock: CMClockRef, 24 | deviceUIDOut: *mut CFStringRef, 25 | deviceIDOut: *mut AudioDeviceID, 26 | trackingDefaultDeviceOut: *mut Boolean, 27 | ) -> OSStatus; 28 | } 29 | 30 | impl CMClock { 31 | #[inline] 32 | pub fn new_audio_device_clock(device_uid: &CFString) -> Result { 33 | unsafe { 34 | let mut clock: CMClockRef = null_mut(); 35 | let status = CMAudioDeviceClockCreate(kCFAllocatorDefault, device_uid.as_concrete_TypeRef(), &mut clock); 36 | if status == 0 { 37 | Ok(TCFType::wrap_under_create_rule(clock)) 38 | } else { 39 | Err(status) 40 | } 41 | } 42 | } 43 | 44 | #[inline] 45 | pub fn new_audio_device_clock_from_device_id(device_id: AudioDeviceID) -> Result { 46 | unsafe { 47 | let mut clock: CMClockRef = null_mut(); 48 | let status = CMAudioDeviceClockCreateFromAudioDeviceID(kCFAllocatorDefault, device_id, &mut clock); 49 | if status == 0 { 50 | Ok(TCFType::wrap_under_create_rule(clock)) 51 | } else { 52 | Err(status) 53 | } 54 | } 55 | } 56 | 57 | #[inline] 58 | pub fn set_audio_device_uid(&self, device_uid: &CFString) -> Result<(), OSStatus> { 59 | unsafe { 60 | let status = CMAudioDeviceClockSetAudioDeviceUID(self.as_concrete_TypeRef(), device_uid.as_concrete_TypeRef()); 61 | if status == 0 { 62 | Ok(()) 63 | } else { 64 | Err(status) 65 | } 66 | } 67 | } 68 | 69 | #[inline] 70 | pub fn set_audio_device_id(&self, device_id: AudioDeviceID) -> Result<(), OSStatus> { 71 | unsafe { 72 | let status = CMAudioDeviceClockSetAudioDeviceID(self.as_concrete_TypeRef(), device_id); 73 | if status == 0 { 74 | Ok(()) 75 | } else { 76 | Err(status) 77 | } 78 | } 79 | } 80 | 81 | #[inline] 82 | pub fn get_audio_device(&self) -> Result<(CFString, AudioDeviceID, Boolean), OSStatus> { 83 | unsafe { 84 | let mut device_uid = null(); 85 | let mut device_id = 0; 86 | let mut tracking_default_device = 0; 87 | let status = CMAudioDeviceClockGetAudioDevice(self.as_concrete_TypeRef(), &mut device_uid, &mut device_id, &mut tracking_default_device); 88 | if status == 0 { 89 | Ok((TCFType::wrap_under_create_rule(device_uid), device_id, tracking_default_device)) 90 | } else { 91 | Err(status) 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /core-media/src/base.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::base::CFIndex; 2 | 3 | pub type CMItemCount = CFIndex; 4 | pub type CMItemIndex = CFIndex; 5 | -------------------------------------------------------------------------------- /core-media/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, improper_ctypes)] 2 | 3 | extern crate block; 4 | #[macro_use] 5 | extern crate cfg_if; 6 | extern crate core_audio_types; 7 | #[macro_use] 8 | extern crate core_foundation; 9 | extern crate core_graphics2 as core_graphics; 10 | extern crate core_video; 11 | extern crate libc; 12 | #[cfg(feature = "objc")] 13 | extern crate objc2; 14 | 15 | pub type OSType = u32; 16 | 17 | #[cfg(any(target_os = "macos", target_os = "ios"))] 18 | #[cfg_attr(feature = "link", link(name = "CoreMedia", kind = "framework"))] 19 | extern "C" {} 20 | 21 | pub mod attachment; 22 | #[cfg(target_os = "ios")] 23 | pub mod audio_clock; 24 | pub mod audio_device_clock; 25 | pub mod base; 26 | pub mod block_buffer; 27 | pub mod buffer_queue; 28 | pub mod format_description; 29 | pub mod format_description_bridge; 30 | pub mod sample_buffer; 31 | pub mod sample_queue; 32 | pub mod sync; 33 | pub mod time; 34 | pub mod time_range; 35 | -------------------------------------------------------------------------------- /core-media/src/sample_queue.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::null_mut; 2 | 3 | use core_foundation::base::{kCFAllocatorDefault, CFAllocatorRef, CFTypeID, OSStatus, TCFType}; 4 | use libc::c_void; 5 | 6 | pub const kCMSimpleQueueError_AllocationFailed: OSStatus = -12770; 7 | pub const kCMSimpleQueueError_RequiredParameterMissing: OSStatus = -12771; 8 | pub const kCMSimpleQueueError_ParameterOutOfRange: OSStatus = -12772; 9 | pub const kCMSimpleQueueError_QueueIsFull: OSStatus = -12773; 10 | 11 | #[repr(C)] 12 | pub struct opaqueCMSimpleQueue(c_void); 13 | 14 | pub type CMSimpleQueueRef = *mut opaqueCMSimpleQueue; 15 | 16 | extern "C" { 17 | pub fn CMSimpleQueueGetTypeID() -> CFTypeID; 18 | pub fn CMSimpleQueueCreate(allocator: CFAllocatorRef, capacity: i32, queueOut: *mut CMSimpleQueueRef) -> OSStatus; 19 | pub fn CMSimpleQueueEnqueue(queue: CMSimpleQueueRef, element: *const c_void) -> OSStatus; 20 | pub fn CMSimpleQueueDequeue(queue: CMSimpleQueueRef) -> *const c_void; 21 | pub fn CMSimpleQueueGetHead(queue: CMSimpleQueueRef) -> *const c_void; 22 | pub fn CMSimpleQueueReset(queue: CMSimpleQueueRef) -> OSStatus; 23 | pub fn CMSimpleQueueGetCapacity(queue: CMSimpleQueueRef) -> i32; 24 | pub fn CMSimpleQueueGetCount(queue: CMSimpleQueueRef) -> i32; 25 | } 26 | 27 | declare_TCFType! { 28 | CMSimpleQueue, CMSimpleQueueRef 29 | } 30 | impl_TCFType!(CMSimpleQueue, CMSimpleQueueRef, CMSimpleQueueGetTypeID); 31 | impl_CFTypeDescription!(CMSimpleQueue); 32 | 33 | impl CMSimpleQueue { 34 | #[inline] 35 | pub fn new(capacity: i32) -> Result { 36 | let mut queue: CMSimpleQueueRef = null_mut(); 37 | let status = unsafe { CMSimpleQueueCreate(kCFAllocatorDefault, capacity, &mut queue) }; 38 | if status == 0 { 39 | Ok(unsafe { CMSimpleQueue::wrap_under_create_rule(queue) }) 40 | } else { 41 | Err(status) 42 | } 43 | } 44 | 45 | #[inline] 46 | pub fn enqueue(&self, element: *const c_void) -> Result<(), OSStatus> { 47 | let status = unsafe { CMSimpleQueueEnqueue(self.as_concrete_TypeRef(), element) }; 48 | if status == 0 { 49 | Ok(()) 50 | } else { 51 | Err(status) 52 | } 53 | } 54 | 55 | #[inline] 56 | pub fn dequeue(&self) -> *const c_void { 57 | unsafe { CMSimpleQueueDequeue(self.as_concrete_TypeRef()) } 58 | } 59 | 60 | #[inline] 61 | pub fn get_head(&self) -> *const c_void { 62 | unsafe { CMSimpleQueueGetHead(self.as_concrete_TypeRef()) } 63 | } 64 | 65 | #[inline] 66 | pub fn reset(&self) -> Result<(), OSStatus> { 67 | let status = unsafe { CMSimpleQueueReset(self.as_concrete_TypeRef()) }; 68 | if status == 0 { 69 | Ok(()) 70 | } else { 71 | Err(status) 72 | } 73 | } 74 | 75 | #[inline] 76 | pub fn get_capacity(&self) -> i32 { 77 | unsafe { CMSimpleQueueGetCapacity(self.as_concrete_TypeRef()) } 78 | } 79 | 80 | #[inline] 81 | pub fn get_count(&self) -> i32 { 82 | unsafe { CMSimpleQueueGetCount(self.as_concrete_TypeRef()) } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /core-media/src/time.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::{ 2 | base::{kCFAllocatorDefault, CFAllocatorRef, TCFType}, 3 | dictionary::{CFDictionary, CFDictionaryRef}, 4 | number::CFNumber, 5 | string::{CFString, CFStringRef}, 6 | }; 7 | #[cfg(feature = "objc")] 8 | use objc2::encode::{Encode, Encoding}; 9 | 10 | pub type CMTimeValue = i64; 11 | pub type CMTimeScale = i32; 12 | pub type CMTimeEpoch = i64; 13 | pub type CMTimeFlags = u32; 14 | 15 | pub const kCMTimeFlags_Valid: CMTimeFlags = 1 << 0; 16 | pub const kCMTimeFlags_HasBeenRounded: CMTimeFlags = 1 << 1; 17 | pub const kCMTimeFlags_PositiveInfinity: CMTimeFlags = 1 << 2; 18 | pub const kCMTimeFlags_NegativeInfinity: CMTimeFlags = 1 << 3; 19 | pub const kCMTimeFlags_Indefinite: CMTimeFlags = 1 << 4; 20 | pub const kCMTimeFlags_ImpliedValueFlagsMask: CMTimeFlags = kCMTimeFlags_PositiveInfinity | kCMTimeFlags_NegativeInfinity | kCMTimeFlags_Indefinite; 21 | 22 | #[repr(C, align(4))] 23 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 24 | pub struct CMTime { 25 | pub value: CMTimeValue, 26 | pub timescale: CMTimeScale, 27 | pub flags: CMTimeFlags, 28 | pub epoch: CMTimeEpoch, 29 | } 30 | 31 | extern "C" { 32 | pub static kCMTimeInvalid: CMTime; 33 | pub static kCMTimeIndefinite: CMTime; 34 | pub static kCMTimePositiveInfinity: CMTime; 35 | pub static kCMTimeNegativeInfinity: CMTime; 36 | pub static kCMTimeZero: CMTime; 37 | 38 | pub fn CMTimeMake(value: CMTimeValue, timescale: CMTimeScale) -> CMTime; 39 | pub fn CMTimeMakeWithEpoch(value: CMTimeValue, timescale: CMTimeScale, epoch: CMTimeEpoch) -> CMTime; 40 | pub fn CMTimeMakeWithSeconds(seconds: f64, preferredTimeScale: i32) -> CMTime; 41 | pub fn CMTimeGetSeconds(time: CMTime) -> f64; 42 | } 43 | 44 | pub type CMTimeRoundingMethod = u32; 45 | 46 | pub const kCMTimeRoundingMethod_RoundHalfAwayFromZero: CMTimeRoundingMethod = 1; 47 | pub const kCMTimeRoundingMethod_RoundTowardZero: CMTimeRoundingMethod = 2; 48 | pub const kCMTimeRoundingMethod_RoundAwayFromZero: CMTimeRoundingMethod = 3; 49 | pub const kCMTimeRoundingMethod_QuickTime: CMTimeRoundingMethod = 4; 50 | pub const kCMTimeRoundingMethod_RoundTowardPositiveInfinity: CMTimeRoundingMethod = 5; 51 | pub const kCMTimeRoundingMethod_RoundTowardNegativeInfinity: CMTimeRoundingMethod = 6; 52 | pub const kCMTimeRoundingMethod_Default: CMTimeRoundingMethod = kCMTimeRoundingMethod_RoundHalfAwayFromZero; 53 | 54 | extern "C" { 55 | pub fn CMTimeConvertScale(time: CMTime, newTimescale: CMTimeScale, method: CMTimeRoundingMethod) -> CMTime; 56 | pub fn CMTimeAdd(lhs: CMTime, rhs: CMTime) -> CMTime; 57 | pub fn CMTimeSubtract(lhs: CMTime, rhs: CMTime) -> CMTime; 58 | pub fn CMTimeMultiply(time: CMTime, multiplier: i32) -> CMTime; 59 | pub fn CMTimeMultiplyByFloat64(time: CMTime, multiplier: f64) -> CMTime; 60 | pub fn CMTimeMultiplyByRatio(time: CMTime, multiplier: i32, divisor: i32) -> CMTime; 61 | pub fn CMTimeCompare(time1: CMTime, time2: CMTime) -> i32; 62 | pub fn CMTimeMinimum(time1: CMTime, time2: CMTime) -> CMTime; 63 | pub fn CMTimeMaximum(time1: CMTime, time2: CMTime) -> CMTime; 64 | pub fn CMTimeAbsoluteValue(time: CMTime) -> CMTime; 65 | pub fn CMTimeCopyAsDictionary(time: CMTime, allocator: CFAllocatorRef) -> CFDictionaryRef; 66 | pub fn CMTimeMakeFromDictionary(dictionaryRepresentation: CFDictionaryRef) -> CMTime; 67 | 68 | pub static kCMTimeValueKey: CFStringRef; 69 | pub static kCMTimeScaleKey: CFStringRef; 70 | pub static kCMTimeEpochKey: CFStringRef; 71 | pub static kCMTimeFlagsKey: CFStringRef; 72 | 73 | pub fn CMTimeCopyDescription(allocator: CFAllocatorRef, time: CMTime) -> CFStringRef; 74 | pub fn CMTimeShow(time: CMTime); 75 | } 76 | 77 | #[cfg(feature = "objc")] 78 | unsafe impl Encode for CMTime { 79 | const ENCODING: Encoding = Encoding::Struct("?", &[CMTimeValue::ENCODING, CMTimeScale::ENCODING, CMTimeFlags::ENCODING, CMTimeEpoch::ENCODING]); 80 | } 81 | 82 | impl CMTime { 83 | #[inline] 84 | pub fn make(value: CMTimeValue, timescale: CMTimeScale) -> Self { 85 | unsafe { CMTimeMake(value, timescale) } 86 | } 87 | 88 | #[inline] 89 | pub fn make_with_epoch(value: CMTimeValue, timescale: CMTimeScale, epoch: CMTimeEpoch) -> Self { 90 | unsafe { CMTimeMakeWithEpoch(value, timescale, epoch) } 91 | } 92 | 93 | #[inline] 94 | pub fn make_with_seconds(seconds: f64, preferred_time_scale: i32) -> Self { 95 | unsafe { CMTimeMakeWithSeconds(seconds, preferred_time_scale) } 96 | } 97 | 98 | #[inline] 99 | pub fn get_seconds(&self) -> f64 { 100 | unsafe { CMTimeGetSeconds(*self) } 101 | } 102 | 103 | #[inline] 104 | pub fn convert_scale(&self, new_timescale: CMTimeScale, method: CMTimeRoundingMethod) -> Self { 105 | unsafe { CMTimeConvertScale(*self, new_timescale, method) } 106 | } 107 | 108 | #[inline] 109 | pub fn add(&self, time: CMTime) -> Self { 110 | unsafe { CMTimeAdd(*self, time) } 111 | } 112 | 113 | #[inline] 114 | pub fn subtract(&self, time: CMTime) -> Self { 115 | unsafe { CMTimeSubtract(*self, time) } 116 | } 117 | 118 | #[inline] 119 | pub fn multiply(&self, multiplier: i32) -> Self { 120 | unsafe { CMTimeMultiply(*self, multiplier) } 121 | } 122 | 123 | #[inline] 124 | pub fn multiply_by_float64(&self, multiplier: f64) -> Self { 125 | unsafe { CMTimeMultiplyByFloat64(*self, multiplier) } 126 | } 127 | 128 | #[inline] 129 | pub fn multiply_by_ratio(&self, multiplier: i32, divisor: i32) -> Self { 130 | unsafe { CMTimeMultiplyByRatio(*self, multiplier, divisor) } 131 | } 132 | 133 | #[inline] 134 | pub fn compare(&self, time: CMTime) -> i32 { 135 | unsafe { CMTimeCompare(*self, time) } 136 | } 137 | 138 | #[inline] 139 | pub fn minimum(&self, time: CMTime) -> Self { 140 | unsafe { CMTimeMinimum(*self, time) } 141 | } 142 | 143 | #[inline] 144 | pub fn maximum(&self, time: CMTime) -> Self { 145 | unsafe { CMTimeMaximum(*self, time) } 146 | } 147 | 148 | #[inline] 149 | pub fn absolute_value(&self) -> Self { 150 | unsafe { CMTimeAbsoluteValue(*self) } 151 | } 152 | 153 | #[inline] 154 | pub fn copy_as_dictionary(&self) -> Option> { 155 | unsafe { 156 | let dict = CMTimeCopyAsDictionary(*self, kCFAllocatorDefault); 157 | if dict.is_null() { 158 | None 159 | } else { 160 | Some(TCFType::wrap_under_create_rule(dict)) 161 | } 162 | } 163 | } 164 | 165 | #[inline] 166 | pub fn make_from_dictionary(dictionary_representation: &CFDictionary) -> Self { 167 | unsafe { CMTimeMakeFromDictionary(dictionary_representation.as_concrete_TypeRef()) } 168 | } 169 | 170 | #[inline] 171 | pub fn copy_description(&self) -> Option { 172 | unsafe { 173 | let description = CMTimeCopyDescription(kCFAllocatorDefault, *self); 174 | if description.is_null() { 175 | None 176 | } else { 177 | Some(TCFType::wrap_under_create_rule(description)) 178 | } 179 | } 180 | } 181 | 182 | #[inline] 183 | pub fn show(&self) { 184 | unsafe { CMTimeShow(*self) } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /core-media/src/time_range.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::{ 2 | base::{kCFAllocatorDefault, Boolean, CFAllocatorRef, TCFType}, 3 | dictionary::{CFDictionary, CFDictionaryRef}, 4 | number::CFNumber, 5 | string::{CFString, CFStringRef}, 6 | }; 7 | 8 | use crate::time::CMTime; 9 | 10 | #[repr(C, align(4))] 11 | #[derive(Clone, Copy, Debug, Default)] 12 | pub struct CMTimeRange { 13 | pub start: CMTime, 14 | pub duration: CMTime, 15 | } 16 | 17 | extern "C" { 18 | pub static kCMTimeRangeZero: CMTimeRange; 19 | pub static kCMTimeRangeInvalid: CMTimeRange; 20 | 21 | pub fn CMTimeRangeMake(start: CMTime, duration: CMTime) -> CMTimeRange; 22 | pub fn CMTimeRangeGetUnion(range: CMTimeRange, otherRange: CMTimeRange) -> CMTimeRange; 23 | pub fn CMTimeRangeGetIntersection(range: CMTimeRange, otherRange: CMTimeRange) -> CMTimeRange; 24 | pub fn CMTimeRangeEqual(range1: CMTimeRange, range2: CMTimeRange) -> Boolean; 25 | pub fn CMTimeRangeContainsTime(range: CMTimeRange, time: CMTime) -> Boolean; 26 | pub fn CMTimeRangeContainsTimeRange(range: CMTimeRange, otherRange: CMTimeRange) -> Boolean; 27 | pub fn CMTimeRangeGetEnd(range: CMTimeRange) -> CMTime; 28 | pub fn CMTimeRangeFromRangeToRange(t: CMTime, fromRange: CMTimeRange, toRange: CMTimeRange) -> CMTime; 29 | pub fn CMTimeClampToRange(time: CMTime, range: CMTimeRange) -> CMTime; 30 | pub fn CMTimeMapDurationFromRangeToRange(dur: CMTime, fromRange: CMTimeRange, toRange: CMTimeRange) -> CMTime; 31 | pub fn CMTimeFoldIntoRange(time: CMTime, foldRange: CMTimeRange) -> CMTime; 32 | pub fn CMTimeRangeFromTimeToTime(start: CMTime, end: CMTime) -> CMTimeRange; 33 | pub fn CMTimeRangeCopyAsDictionary(range: CMTimeRange, allocator: CFAllocatorRef) -> CFDictionaryRef; 34 | pub fn CMTimeRangeMakeFromDictionary(dict: CFDictionaryRef) -> CMTimeRange; 35 | 36 | pub static kCMTimeRangeStartKey: CFStringRef; 37 | pub static kCMTimeRangeDurationKey: CFStringRef; 38 | 39 | pub fn CMTimeRangeCopyDescription(allocator: CFAllocatorRef, range: CMTimeRange) -> CFStringRef; 40 | pub fn CMTimeRangeShow(range: CMTimeRange); 41 | } 42 | 43 | #[repr(C, align(4))] 44 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 45 | pub struct CMTimeMapping { 46 | pub source: CMTimeRange, 47 | pub target: CMTimeRange, 48 | } 49 | 50 | extern "C" { 51 | pub static kCMTimeMappingInvalid: CMTimeMapping; 52 | 53 | pub fn CMTimeMappingMake(source: CMTimeRange, target: CMTimeRange) -> CMTimeMapping; 54 | pub fn CMTimeMappingMakeEmpty(target: CMTimeRange) -> CMTimeMapping; 55 | pub fn CMTimeMappingCopyAsDictionary(mapping: CMTimeMapping, allocator: CFAllocatorRef) -> CFDictionaryRef; 56 | pub fn CMTimeMappingMakeFromDictionary(dictionaryRepresentation: CFDictionaryRef) -> CMTimeMapping; 57 | 58 | pub static kCMTimeMappingSourceKey: CFStringRef; 59 | pub static kCMTimeMappingTargetKey: CFStringRef; 60 | 61 | pub fn CMTimeMappingCopyDescription(allocator: CFAllocatorRef, mapping: CMTimeMapping) -> CFStringRef; 62 | pub fn CMTimeMappingShow(mapping: CMTimeMapping); 63 | } 64 | 65 | impl PartialEq for CMTimeRange { 66 | #[inline] 67 | fn eq(&self, other: &Self) -> bool { 68 | unsafe { CMTimeRangeEqual(*self, *other) != 0 } 69 | } 70 | } 71 | 72 | impl CMTimeRange { 73 | #[inline] 74 | pub fn make(start: CMTime, duration: CMTime) -> Self { 75 | unsafe { CMTimeRangeMake(start, duration) } 76 | } 77 | 78 | #[inline] 79 | pub fn get_union(&self, other: CMTimeRange) -> CMTimeRange { 80 | unsafe { CMTimeRangeGetUnion(*self, other) } 81 | } 82 | 83 | #[inline] 84 | pub fn get_intersection(&self, other: CMTimeRange) -> CMTimeRange { 85 | unsafe { CMTimeRangeGetIntersection(*self, other) } 86 | } 87 | 88 | #[inline] 89 | pub fn equal(&self, other: CMTimeRange) -> bool { 90 | unsafe { CMTimeRangeEqual(*self, other) != 0 } 91 | } 92 | 93 | #[inline] 94 | pub fn contains_time(&self, time: CMTime) -> bool { 95 | unsafe { CMTimeRangeContainsTime(*self, time) != 0 } 96 | } 97 | 98 | #[inline] 99 | pub fn contains_time_range(&self, other: CMTimeRange) -> bool { 100 | unsafe { CMTimeRangeContainsTimeRange(*self, other) != 0 } 101 | } 102 | 103 | #[inline] 104 | pub fn get_end(&self) -> CMTime { 105 | unsafe { CMTimeRangeGetEnd(*self) } 106 | } 107 | 108 | #[inline] 109 | pub fn copy_as_dictionary(&self) -> Option>> { 110 | unsafe { 111 | let dict = CMTimeRangeCopyAsDictionary(*self, kCFAllocatorDefault); 112 | if dict.is_null() { 113 | None 114 | } else { 115 | Some(TCFType::wrap_under_create_rule(dict)) 116 | } 117 | } 118 | } 119 | 120 | #[inline] 121 | pub fn make_from_dictionary(dict: &CFDictionary>) -> Self { 122 | unsafe { CMTimeRangeMakeFromDictionary(dict.as_concrete_TypeRef()) } 123 | } 124 | 125 | #[inline] 126 | pub fn copy_description(&self) -> Option { 127 | unsafe { 128 | let description = CMTimeRangeCopyDescription(kCFAllocatorDefault, *self); 129 | if description.is_null() { 130 | None 131 | } else { 132 | Some(TCFType::wrap_under_create_rule(description)) 133 | } 134 | } 135 | } 136 | 137 | #[inline] 138 | pub fn show(&self) { 139 | unsafe { CMTimeRangeShow(*self) } 140 | } 141 | } 142 | 143 | impl CMTimeMapping { 144 | #[inline] 145 | pub fn make(source: CMTimeRange, target: CMTimeRange) -> Self { 146 | unsafe { CMTimeMappingMake(source, target) } 147 | } 148 | 149 | #[inline] 150 | pub fn make_empty(target: CMTimeRange) -> Self { 151 | unsafe { CMTimeMappingMakeEmpty(target) } 152 | } 153 | 154 | #[inline] 155 | pub fn copy_as_dictionary(&self) -> Option>>> { 156 | unsafe { 157 | let dict = CMTimeMappingCopyAsDictionary(*self, kCFAllocatorDefault); 158 | if dict.is_null() { 159 | None 160 | } else { 161 | Some(TCFType::wrap_under_create_rule(dict)) 162 | } 163 | } 164 | } 165 | 166 | #[inline] 167 | pub fn make_from_dictionary(dict: &CFDictionary>>) -> Self { 168 | unsafe { CMTimeMappingMakeFromDictionary(dict.as_concrete_TypeRef()) } 169 | } 170 | 171 | #[inline] 172 | pub fn copy_description(&self) -> Option { 173 | unsafe { 174 | let description = CMTimeMappingCopyDescription(kCFAllocatorDefault, *self); 175 | if description.is_null() { 176 | None 177 | } else { 178 | Some(TCFType::wrap_under_create_rule(description)) 179 | } 180 | } 181 | } 182 | 183 | #[inline] 184 | pub fn show(&self) { 185 | unsafe { CMTimeMappingShow(*self) } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /core-video/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "core-video" 3 | description = "Safe bindings to CoreVideo framework" 4 | homepage = "https://github.com/rust-media/apple-media-rs" 5 | repository = "https://github.com/rust-media/apple-media-rs" 6 | documentation = "https://docs.rs/core-video" 7 | version = "0.4.3" 8 | authors = ["Zhou Wei "] 9 | license = "MIT OR Apache-2.0" 10 | categories = ["multimedia::video", "os::macos-apis"] 11 | keywords = ["corevideo"] 12 | edition = "2018" 13 | 14 | [dependencies] 15 | block = "0.1" 16 | core-foundation = { version = "0.10", default-features = false } 17 | core-graphics2 = { version = "0.4", path = "../core-graphics", default-features = false, features = ["display"] } 18 | io-surface = { version = "0.16", default-features = false } 19 | libc = "0.2" 20 | metal = { version = "0.29", optional = true } 21 | objc2 = { version = "0.5", optional = true } 22 | 23 | [features] 24 | default = ["display-link", "link"] 25 | display-link = [] 26 | link = ["core-foundation/link", "core-graphics2/link"] 27 | objc = ["objc2"] 28 | 29 | [package.metadata.docs.rs] 30 | no-default-features = true 31 | features = ["display-link", "metal", "objc"] 32 | default-target = "x86_64-apple-darwin" 33 | targets = [ 34 | "aarch64-apple-darwin", 35 | "aarch64-apple-ios", 36 | "x86_64-apple-darwin", 37 | "x86_64-apple-ios", 38 | ] 39 | -------------------------------------------------------------------------------- /core-video/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /core-video/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /core-video/README.md: -------------------------------------------------------------------------------- 1 | # core-video 2 | 3 | [![Version](https://img.shields.io/crates/v/core-video)](https://crates.io/crates/core-video) 4 | [![Documentation](https://docs.rs/core-video/badge.svg)](https://docs.rs/core-video) 5 | [![License](https://img.shields.io/badge/License-Apache%202-blue.svg)](LICENSE-APACHE) 6 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE-MIT) 7 | 8 | Rust safe bindings to CoreVideo framework 9 | -------------------------------------------------------------------------------- /core-video/src/base.rs: -------------------------------------------------------------------------------- 1 | use crate::libc::c_double; 2 | 3 | pub type CVOptionFlags = u64; 4 | 5 | #[repr(C)] 6 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 7 | pub struct CVSMPTETime { 8 | pub subframes: i16, 9 | pub subframeDivisor: i16, 10 | pub counter: u32, 11 | pub type_: u32, 12 | pub flags: u32, 13 | pub hours: i16, 14 | pub minutes: i16, 15 | pub seconds: i16, 16 | pub frames: i16, 17 | } 18 | 19 | #[repr(u32)] 20 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 21 | pub enum CVSMPTETimeType { 22 | kCVSMPTETimeType24 = 0, 23 | kCVSMPTETimeType25 = 1, 24 | kCVSMPTETimeType30Drop = 2, 25 | kCVSMPTETimeType30 = 3, 26 | kCVSMPTETimeType2997 = 4, 27 | kCVSMPTETimeType2997Drop = 5, 28 | kCVSMPTETimeType60 = 6, 29 | kCVSMPTETimeType5994 = 7, 30 | } 31 | 32 | #[repr(u32)] 33 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 34 | pub enum CVSMPTETimeFlags { 35 | kCVSMPTETimeValid = (1 << 0), 36 | kCVSMPTETimeRunning = (1 << 1), 37 | } 38 | 39 | #[repr(i32)] 40 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 41 | pub enum CVTimeFlags { 42 | kCVTimeIsIndefinite = (1 << 0), 43 | } 44 | 45 | #[repr(C)] 46 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] 47 | pub struct CVTime { 48 | pub timeValue: i64, 49 | pub timeScale: i32, 50 | pub flags: i32, 51 | } 52 | 53 | #[repr(C)] 54 | #[derive(Clone, Copy, Debug, PartialEq)] 55 | pub struct CVTimeStamp { 56 | pub version: u32, 57 | pub videoTimeScale: i32, 58 | pub videoTime: i64, 59 | pub hostTime: u64, 60 | pub rateScalar: c_double, 61 | pub videoRefreshPeriod: i64, 62 | pub smpteTime: CVSMPTETime, 63 | pub flags: u64, 64 | pub reserved: u64, 65 | } 66 | 67 | #[repr(u64)] 68 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 69 | pub enum CVTimeStampFlags { 70 | kCVTimeStampVideoTimeValid = (1 << 0), 71 | kCVTimeStampHostTimeValid = (1 << 1), 72 | kCVTimeStampSMPTETimeValid = (1 << 2), 73 | kCVTimeStampVideoRefreshPeriodValid = (1 << 3), 74 | kCVTimeStampRateScalarValid = (1 << 4), 75 | kCVTimeStampTopField = (1 << 16), 76 | kCVTimeStampBottomField = (1 << 17), 77 | kCVTimeStampVideoHostTimeValid = (1 << 0) | (1 << 1), /* kCVTimeStampVideoTimeValid | 78 | * kCVTimeStampHostTimeValid */ 79 | kCVTimeStampIsInterlaced = (1 << 16) | (1 << 17), /* kCVTimeStampTopField | 80 | * kCVTimeStampBottomField */ 81 | } 82 | 83 | extern "C" { 84 | pub static kCVZeroTime: CVTime; 85 | pub static kCVIndefiniteTime: CVTime; 86 | } 87 | -------------------------------------------------------------------------------- /core-video/src/host_time.rs: -------------------------------------------------------------------------------- 1 | use crate::libc::c_double; 2 | 3 | extern "C" { 4 | pub fn CVGetCurrentHostTime() -> u64; 5 | pub fn CVGetHostClockFrequency() -> c_double; 6 | pub fn CVGetHostClockMinimumTimeDelta() -> u32; 7 | } 8 | 9 | pub fn get_current_host_time() -> u64 { 10 | unsafe { CVGetCurrentHostTime() } 11 | } 12 | 13 | pub fn get_host_clock_frequency() -> f64 { 14 | unsafe { CVGetHostClockFrequency() } 15 | } 16 | 17 | pub fn get_host_clock_minimum_time_delta() -> u32 { 18 | unsafe { CVGetHostClockMinimumTimeDelta() } 19 | } 20 | -------------------------------------------------------------------------------- /core-video/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, improper_ctypes)] 2 | 3 | extern crate block; 4 | #[macro_use] 5 | extern crate core_foundation; 6 | extern crate core_graphics2 as core_graphics; 7 | extern crate io_surface; 8 | extern crate libc; 9 | #[cfg(feature = "metal")] 10 | extern crate metal; 11 | #[cfg(feature = "objc")] 12 | extern crate objc2; 13 | 14 | #[cfg(any(target_os = "macos", target_os = "ios"))] 15 | #[cfg_attr(feature = "link", link(name = "CoreVideo", kind = "framework"))] 16 | extern "C" {} 17 | 18 | pub type CGLContextObj = *mut libc::c_void; 19 | pub type CGLPixelFormatObj = *mut libc::c_void; 20 | pub type GLenum = libc::c_uint; 21 | pub type GLsizei = libc::c_int; 22 | pub type GLint = libc::c_int; 23 | pub type GLuint = libc::c_uint; 24 | pub type OSType = u32; 25 | 26 | pub mod base; 27 | pub mod buffer; 28 | #[cfg(all(target_os = "macos", feature = "display-link"))] 29 | pub mod display_link; 30 | pub mod host_time; 31 | pub mod image_buffer; 32 | #[cfg(feature = "metal")] 33 | pub mod metal_texture; 34 | #[cfg(feature = "metal")] 35 | pub mod metal_texture_cache; 36 | #[cfg(target_os = "macos")] 37 | pub mod opengl_buffer; 38 | #[cfg(target_os = "macos")] 39 | pub mod opengl_buffer_pool; 40 | #[cfg(target_os = "ios")] 41 | pub mod opengl_es_texture; 42 | #[cfg(target_os = "ios")] 43 | pub mod opengl_es_texture_cache; 44 | #[cfg(target_os = "macos")] 45 | pub mod opengl_texture; 46 | #[cfg(target_os = "macos")] 47 | pub mod opengl_texture_cache; 48 | pub mod pixel_buffer; 49 | pub mod pixel_buffer_io_surface; 50 | pub mod pixel_buffer_pool; 51 | pub mod pixel_format_description; 52 | pub mod r#return; 53 | -------------------------------------------------------------------------------- /core-video/src/metal_texture.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::{ 2 | base::{Boolean, CFTypeID, TCFType}, 3 | string::{CFString, CFStringRef}, 4 | }; 5 | use metal::{foreign_types::ForeignType, MTLTexture, Texture}; 6 | 7 | use crate::{ 8 | buffer::TCVBuffer, 9 | image_buffer::{CVImageBufferRef, TCVImageBuffer}, 10 | }; 11 | 12 | pub type CVMetalTextureRef = CVImageBufferRef; 13 | 14 | extern "C" { 15 | pub fn CVMetalTextureGetTypeID() -> CFTypeID; 16 | pub fn CVMetalTextureGetTexture(image: CVMetalTextureRef) -> *mut MTLTexture; 17 | pub fn CVMetalTextureIsFlipped(image: CVMetalTextureRef) -> Boolean; 18 | pub fn CVMetalTextureGetCleanTexCoords( 19 | image: CVMetalTextureRef, 20 | lowerLeft: *mut f32, 21 | lowerRight: *mut f32, 22 | upperRight: *mut f32, 23 | upperLeft: *mut f32, 24 | ); 25 | 26 | pub static kCVMetalTextureUsage: CFStringRef; 27 | pub static kCVMetalTextureStorageMode: CFStringRef; 28 | } 29 | 30 | pub enum CVMetalTextureKeys { 31 | Usage, 32 | StorageMode, 33 | } 34 | 35 | impl From for CFStringRef { 36 | fn from(key: CVMetalTextureKeys) -> Self { 37 | unsafe { 38 | match key { 39 | CVMetalTextureKeys::Usage => kCVMetalTextureUsage, 40 | CVMetalTextureKeys::StorageMode => kCVMetalTextureStorageMode, 41 | } 42 | } 43 | } 44 | } 45 | 46 | impl From for CFString { 47 | fn from(key: CVMetalTextureKeys) -> Self { 48 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) } 49 | } 50 | } 51 | 52 | impl TCVBuffer for CVMetalTexture {} 53 | impl TCVImageBuffer for CVMetalTexture {} 54 | 55 | declare_TCFType! { 56 | CVMetalTexture, CVMetalTextureRef 57 | } 58 | impl_TCFType!(CVMetalTexture, CVMetalTextureRef, CVMetalTextureGetTypeID); 59 | impl_CFTypeDescription!(CVMetalTexture); 60 | 61 | impl CVMetalTexture { 62 | #[inline] 63 | pub fn get_texture(&self) -> Option { 64 | unsafe { 65 | let texture = CVMetalTextureGetTexture(self.as_concrete_TypeRef()); 66 | if texture.is_null() { 67 | None 68 | } else { 69 | Some(Texture::from_ptr(texture)) 70 | } 71 | } 72 | } 73 | 74 | #[inline] 75 | pub fn is_flipped(&self) -> bool { 76 | unsafe { CVMetalTextureIsFlipped(self.as_concrete_TypeRef()) != 0 } 77 | } 78 | 79 | #[inline] 80 | pub fn get_clean_tex_coords(&self) -> (f32, f32, f32, f32) { 81 | let mut lower_left = 0.0; 82 | let mut lower_right = 0.0; 83 | let mut upper_right = 0.0; 84 | let mut upper_left = 0.0; 85 | unsafe { 86 | CVMetalTextureGetCleanTexCoords(self.as_concrete_TypeRef(), &mut lower_left, &mut lower_right, &mut upper_right, &mut upper_left); 87 | } 88 | (lower_left, lower_right, upper_right, upper_left) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /core-video/src/metal_texture_cache.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | base::{kCFAllocatorDefault, CFAllocatorRef, CFType, CFTypeID, TCFType}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::{CFString, CFStringRef}, 7 | }; 8 | use libc::{c_void, size_t}; 9 | 10 | use crate::{ 11 | base::CVOptionFlags, 12 | image_buffer::CVImageBufferRef, 13 | metal_texture::{CVMetalTexture, CVMetalTextureRef}, 14 | r#return::{kCVReturnSuccess, CVReturn}, 15 | }; 16 | 17 | #[repr(C)] 18 | pub struct __CVMetalTextureCache(c_void); 19 | 20 | pub type CVMetalTextureCacheRef = *mut __CVMetalTextureCache; 21 | 22 | extern "C" { 23 | pub static kCVMetalTextureCacheMaximumTextureAgeKey: CFStringRef; 24 | 25 | pub fn CVMetalTextureCacheGetTypeID() -> CFTypeID; 26 | pub fn CVMetalTextureCacheCreate( 27 | allocator: CFAllocatorRef, 28 | cacheAttributes: CFDictionaryRef, 29 | metalDevice: metal::Device, 30 | textureAttributes: CFDictionaryRef, 31 | cacheOut: *mut CVMetalTextureCacheRef, 32 | ) -> CVReturn; 33 | pub fn CVMetalTextureCacheCreateTextureFromImage( 34 | allocator: CFAllocatorRef, 35 | textureCache: CVMetalTextureCacheRef, 36 | sourceImage: CVImageBufferRef, 37 | textureAttributes: CFDictionaryRef, 38 | pixelFormat: metal::MTLPixelFormat, 39 | width: size_t, 40 | height: size_t, 41 | planeIndex: size_t, 42 | textureOut: *mut CVMetalTextureRef, 43 | ) -> CVReturn; 44 | pub fn CVMetalTextureCacheFlush(textureCache: CVMetalTextureCacheRef, options: CVOptionFlags); 45 | } 46 | 47 | pub enum CVMetalTextureCacheKeys { 48 | MaximumTextureAge, 49 | } 50 | 51 | impl From for CFStringRef { 52 | fn from(key: CVMetalTextureCacheKeys) -> Self { 53 | unsafe { 54 | match key { 55 | CVMetalTextureCacheKeys::MaximumTextureAge => kCVMetalTextureCacheMaximumTextureAgeKey, 56 | } 57 | } 58 | } 59 | } 60 | 61 | impl From for CFString { 62 | fn from(key: CVMetalTextureCacheKeys) -> Self { 63 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) } 64 | } 65 | } 66 | 67 | declare_TCFType! { 68 | CVMetalTextureCache, CVMetalTextureCacheRef 69 | } 70 | impl_TCFType!(CVMetalTextureCache, CVMetalTextureCacheRef, CVMetalTextureCacheGetTypeID); 71 | impl_CFTypeDescription!(CVMetalTextureCache); 72 | 73 | impl CVMetalTextureCache { 74 | #[inline] 75 | pub fn new( 76 | cache_attributes: Option<&CFDictionary>, 77 | metal_device: metal::Device, 78 | texture_attributes: Option<&CFDictionary>, 79 | ) -> Result { 80 | let mut cache: CVMetalTextureCacheRef = null_mut(); 81 | let status = unsafe { 82 | CVMetalTextureCacheCreate( 83 | kCFAllocatorDefault, 84 | cache_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 85 | metal_device, 86 | texture_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 87 | &mut cache, 88 | ) 89 | }; 90 | if status == kCVReturnSuccess { 91 | Ok(unsafe { TCFType::wrap_under_create_rule(cache) }) 92 | } else { 93 | Err(status) 94 | } 95 | } 96 | 97 | #[inline] 98 | pub fn create_texture_from_image( 99 | &self, 100 | source_image: CVImageBufferRef, 101 | texture_attributes: Option<&CFDictionary>, 102 | pixel_format: metal::MTLPixelFormat, 103 | width: size_t, 104 | height: size_t, 105 | plane_index: size_t, 106 | ) -> Result { 107 | let mut texture: CVMetalTextureRef = null_mut(); 108 | let status = unsafe { 109 | CVMetalTextureCacheCreateTextureFromImage( 110 | kCFAllocatorDefault, 111 | self.as_concrete_TypeRef(), 112 | source_image, 113 | texture_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 114 | pixel_format, 115 | width, 116 | height, 117 | plane_index, 118 | &mut texture, 119 | ) 120 | }; 121 | if status == kCVReturnSuccess { 122 | Ok(unsafe { TCFType::wrap_under_create_rule(texture) }) 123 | } else { 124 | Err(status) 125 | } 126 | } 127 | 128 | #[inline] 129 | pub fn flush(&self, options: CVOptionFlags) { 130 | unsafe { CVMetalTextureCacheFlush(self.as_concrete_TypeRef(), options) } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /core-video/src/opengl_buffer.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | base::{kCFAllocatorDefault, CFAllocatorRef, CFType, CFTypeID, TCFType}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::{CFString, CFStringRef}, 7 | }; 8 | use libc::size_t; 9 | 10 | use crate::{ 11 | buffer::TCVBuffer, 12 | image_buffer::{CVImageBufferRef, TCVImageBuffer}, 13 | r#return::{kCVReturnSuccess, CVReturn}, 14 | CGLContextObj, GLenum, GLint, 15 | }; 16 | 17 | pub type CVOpenGLBufferRef = CVImageBufferRef; 18 | 19 | extern "C" { 20 | pub static kCVOpenGLBufferWidth: CFStringRef; 21 | pub static kCVOpenGLBufferHeight: CFStringRef; 22 | pub static kCVOpenGLBufferTarget: CFStringRef; 23 | pub static kCVOpenGLBufferInternalFormat: CFStringRef; 24 | pub static kCVOpenGLBufferMaximumMipmapLevel: CFStringRef; 25 | 26 | pub fn CVOpenGLBufferGetTypeID() -> CFTypeID; 27 | pub fn CVOpenGLBufferRetain(buffer: CVOpenGLBufferRef) -> CVOpenGLBufferRef; 28 | pub fn CVOpenGLBufferRelease(buffer: CVOpenGLBufferRef); 29 | pub fn CVOpenGLBufferCreate( 30 | allocator: CFAllocatorRef, 31 | width: size_t, 32 | height: size_t, 33 | attributes: CFDictionaryRef, 34 | bufferOut: *mut CVOpenGLBufferRef, 35 | ) -> CVReturn; 36 | pub fn CVOpenGLBufferGetAttributes(openGLBuffer: CVOpenGLBufferRef) -> CFDictionaryRef; 37 | pub fn CVOpenGLBufferAttach(openGLBuffer: CVOpenGLBufferRef, cglContext: CGLContextObj, face: GLenum, level: GLint, screen: GLint) -> CVReturn; 38 | } 39 | 40 | pub enum CVOpenGLBufferKeys { 41 | Width, 42 | Height, 43 | Target, 44 | InternalFormat, 45 | MaximumMipmapLevel, 46 | } 47 | 48 | impl From for CFStringRef { 49 | fn from(key: CVOpenGLBufferKeys) -> Self { 50 | unsafe { 51 | match key { 52 | CVOpenGLBufferKeys::Width => kCVOpenGLBufferWidth, 53 | CVOpenGLBufferKeys::Height => kCVOpenGLBufferHeight, 54 | CVOpenGLBufferKeys::Target => kCVOpenGLBufferTarget, 55 | CVOpenGLBufferKeys::InternalFormat => kCVOpenGLBufferInternalFormat, 56 | CVOpenGLBufferKeys::MaximumMipmapLevel => kCVOpenGLBufferMaximumMipmapLevel, 57 | } 58 | } 59 | } 60 | } 61 | 62 | impl From for CFString { 63 | fn from(key: CVOpenGLBufferKeys) -> Self { 64 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) } 65 | } 66 | } 67 | 68 | impl TCVBuffer for CVOpenGLBuffer {} 69 | impl TCVImageBuffer for CVOpenGLBuffer {} 70 | pub struct CVOpenGLBuffer(CVOpenGLBufferRef); 71 | 72 | impl Drop for CVOpenGLBuffer { 73 | fn drop(&mut self) { 74 | unsafe { CVOpenGLBufferRelease(self.0) } 75 | } 76 | } 77 | 78 | impl_TCFType!(CVOpenGLBuffer, CVOpenGLBufferRef, CVOpenGLBufferGetTypeID); 79 | impl_CFTypeDescription!(CVOpenGLBuffer); 80 | 81 | impl CVOpenGLBuffer { 82 | #[inline] 83 | pub fn new(width: size_t, height: size_t, attributes: Option<&CFDictionary>) -> Result { 84 | let mut buffer: CVOpenGLBufferRef = null_mut(); 85 | let status = unsafe { 86 | CVOpenGLBufferCreate(kCFAllocatorDefault, width, height, attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), &mut buffer) 87 | }; 88 | if status == kCVReturnSuccess { 89 | Ok(unsafe { TCFType::wrap_under_create_rule(buffer) }) 90 | } else { 91 | Err(status) 92 | } 93 | } 94 | 95 | #[inline] 96 | pub fn get_attributes(&self) -> Option> { 97 | unsafe { 98 | let attributes = CVOpenGLBufferGetAttributes(self.as_concrete_TypeRef()); 99 | if attributes.is_null() { 100 | None 101 | } else { 102 | Some(TCFType::wrap_under_create_rule(attributes)) 103 | } 104 | } 105 | } 106 | 107 | #[inline] 108 | pub unsafe fn attach(&self, cgl_context: CGLContextObj, face: GLenum, level: GLint, screen: GLint) -> Result<(), CVReturn> { 109 | let status = unsafe { CVOpenGLBufferAttach(self.as_concrete_TypeRef(), cgl_context, face, level, screen) }; 110 | if status == kCVReturnSuccess { 111 | Ok(()) 112 | } else { 113 | Err(status) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /core-video/src/opengl_buffer_pool.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | base::{kCFAllocatorDefault, CFAllocatorRef, CFType, CFTypeID, TCFType}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::{CFString, CFStringRef}, 7 | }; 8 | use libc::c_void; 9 | 10 | use crate::{ 11 | opengl_buffer::{CVOpenGLBuffer, CVOpenGLBufferRef}, 12 | r#return::{kCVReturnSuccess, CVReturn}, 13 | }; 14 | 15 | #[repr(C)] 16 | pub struct __CVOpenGLBufferPool(c_void); 17 | 18 | pub type CVOpenGLBufferPoolRef = *mut __CVOpenGLBufferPool; 19 | 20 | extern "C" { 21 | pub static kCVOpenGLBufferPoolMinimumBufferCountKey: CFStringRef; 22 | pub static kCVOpenGLBufferPoolMaximumBufferAgeKey: CFStringRef; 23 | 24 | pub fn CVOpenGLBufferPoolGetTypeID() -> CFTypeID; 25 | pub fn CVOpenGLBufferPoolRetain(openGLBufferPool: CVOpenGLBufferPoolRef) -> CVOpenGLBufferPoolRef; 26 | pub fn CVOpenGLBufferPoolRelease(openGLBufferPool: CVOpenGLBufferPoolRef); 27 | pub fn CVOpenGLBufferPoolCreate( 28 | allocator: CFAllocatorRef, 29 | poolAttributes: CFDictionaryRef, 30 | openGLBufferAttributes: CFDictionaryRef, 31 | poolOut: *mut CVOpenGLBufferPoolRef, 32 | ) -> CVReturn; 33 | pub fn CVOpenGLBufferPoolGetAttributes(pool: CVOpenGLBufferPoolRef) -> CFDictionaryRef; 34 | pub fn CVOpenGLBufferPoolGetOpenGLBufferAttributes(pool: CVOpenGLBufferPoolRef) -> CFDictionaryRef; 35 | pub fn CVOpenGLBufferPoolCreateOpenGLBuffer( 36 | allocator: CFAllocatorRef, 37 | openGLBufferPool: CVOpenGLBufferPoolRef, 38 | openGLBufferOut: *mut CVOpenGLBufferRef, 39 | ) -> CVReturn; 40 | } 41 | 42 | pub enum CVOpenGLBufferPoolKeys { 43 | MinimumBufferCount, 44 | MaximumBufferAge, 45 | } 46 | 47 | impl From for CFStringRef { 48 | fn from(key: CVOpenGLBufferPoolKeys) -> Self { 49 | unsafe { 50 | match key { 51 | CVOpenGLBufferPoolKeys::MinimumBufferCount => kCVOpenGLBufferPoolMinimumBufferCountKey, 52 | CVOpenGLBufferPoolKeys::MaximumBufferAge => kCVOpenGLBufferPoolMaximumBufferAgeKey, 53 | } 54 | } 55 | } 56 | } 57 | 58 | impl From for CFString { 59 | fn from(key: CVOpenGLBufferPoolKeys) -> Self { 60 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) } 61 | } 62 | } 63 | 64 | pub struct CVOpenGLBufferPool(CVOpenGLBufferPoolRef); 65 | 66 | impl Drop for CVOpenGLBufferPool { 67 | fn drop(&mut self) { 68 | unsafe { CVOpenGLBufferPoolRelease(self.0) } 69 | } 70 | } 71 | 72 | impl_TCFType!(CVOpenGLBufferPool, CVOpenGLBufferPoolRef, CVOpenGLBufferPoolGetTypeID); 73 | impl_CFTypeDescription!(CVOpenGLBufferPool); 74 | 75 | impl CVOpenGLBufferPool { 76 | #[inline] 77 | pub fn new( 78 | pool_attributes: Option<&CFDictionary>, 79 | opengl_buffer_attributes: Option<&CFDictionary>, 80 | ) -> Result { 81 | let mut pool: CVOpenGLBufferPoolRef = null_mut(); 82 | let status = unsafe { 83 | CVOpenGLBufferPoolCreate( 84 | kCFAllocatorDefault, 85 | pool_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 86 | opengl_buffer_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 87 | &mut pool, 88 | ) 89 | }; 90 | if status == kCVReturnSuccess { 91 | Ok(unsafe { TCFType::wrap_under_create_rule(pool) }) 92 | } else { 93 | Err(status) 94 | } 95 | } 96 | 97 | #[inline] 98 | pub fn get_attributes(&self) -> Option> { 99 | unsafe { 100 | let attributes = CVOpenGLBufferPoolGetAttributes(self.as_concrete_TypeRef()); 101 | if attributes.is_null() { 102 | None 103 | } else { 104 | Some(TCFType::wrap_under_create_rule(attributes)) 105 | } 106 | } 107 | } 108 | 109 | #[inline] 110 | pub fn get_opengl_buffer_attributes(&self) -> Option> { 111 | unsafe { 112 | let attributes = CVOpenGLBufferPoolGetOpenGLBufferAttributes(self.as_concrete_TypeRef()); 113 | if attributes.is_null() { 114 | None 115 | } else { 116 | Some(TCFType::wrap_under_create_rule(attributes)) 117 | } 118 | } 119 | } 120 | 121 | #[inline] 122 | pub fn create_open_gl_buffer(&self) -> Result { 123 | let mut buffer: CVOpenGLBufferRef = null_mut(); 124 | let status = unsafe { CVOpenGLBufferPoolCreateOpenGLBuffer(kCFAllocatorDefault, self.as_concrete_TypeRef(), &mut buffer) }; 125 | if status == kCVReturnSuccess { 126 | Ok(unsafe { TCFType::wrap_under_create_rule(buffer) }) 127 | } else { 128 | Err(status) 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /core-video/src/opengl_es_texture.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::base::{Boolean, CFTypeID, TCFType}; 2 | 3 | use crate::{ 4 | buffer::TCVBuffer, 5 | image_buffer::{CVImageBufferRef, TCVImageBuffer}, 6 | GLenum, GLuint, 7 | }; 8 | 9 | pub type CVOpenGLESTextureRef = CVImageBufferRef; 10 | 11 | extern "C" { 12 | pub fn CVOpenGLESTextureGetTypeID() -> CFTypeID; 13 | pub fn CVOpenGLESTextureGetTarget(image: CVOpenGLESTextureRef) -> GLenum; 14 | pub fn CVOpenGLESTextureGetName(image: CVOpenGLESTextureRef) -> GLuint; 15 | pub fn CVOpenGLESTextureIsFlipped(image: CVOpenGLESTextureRef) -> Boolean; 16 | pub fn CVOpenGLESTextureGetCleanTexCoords( 17 | image: CVOpenGLESTextureRef, 18 | lowerLeft: *mut f32, 19 | lowerRight: *mut f32, 20 | upperRight: *mut f32, 21 | upperLeft: *mut f32, 22 | ); 23 | } 24 | 25 | impl TCVBuffer for CVOpenGLESTexture {} 26 | impl TCVImageBuffer for CVOpenGLESTexture {} 27 | 28 | declare_TCFType! { 29 | CVOpenGLESTexture, CVOpenGLESTextureRef 30 | } 31 | impl_TCFType!(CVOpenGLESTexture, CVOpenGLESTextureRef, CVOpenGLESTextureGetTypeID); 32 | impl_CFTypeDescription!(CVOpenGLESTexture); 33 | 34 | impl CVOpenGLESTexture { 35 | #[inline] 36 | pub fn get_target(&self) -> GLenum { 37 | unsafe { CVOpenGLESTextureGetTarget(self.as_concrete_TypeRef()) } 38 | } 39 | 40 | #[inline] 41 | pub fn get_name(&self) -> GLuint { 42 | unsafe { CVOpenGLESTextureGetName(self.as_concrete_TypeRef()) } 43 | } 44 | 45 | #[inline] 46 | pub fn is_flipped(&self) -> bool { 47 | unsafe { CVOpenGLESTextureIsFlipped(self.as_concrete_TypeRef()) != 0 } 48 | } 49 | 50 | #[inline] 51 | pub fn get_clean_tex_coords(&self) -> (f32, f32, f32, f32) { 52 | let mut lower_left = 0.0; 53 | let mut lower_right = 0.0; 54 | let mut upper_right = 0.0; 55 | let mut upper_left = 0.0; 56 | unsafe { 57 | CVOpenGLESTextureGetCleanTexCoords(self.as_concrete_TypeRef(), &mut lower_left, &mut lower_right, &mut upper_right, &mut upper_left); 58 | } 59 | (lower_left, lower_right, upper_right, upper_left) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /core-video/src/opengl_es_texture_cache.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | base::{kCFAllocatorDefault, CFAllocatorRef, CFType, CFTypeID, TCFType}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::CFString, 7 | }; 8 | use libc::{c_void, size_t}; 9 | #[cfg(feature = "objc")] 10 | use objc2::runtime::NSObject; 11 | 12 | use crate::{ 13 | base::CVOptionFlags, 14 | image_buffer::{CVImageBuffer, CVImageBufferRef}, 15 | opengl_es_texture::{CVOpenGLESTexture, CVOpenGLESTextureRef}, 16 | r#return::{kCVReturnSuccess, CVReturn}, 17 | GLenum, GLint, GLsizei, 18 | }; 19 | 20 | #[repr(C)] 21 | pub struct __CVOpenGLESTextureCache(c_void); 22 | 23 | pub type CVOpenGLESTextureCacheRef = *mut __CVOpenGLESTextureCache; 24 | 25 | #[cfg(feature = "objc")] 26 | pub type CVEAGLContext = *mut NSObject; 27 | #[cfg(not(feature = "objc"))] 28 | pub type CVEAGLContext = *mut c_void; 29 | 30 | extern "C" { 31 | pub fn CVOpenGLESTextureCacheCreate( 32 | allocator: CFAllocatorRef, 33 | cacheAttributes: CFDictionaryRef, 34 | eaglContext: CVEAGLContext, 35 | textureAttributes: CFDictionaryRef, 36 | cacheOut: *mut CVOpenGLESTextureCacheRef, 37 | ) -> CVReturn; 38 | pub fn CVOpenGLESTextureCacheCreateTextureFromImage( 39 | allocator: CFAllocatorRef, 40 | textureCache: CVOpenGLESTextureCacheRef, 41 | sourceImage: CVImageBufferRef, 42 | textureAttributes: CFDictionaryRef, 43 | target: GLenum, 44 | internalFormat: GLint, 45 | width: GLsizei, 46 | height: GLsizei, 47 | format: GLenum, 48 | type_: GLenum, 49 | planeIndex: size_t, 50 | textureOut: *mut CVOpenGLESTextureRef, 51 | ) -> CVReturn; 52 | pub fn CVOpenGLESTextureCacheFlush(textureCache: CVOpenGLESTextureCacheRef, options: CVOptionFlags); 53 | pub fn CVOpenGLESTextureCacheGetTypeID() -> CFTypeID; 54 | } 55 | 56 | declare_TCFType! { 57 | CVOpenGLESTextureCache, CVOpenGLESTextureCacheRef 58 | } 59 | impl_TCFType!(CVOpenGLESTextureCache, CVOpenGLESTextureCacheRef, CVOpenGLESTextureCacheGetTypeID); 60 | impl_CFTypeDescription!(CVOpenGLESTextureCache); 61 | 62 | impl CVOpenGLESTextureCache { 63 | #[inline] 64 | pub fn new( 65 | cache_attributes: Option<&CFDictionary>, 66 | eagl_context: CVEAGLContext, 67 | texture_attributes: Option<&CFDictionary>, 68 | ) -> Result { 69 | let mut cache: CVOpenGLESTextureCacheRef = null_mut(); 70 | let status = unsafe { 71 | CVOpenGLESTextureCacheCreate( 72 | kCFAllocatorDefault, 73 | cache_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 74 | eagl_context, 75 | texture_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 76 | &mut cache, 77 | ) 78 | }; 79 | if status == kCVReturnSuccess { 80 | Ok(unsafe { TCFType::wrap_under_create_rule(cache) }) 81 | } else { 82 | Err(status) 83 | } 84 | } 85 | 86 | #[inline] 87 | pub fn create_texture_from_image( 88 | &self, 89 | source_image: &CVImageBuffer, 90 | texture_attributes: Option<&CFDictionary>, 91 | target: GLenum, 92 | internal_format: GLint, 93 | width: GLsizei, 94 | height: GLsizei, 95 | format: GLenum, 96 | type_: GLenum, 97 | plane_index: size_t, 98 | ) -> Result { 99 | let mut texture: CVOpenGLESTextureRef = null_mut(); 100 | let status = unsafe { 101 | CVOpenGLESTextureCacheCreateTextureFromImage( 102 | kCFAllocatorDefault, 103 | self.as_concrete_TypeRef(), 104 | source_image.as_concrete_TypeRef(), 105 | texture_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 106 | target, 107 | internal_format, 108 | width, 109 | height, 110 | format, 111 | type_, 112 | plane_index, 113 | &mut texture, 114 | ) 115 | }; 116 | if status == kCVReturnSuccess { 117 | Ok(unsafe { TCFType::wrap_under_create_rule(texture) }) 118 | } else { 119 | Err(status) 120 | } 121 | } 122 | 123 | #[inline] 124 | pub fn flush(&self, options: CVOptionFlags) { 125 | unsafe { CVOpenGLESTextureCacheFlush(self.as_concrete_TypeRef(), options) } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /core-video/src/opengl_texture.rs: -------------------------------------------------------------------------------- 1 | use core_foundation::base::{Boolean, CFTypeID, TCFType}; 2 | 3 | use crate::{ 4 | buffer::TCVBuffer, 5 | image_buffer::{CVImageBufferRef, TCVImageBuffer}, 6 | GLenum, GLuint, 7 | }; 8 | 9 | pub type CVOpenGLTextureRef = CVImageBufferRef; 10 | 11 | extern "C" { 12 | pub fn CVOpenGLTextureGetTypeID() -> CFTypeID; 13 | pub fn CVOpenGLTextureRetain(texture: CVOpenGLTextureRef) -> CVOpenGLTextureRef; 14 | pub fn CVOpenGLTextureRelease(texture: CVOpenGLTextureRef); 15 | pub fn CVOpenGLTextureGetTarget(image: CVOpenGLTextureRef) -> GLenum; 16 | pub fn CVOpenGLTextureGetName(image: CVOpenGLTextureRef) -> GLuint; 17 | pub fn CVOpenGLTextureIsFlipped(image: CVOpenGLTextureRef) -> Boolean; 18 | pub fn CVOpenGLTextureGetCleanTexCoords( 19 | image: CVOpenGLTextureRef, 20 | lowerLeft: *mut f32, 21 | lowerRight: *mut f32, 22 | upperRight: *mut f32, 23 | upperLeft: *mut f32, 24 | ); 25 | } 26 | 27 | impl TCVBuffer for CVOpenGLTexture {} 28 | impl TCVImageBuffer for CVOpenGLTexture {} 29 | 30 | pub struct CVOpenGLTexture(CVOpenGLTextureRef); 31 | 32 | impl Drop for CVOpenGLTexture { 33 | fn drop(&mut self) { 34 | unsafe { CVOpenGLTextureRelease(self.0) } 35 | } 36 | } 37 | 38 | impl_TCFType!(CVOpenGLTexture, CVOpenGLTextureRef, CVOpenGLTextureGetTypeID); 39 | impl_CFTypeDescription!(CVOpenGLTexture); 40 | 41 | impl CVOpenGLTexture { 42 | #[inline] 43 | pub fn get_target(&self) -> GLenum { 44 | unsafe { CVOpenGLTextureGetTarget(self.as_concrete_TypeRef()) } 45 | } 46 | 47 | #[inline] 48 | pub fn get_name(&self) -> GLuint { 49 | unsafe { CVOpenGLTextureGetName(self.as_concrete_TypeRef()) } 50 | } 51 | 52 | #[inline] 53 | pub fn is_flipped(&self) -> bool { 54 | unsafe { CVOpenGLTextureIsFlipped(self.as_concrete_TypeRef()) != 0 } 55 | } 56 | 57 | #[inline] 58 | pub fn get_clean_tex_coords(&self) -> (f32, f32, f32, f32) { 59 | let mut lower_left = 0.0; 60 | let mut lower_right = 0.0; 61 | let mut upper_right = 0.0; 62 | let mut upper_left = 0.0; 63 | unsafe { 64 | CVOpenGLTextureGetCleanTexCoords(self.as_concrete_TypeRef(), &mut lower_left, &mut lower_right, &mut upper_right, &mut upper_left); 65 | } 66 | (lower_left, lower_right, upper_right, upper_left) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /core-video/src/opengl_texture_cache.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | base::{kCFAllocatorDefault, CFAllocatorRef, CFType, CFTypeID, TCFType}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::{CFString, CFStringRef}, 7 | }; 8 | use libc::c_void; 9 | 10 | use crate::{ 11 | base::CVOptionFlags, 12 | image_buffer::{CVImageBuffer, CVImageBufferRef}, 13 | opengl_texture::{CVOpenGLTexture, CVOpenGLTextureRef}, 14 | r#return::{kCVReturnSuccess, CVReturn}, 15 | CGLContextObj, CGLPixelFormatObj, 16 | }; 17 | 18 | #[repr(C)] 19 | pub struct __CVOpenGLTextureCache(c_void); 20 | 21 | pub type CVOpenGLTextureCacheRef = *mut __CVOpenGLTextureCache; 22 | 23 | extern "C" { 24 | pub static kCVOpenGLTextureCacheChromaSamplingModeKey: CFStringRef; 25 | pub static kCVOpenGLTextureCacheChromaSamplingModeAutomatic: CFStringRef; 26 | pub static kCVOpenGLTextureCacheChromaSamplingModeHighestQuality: CFStringRef; 27 | pub static kCVOpenGLTextureCacheChromaSamplingModeBestPerformance: CFStringRef; 28 | 29 | pub fn CVOpenGLTextureCacheGetTypeID() -> CFTypeID; 30 | pub fn CVOpenGLTextureCacheRetain(textureCache: CVOpenGLTextureCacheRef) -> CVOpenGLTextureCacheRef; 31 | pub fn CVOpenGLTextureCacheRelease(textureCache: CVOpenGLTextureCacheRef); 32 | pub fn CVOpenGLTextureCacheCreate( 33 | allocator: CFAllocatorRef, 34 | cacheAttributes: CFDictionaryRef, 35 | cglContext: CGLContextObj, 36 | cglPixelFormat: CGLPixelFormatObj, 37 | textureAttributes: CFDictionaryRef, 38 | cacheOut: *mut CVOpenGLTextureCacheRef, 39 | ) -> CVReturn; 40 | pub fn CVOpenGLTextureCacheCreateTextureFromImage( 41 | allocator: CFAllocatorRef, 42 | textureCache: CVOpenGLTextureCacheRef, 43 | sourceImage: CVImageBufferRef, 44 | attributes: CFDictionaryRef, 45 | textureOut: *mut CVOpenGLTextureRef, 46 | ) -> CVReturn; 47 | pub fn CVOpenGLTextureCacheFlush(textureCache: CVOpenGLTextureCacheRef, options: CVOptionFlags); 48 | } 49 | 50 | pub enum CVOpenGLTextureCacheKeys { 51 | ChromaSamplingMode, 52 | } 53 | 54 | impl From for CFStringRef { 55 | fn from(key: CVOpenGLTextureCacheKeys) -> Self { 56 | unsafe { 57 | match key { 58 | CVOpenGLTextureCacheKeys::ChromaSamplingMode => kCVOpenGLTextureCacheChromaSamplingModeKey, 59 | } 60 | } 61 | } 62 | } 63 | 64 | impl From for CFString { 65 | fn from(key: CVOpenGLTextureCacheKeys) -> Self { 66 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) } 67 | } 68 | } 69 | 70 | pub enum CVOpenGLTextureCacheChromaSamplingMode { 71 | Automatic, 72 | HighestQuality, 73 | BestPerformance, 74 | } 75 | 76 | impl From for CFStringRef { 77 | fn from(mode: CVOpenGLTextureCacheChromaSamplingMode) -> Self { 78 | unsafe { 79 | match mode { 80 | CVOpenGLTextureCacheChromaSamplingMode::Automatic => kCVOpenGLTextureCacheChromaSamplingModeAutomatic, 81 | CVOpenGLTextureCacheChromaSamplingMode::HighestQuality => kCVOpenGLTextureCacheChromaSamplingModeHighestQuality, 82 | CVOpenGLTextureCacheChromaSamplingMode::BestPerformance => kCVOpenGLTextureCacheChromaSamplingModeBestPerformance, 83 | } 84 | } 85 | } 86 | } 87 | 88 | impl From for CFString { 89 | fn from(mode: CVOpenGLTextureCacheChromaSamplingMode) -> Self { 90 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(mode)) } 91 | } 92 | } 93 | 94 | pub struct CVOpenGLTextureCache(CVOpenGLTextureCacheRef); 95 | 96 | impl Drop for CVOpenGLTextureCache { 97 | fn drop(&mut self) { 98 | unsafe { CVOpenGLTextureCacheRelease(self.0) } 99 | } 100 | } 101 | 102 | impl_TCFType!(CVOpenGLTextureCache, CVOpenGLTextureCacheRef, CVOpenGLTextureCacheGetTypeID); 103 | impl_CFTypeDescription!(CVOpenGLTextureCache); 104 | 105 | impl CVOpenGLTextureCache { 106 | #[inline] 107 | pub unsafe fn new( 108 | cache_attributes: Option<&CFDictionary>, 109 | cgl_context: CGLContextObj, 110 | cgl_pixel_format: CGLPixelFormatObj, 111 | texture_attributes: Option<&CFDictionary>, 112 | ) -> Result { 113 | let mut cache: CVOpenGLTextureCacheRef = null_mut(); 114 | let status = unsafe { 115 | CVOpenGLTextureCacheCreate( 116 | kCFAllocatorDefault, 117 | cache_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 118 | cgl_context, 119 | cgl_pixel_format, 120 | texture_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 121 | &mut cache, 122 | ) 123 | }; 124 | if status == kCVReturnSuccess { 125 | Ok(unsafe { TCFType::wrap_under_create_rule(cache) }) 126 | } else { 127 | Err(status) 128 | } 129 | } 130 | 131 | #[inline] 132 | pub fn create_texture_from_image( 133 | &self, 134 | source_image: &CVImageBuffer, 135 | attributes: Option<&CFDictionary>, 136 | ) -> Result { 137 | let mut texture: CVOpenGLTextureRef = null_mut(); 138 | let status = unsafe { 139 | CVOpenGLTextureCacheCreateTextureFromImage( 140 | kCFAllocatorDefault, 141 | self.as_concrete_TypeRef(), 142 | source_image.as_concrete_TypeRef(), 143 | attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 144 | &mut texture, 145 | ) 146 | }; 147 | if status == kCVReturnSuccess { 148 | Ok(unsafe { TCFType::wrap_under_create_rule(texture) }) 149 | } else { 150 | Err(status) 151 | } 152 | } 153 | 154 | #[inline] 155 | pub fn flush(&self, options: CVOptionFlags) { 156 | unsafe { CVOpenGLTextureCacheFlush(self.as_concrete_TypeRef(), options) } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /core-video/src/pixel_buffer_io_surface.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | base::{kCFAllocatorDefault, CFAllocatorRef, CFType, TCFType}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::{CFString, CFStringRef}, 7 | }; 8 | use io_surface::IOSurface; 9 | 10 | use crate::{ 11 | io_surface::IOSurfaceRef, 12 | pixel_buffer::{CVPixelBuffer, CVPixelBufferRef}, 13 | r#return::{kCVReturnSuccess, CVReturn}, 14 | }; 15 | 16 | extern "C" { 17 | pub static kCVPixelBufferIOSurfaceOpenGLTextureCompatibilityKey: CFStringRef; 18 | pub static kCVPixelBufferIOSurfaceOpenGLFBOCompatibilityKey: CFStringRef; 19 | pub static kCVPixelBufferIOSurfaceCoreAnimationCompatibilityKey: CFStringRef; 20 | pub static kCVPixelBufferIOSurfaceOpenGLESTextureCompatibilityKey: CFStringRef; 21 | pub static kCVPixelBufferIOSurfaceOpenGLESFBOCompatibilityKey: CFStringRef; 22 | 23 | pub fn CVPixelBufferGetIOSurface(pixelBuffer: CVPixelBufferRef) -> IOSurfaceRef; 24 | pub fn CVPixelBufferCreateWithIOSurface( 25 | allocator: CFAllocatorRef, 26 | surface: IOSurfaceRef, 27 | pixelBufferAttributes: CFDictionaryRef, 28 | pixelBufferOut: *mut CVPixelBufferRef, 29 | ) -> CVReturn; 30 | } 31 | 32 | pub enum CVPixelBufferIOSurfaceKeys { 33 | OpenGLTextureCompatibility, 34 | OpenGLFBOCompatibility, 35 | CoreAnimationCompatibility, 36 | OpenGLESTextureCompatibility, 37 | OpenGLESFBOCompatibility, 38 | } 39 | 40 | impl From for CFStringRef { 41 | fn from(key: CVPixelBufferIOSurfaceKeys) -> Self { 42 | unsafe { 43 | match key { 44 | CVPixelBufferIOSurfaceKeys::OpenGLTextureCompatibility => kCVPixelBufferIOSurfaceOpenGLTextureCompatibilityKey, 45 | CVPixelBufferIOSurfaceKeys::OpenGLFBOCompatibility => kCVPixelBufferIOSurfaceOpenGLFBOCompatibilityKey, 46 | CVPixelBufferIOSurfaceKeys::CoreAnimationCompatibility => kCVPixelBufferIOSurfaceCoreAnimationCompatibilityKey, 47 | CVPixelBufferIOSurfaceKeys::OpenGLESTextureCompatibility => kCVPixelBufferIOSurfaceOpenGLESTextureCompatibilityKey, 48 | CVPixelBufferIOSurfaceKeys::OpenGLESFBOCompatibility => kCVPixelBufferIOSurfaceOpenGLESFBOCompatibilityKey, 49 | } 50 | } 51 | } 52 | } 53 | 54 | impl From for CFString { 55 | fn from(key: CVPixelBufferIOSurfaceKeys) -> Self { 56 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) } 57 | } 58 | } 59 | 60 | impl CVPixelBuffer { 61 | #[inline] 62 | pub fn from_io_surface(io_surface: &IOSurface, options: Option<&CFDictionary>) -> Result { 63 | let mut pixel_buffer: CVPixelBufferRef = null_mut(); 64 | let status = unsafe { 65 | CVPixelBufferCreateWithIOSurface( 66 | kCFAllocatorDefault, 67 | io_surface.as_concrete_TypeRef(), 68 | options.map_or(null(), |options| options.as_concrete_TypeRef()), 69 | &mut pixel_buffer, 70 | ) 71 | }; 72 | if status == kCVReturnSuccess { 73 | Ok(unsafe { TCFType::wrap_under_create_rule(pixel_buffer) }) 74 | } else { 75 | Err(status) 76 | } 77 | } 78 | 79 | #[inline] 80 | pub fn get_io_surface(&self) -> Option { 81 | unsafe { 82 | let surface = CVPixelBufferGetIOSurface(self.as_concrete_TypeRef()); 83 | if surface.is_null() { 84 | None 85 | } else { 86 | Some(TCFType::wrap_under_create_rule(surface)) 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /core-video/src/pixel_buffer_pool.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{null, null_mut}; 2 | 3 | use core_foundation::{ 4 | base::{kCFAllocatorDefault, CFAllocatorRef, CFType, CFTypeID, TCFType}, 5 | dictionary::{CFDictionary, CFDictionaryRef}, 6 | string::{CFString, CFStringRef}, 7 | }; 8 | use libc::c_void; 9 | 10 | use crate::{ 11 | base::CVOptionFlags, 12 | pixel_buffer::{CVPixelBuffer, CVPixelBufferRef}, 13 | r#return::{kCVReturnSuccess, CVReturn}, 14 | }; 15 | 16 | #[repr(C)] 17 | pub struct __CVPixelBufferPool(c_void); 18 | 19 | pub type CVPixelBufferPoolRef = *mut __CVPixelBufferPool; 20 | 21 | pub type CVPixelBufferPoolFlushFlags = CVOptionFlags; 22 | 23 | pub const kCVPixelBufferPoolFlushExcessBuffers: CVPixelBufferPoolFlushFlags = 1; 24 | 25 | extern "C" { 26 | pub static kCVPixelBufferPoolMinimumBufferCountKey: CFStringRef; 27 | pub static kCVPixelBufferPoolMaximumBufferAgeKey: CFStringRef; 28 | 29 | pub static kCVPixelBufferPoolAllocationThresholdKey: CFStringRef; 30 | pub static kCVPixelBufferPoolFreeBufferNotification: CFStringRef; 31 | 32 | pub fn CVPixelBufferPoolGetTypeID() -> CFTypeID; 33 | pub fn CVPixelBufferPoolRetain(pixelBufferPool: CVPixelBufferPoolRef) -> CVPixelBufferPoolRef; 34 | pub fn CVPixelBufferPoolRelease(pixelBufferPool: CVPixelBufferPoolRef); 35 | pub fn CVPixelBufferPoolCreate( 36 | allocator: CFAllocatorRef, 37 | poolAttributes: CFDictionaryRef, 38 | pixelBufferAttributes: CFDictionaryRef, 39 | poolOut: *mut CVPixelBufferPoolRef, 40 | ) -> CVReturn; 41 | pub fn CVPixelBufferPoolGetAttributes(pool: CVPixelBufferPoolRef) -> CFDictionaryRef; 42 | pub fn CVPixelBufferPoolGetPixelBufferAttributes(pool: CVPixelBufferPoolRef) -> CFDictionaryRef; 43 | pub fn CVPixelBufferPoolCreatePixelBuffer( 44 | allocator: CFAllocatorRef, 45 | pixelBufferPool: CVPixelBufferPoolRef, 46 | pixelBufferOut: *mut CVPixelBufferRef, 47 | ) -> CVReturn; 48 | pub fn CVPixelBufferPoolCreatePixelBufferWithAuxAttributes( 49 | allocator: CFAllocatorRef, 50 | pixelBufferPool: CVPixelBufferPoolRef, 51 | auxAttributes: CFDictionaryRef, 52 | pixelBufferOut: *mut CVPixelBufferRef, 53 | ) -> CVReturn; 54 | pub fn CVPixelBufferPoolFlush(pool: CVPixelBufferPoolRef, options: CVPixelBufferPoolFlushFlags); 55 | } 56 | 57 | pub enum CVPixelBufferPoolKeys { 58 | MinimumBufferCount, 59 | MaximumBufferAge, 60 | AllocationThreshold, 61 | FreeBufferNotification, 62 | } 63 | 64 | impl From for CFStringRef { 65 | fn from(key: CVPixelBufferPoolKeys) -> CFStringRef { 66 | match key { 67 | CVPixelBufferPoolKeys::MinimumBufferCount => unsafe { kCVPixelBufferPoolMinimumBufferCountKey }, 68 | CVPixelBufferPoolKeys::MaximumBufferAge => unsafe { kCVPixelBufferPoolMaximumBufferAgeKey }, 69 | CVPixelBufferPoolKeys::AllocationThreshold => unsafe { kCVPixelBufferPoolAllocationThresholdKey }, 70 | CVPixelBufferPoolKeys::FreeBufferNotification => unsafe { kCVPixelBufferPoolFreeBufferNotification }, 71 | } 72 | } 73 | } 74 | 75 | impl From for CFString { 76 | fn from(key: CVPixelBufferPoolKeys) -> CFString { 77 | unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) } 78 | } 79 | } 80 | 81 | pub struct CVPixelBufferPool(CVPixelBufferPoolRef); 82 | 83 | impl Drop for CVPixelBufferPool { 84 | fn drop(&mut self) { 85 | unsafe { CVPixelBufferPoolRelease(self.0) } 86 | } 87 | } 88 | 89 | impl_TCFType!(CVPixelBufferPool, CVPixelBufferPoolRef, CVPixelBufferPoolGetTypeID); 90 | impl_CFTypeDescription!(CVPixelBufferPool); 91 | 92 | impl CVPixelBufferPool { 93 | #[inline] 94 | pub fn new( 95 | pool_attributes: Option<&CFDictionary>, 96 | pixel_buffer_attributes: Option<&CFDictionary>, 97 | ) -> Result { 98 | let mut pool: CVPixelBufferPoolRef = null_mut(); 99 | let status = unsafe { 100 | CVPixelBufferPoolCreate( 101 | kCFAllocatorDefault, 102 | pool_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 103 | pixel_buffer_attributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 104 | &mut pool, 105 | ) 106 | }; 107 | if status == kCVReturnSuccess { 108 | Ok(unsafe { TCFType::wrap_under_create_rule(pool) }) 109 | } else { 110 | Err(status) 111 | } 112 | } 113 | 114 | #[inline] 115 | pub fn get_attributes(&self) -> Option> { 116 | unsafe { 117 | let attributes = CVPixelBufferPoolGetAttributes(self.as_concrete_TypeRef()); 118 | if attributes.is_null() { 119 | None 120 | } else { 121 | Some(TCFType::wrap_under_create_rule(attributes)) 122 | } 123 | } 124 | } 125 | 126 | #[inline] 127 | pub fn get_pixel_buffer_attributes(&self) -> Option> { 128 | unsafe { 129 | let attributes = CVPixelBufferPoolGetPixelBufferAttributes(self.as_concrete_TypeRef()); 130 | if attributes.is_null() { 131 | None 132 | } else { 133 | Some(TCFType::wrap_under_create_rule(attributes)) 134 | } 135 | } 136 | } 137 | 138 | #[inline] 139 | pub fn create_pixel_buffer(&self) -> Result { 140 | let mut pixel_buffer: CVPixelBufferRef = null_mut(); 141 | let status = unsafe { CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, self.as_concrete_TypeRef(), &mut pixel_buffer) }; 142 | if status == kCVReturnSuccess { 143 | Ok(unsafe { TCFType::wrap_under_create_rule(pixel_buffer) }) 144 | } else { 145 | Err(status) 146 | } 147 | } 148 | 149 | #[inline] 150 | pub fn create_pixel_buffer_with_aux_attributes(&self, auxAttributes: Option<&CFDictionary>) -> Result { 151 | let mut pixel_buffer: CVPixelBufferRef = null_mut(); 152 | let status = unsafe { 153 | CVPixelBufferPoolCreatePixelBufferWithAuxAttributes( 154 | kCFAllocatorDefault, 155 | self.as_concrete_TypeRef(), 156 | auxAttributes.map_or(null(), |attrs| attrs.as_concrete_TypeRef()), 157 | &mut pixel_buffer, 158 | ) 159 | }; 160 | if status == kCVReturnSuccess { 161 | Ok(unsafe { TCFType::wrap_under_create_rule(pixel_buffer) }) 162 | } else { 163 | Err(status) 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /core-video/src/return.rs: -------------------------------------------------------------------------------- 1 | pub type CVReturn = i32; 2 | 3 | pub const kCVReturnSuccess: CVReturn = 0; 4 | pub const kCVReturnFirst: CVReturn = -6660; 5 | pub const kCVReturnError: CVReturn = kCVReturnFirst; 6 | pub const kCVReturnInvalidArgument: CVReturn = -6661; 7 | pub const kCVReturnAllocationFailed: CVReturn = -6662; 8 | pub const kCVReturnUnsupported: CVReturn = -6663; 9 | 10 | // DisplayLink related errors 11 | pub const kCVReturnInvalidDisplay: CVReturn = -6670; 12 | pub const kCVReturnDisplayLinkAlreadyRunning: CVReturn = -6671; 13 | pub const kCVReturnDisplayLinkNotRunning: CVReturn = -6672; 14 | pub const kCVReturnDisplayLinkCallbacksNotSet: CVReturn = -6673; 15 | 16 | // Buffer related errors 17 | pub const kCVReturnInvalidPixelFormat: CVReturn = -6680; 18 | pub const kCVReturnInvalidSize: CVReturn = -6681; 19 | pub const kCVReturnInvalidPixelBufferAttributes: CVReturn = -6682; 20 | pub const kCVReturnPixelBufferNotOpenGLCompatible: CVReturn = -6683; 21 | pub const kCVReturnPixelBufferNotMetalCompatible: CVReturn = -6684; 22 | 23 | // Buffer Pool related errors 24 | pub const kCVReturnWouldExceedAllocationThreshold: CVReturn = -6689; 25 | pub const kCVReturnPoolAllocationFailed: CVReturn = -6690; 26 | pub const kCVReturnInvalidPoolAttributes: CVReturn = -6691; 27 | pub const kCVReturnRetry: CVReturn = -6692; 28 | 29 | pub const kCVReturnLast: CVReturn = -6699; 30 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | version = "Two" 2 | 3 | binop_separator = "Back" 4 | enum_discrim_align_threshold = 30 5 | format_code_in_doc_comments = true 6 | format_macro_matchers = true 7 | group_imports = "StdExternalCrate" 8 | imports_granularity = "Crate" 9 | max_width = 150 10 | newline_style = "Native" 11 | normalize_comments = true 12 | single_line_if_else_max_width = 0 13 | struct_lit_single_line = false 14 | use_field_init_shorthand = true 15 | use_small_heuristics = "Max" 16 | use_try_shorthand = true 17 | wrap_comments = true 18 | -------------------------------------------------------------------------------- /screen-capture-kit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "screen-capture-kit" 3 | description = "Bindings to ScreenCaptureKit framework" 4 | homepage = "https://github.com/rust-media/apple-media-rs" 5 | repository = "https://github.com/rust-media/apple-media-rs" 6 | documentation = "https://docs.rs/screen-capture-kit" 7 | version = "0.3.1" 8 | authors = ["Zhou Wei "] 9 | license = "MIT OR Apache-2.0" 10 | categories = ["multimedia", "os::macos-apis"] 11 | keywords = ["screencapturekit", "screencapture"] 12 | edition = "2018" 13 | 14 | [dependencies] 15 | block2 = "0.5" 16 | core-foundation = { version = "0.10", default-features = false } 17 | core-graphics2 = { version = "0.4", path = "../core-graphics", default-features = false, features = ["display", "objc", "window"]} 18 | core-media = { version = "0.5", path = "../core-media", default-features = false, features = ["objc"] } 19 | dispatch2 = "0.1" 20 | libc = "0.2" 21 | objc2 = "0.5" 22 | objc2-foundation = { version = "0.2", features = ["NSArray", "NSDictionary", "NSError", "NSGeometry", "NSString"] } 23 | 24 | [dev-dependencies] 25 | core-audio-types = { version = "0.1", path = "../core-audio-types" } 26 | core-video = { version = "0.4", path = "../core-video", features = ["objc"] } 27 | 28 | [features] 29 | default = ["link"] 30 | link = ["core-foundation/link", "core-graphics2/link", "core-media/link"] 31 | 32 | [[example]] 33 | name = "screen_capture" 34 | 35 | [package.metadata.docs.rs] 36 | no-default-features = true 37 | default-target = "x86_64-apple-darwin" 38 | targets = [ 39 | "aarch64-apple-darwin", 40 | "x86_64-apple-darwin", 41 | ] 42 | -------------------------------------------------------------------------------- /screen-capture-kit/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /screen-capture-kit/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /screen-capture-kit/README.md: -------------------------------------------------------------------------------- 1 | # screen-capture-kit 2 | 3 | [![Version](https://img.shields.io/crates/v/screen-capture-kit)](https://crates.io/crates/screen-capture-kit) 4 | [![Documentation](https://docs.rs/screen-capture-kit/badge.svg)](https://docs.rs/screen-capture-kit) 5 | [![License](https://img.shields.io/badge/License-Apache%202-blue.svg)](LICENSE-APACHE) 6 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE-MIT) 7 | 8 | Rust bindings to ScreenCaptureKit framework 9 | -------------------------------------------------------------------------------- /screen-capture-kit/examples/screen_capture.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc::channel; 2 | 3 | use core_foundation::base::TCFType; 4 | use core_media::sample_buffer::{CMSampleBuffer, CMSampleBufferRef}; 5 | use core_video::pixel_buffer::CVPixelBuffer; 6 | use dispatch2::{Queue, QueueAttribute}; 7 | use libc::size_t; 8 | use objc2::{ 9 | declare_class, extern_methods, msg_send_id, mutability, 10 | rc::{Allocated, Id}, 11 | runtime::ProtocolObject, 12 | ClassType, DeclaredClass, 13 | }; 14 | use objc2_foundation::{NSArray, NSError, NSObject, NSObjectProtocol}; 15 | use screen_capture_kit::{ 16 | shareable_content::SCShareableContent, 17 | stream::{SCContentFilter, SCStream, SCStreamConfiguration, SCStreamDelegate, SCStreamOutput, SCStreamOutputType}, 18 | }; 19 | 20 | pub struct DelegateIvars {} 21 | 22 | declare_class!( 23 | struct Delegate; 24 | 25 | unsafe impl ClassType for Delegate { 26 | type Super = NSObject; 27 | type Mutability = mutability::Mutable; 28 | const NAME: &'static str = "StreamOutputSampleBufferDelegate"; 29 | } 30 | 31 | impl DeclaredClass for Delegate { 32 | type Ivars = DelegateIvars; 33 | } 34 | 35 | unsafe impl NSObjectProtocol for Delegate {} 36 | 37 | unsafe impl SCStreamOutput for Delegate { 38 | #[method(stream:didOutputSampleBuffer:ofType:)] 39 | unsafe fn stream_did_output_sample_buffer(&self, _stream: &SCStream, sample_buffer: CMSampleBufferRef, of_type: SCStreamOutputType) { 40 | if of_type != SCStreamOutputType::Screen { 41 | return; 42 | } 43 | let sample_buffer = CMSampleBuffer::wrap_under_get_rule(sample_buffer); 44 | if let Some(image_buffer) = sample_buffer.get_image_buffer() { 45 | if let Some(pixel_buffer) = image_buffer.downcast::() { 46 | println!("pixel buffer: {:?}", pixel_buffer); 47 | } 48 | } 49 | } 50 | } 51 | 52 | unsafe impl SCStreamDelegate for Delegate { 53 | #[method(stream:didStopWithError:)] 54 | unsafe fn stream_did_stop_with_error(&self, _stream: &SCStream, error: &NSError) { 55 | println!("error: {:?}", error); 56 | } 57 | } 58 | 59 | unsafe impl Delegate { 60 | #[method_id(init)] 61 | fn init(this: Allocated) -> Option> { 62 | let this = this.set_ivars(DelegateIvars {}); 63 | unsafe { msg_send_id![super(this), init] } 64 | } 65 | } 66 | ); 67 | 68 | extern_methods!( 69 | unsafe impl Delegate { 70 | #[method_id(new)] 71 | pub fn new() -> Id; 72 | } 73 | ); 74 | 75 | fn main() { 76 | let (tx, rx) = channel(); 77 | SCShareableContent::get_shareable_content_with_completion_closure(move |shareable_content, error| { 78 | let ret = shareable_content.ok_or_else(|| error.unwrap()); 79 | tx.send(ret).unwrap(); 80 | }); 81 | let shareable_content = rx.recv().unwrap(); 82 | if let Err(error) = shareable_content { 83 | println!("error: {:?}", error); 84 | return; 85 | } 86 | let shareable_content = shareable_content.unwrap(); 87 | let displays = shareable_content.displays(); 88 | let display = match displays.first() { 89 | Some(display) => display, 90 | None => { 91 | println!("no display found"); 92 | return; 93 | } 94 | }; 95 | let filter = SCContentFilter::init_with_display_exclude_windows(SCContentFilter::alloc(), display, &NSArray::new()); 96 | let configuration: Id = SCStreamConfiguration::new(); 97 | configuration.set_width(display.width() as size_t); 98 | configuration.set_height(display.height() as size_t); 99 | let delegate = Delegate::new(); 100 | let stream_error = ProtocolObject::from_ref(&*delegate); 101 | let stream = SCStream::init_with_filter(SCStream::alloc(), &filter, &configuration, stream_error); 102 | let queue = Queue::new("com.screen_capture.queue", QueueAttribute::Serial); 103 | let output = ProtocolObject::from_ref(&*delegate); 104 | if let Err(ret) = stream.add_stream_output(output, SCStreamOutputType::Screen, &queue) { 105 | println!("error: {:?}", ret); 106 | return; 107 | } 108 | stream.start_capture(move |result| { 109 | if let Some(error) = result { 110 | println!("error: {:?}", error); 111 | } 112 | }); 113 | std::thread::sleep(std::time::Duration::from_secs(10)); 114 | stream.stop_capture(move |result| { 115 | if let Some(error) = result { 116 | println!("error: {:?}", error); 117 | } 118 | }); 119 | } 120 | -------------------------------------------------------------------------------- /screen-capture-kit/src/encode.rs: -------------------------------------------------------------------------------- 1 | use libc::c_void; 2 | use objc2::encode::{Encoding, RefEncode}; 3 | 4 | #[repr(C)] 5 | pub struct __CFString(c_void); 6 | 7 | pub type CFStringRef = *const __CFString; 8 | 9 | unsafe impl RefEncode for __CFString { 10 | const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("__CFString", &[])); 11 | } 12 | -------------------------------------------------------------------------------- /screen-capture-kit/src/error.rs: -------------------------------------------------------------------------------- 1 | use objc2_foundation::{NSInteger, NSString}; 2 | 3 | extern "C" { 4 | pub static SCStreamErrorDomain: &'static NSString; 5 | } 6 | 7 | #[repr(transparent)] 8 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 9 | pub struct SCStreamErrorCode(pub NSInteger); 10 | 11 | impl SCStreamErrorCode { 12 | #[doc(alias = "SCStreamErrorUserDeclined")] 13 | pub const UserDeclined: Self = Self(-3801); 14 | #[doc(alias = "SCStreamErrorFailedToStart")] 15 | pub const FailedToStart: Self = Self(-3802); 16 | #[doc(alias = "SCStreamErrorMissingEntitlements")] 17 | pub const MissingEntitlements: Self = Self(-3803); 18 | #[doc(alias = "SCStreamErrorFailedApplicationConnectionInvalid")] 19 | pub const FailedApplicationConnectionInvalid: Self = Self(-3804); 20 | #[doc(alias = "SCStreamErrorFailedApplicationConnectionInterrupted")] 21 | pub const FailedApplicationConnectionInterrupted: Self = Self(-3805); 22 | #[doc(alias = "SCStreamErrorFailedNoMatchingApplicationContext")] 23 | pub const FailedNoMatchingApplicationContext: Self = Self(-3806); 24 | #[doc(alias = "SCStreamErrorAttemptToStartStreamState")] 25 | pub const AttemptToStartStreamState: Self = Self(-3807); 26 | #[doc(alias = "SCStreamErrorAttemptToStopStreamState")] 27 | pub const AttemptToStopStreamState: Self = Self(-3808); 28 | #[doc(alias = "SCStreamErrorAttemptToUpdateFilterState")] 29 | pub const AttemptToUpdateFilterState: Self = Self(-3809); 30 | #[doc(alias = "SCStreamErrorAttemptToConfigState")] 31 | pub const AttemptToConfigState: Self = Self(-3810); 32 | #[doc(alias = "SCStreamErrorInternalError")] 33 | pub const InternalError: Self = Self(-3811); 34 | #[doc(alias = "SCStreamErrorInvalidParameter")] 35 | pub const InvalidParameter: Self = Self(-3812); 36 | #[doc(alias = "SCStreamErrorNoWindowList")] 37 | pub const NoWindowList: Self = Self(-3813); 38 | #[doc(alias = "SCStreamErrorNoDisplayList")] 39 | pub const NoDisplayList: Self = Self(-3814); 40 | #[doc(alias = "SCStreamErrorNoCaptureSource")] 41 | pub const NoCaptureSource: Self = Self(-3815); 42 | #[doc(alias = "SCStreamErrorRemovingStream")] 43 | pub const RemovingStream: Self = Self(-3816); 44 | #[doc(alias = "SCStreamErrorUserStopped")] 45 | pub const UserStopped: Self = Self(-3817); 46 | #[doc(alias = "SCStreamErrorFailedToStartAudioCapture")] 47 | pub const FailedToStartAudioCapture: Self = Self(-3818); 48 | #[doc(alias = "SCStreamErrorFailedToStopAudioCapture")] 49 | pub const FailedToStopAudioCapture: Self = Self(-3819); 50 | } 51 | -------------------------------------------------------------------------------- /screen-capture-kit/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, improper_ctypes)] 2 | 3 | extern crate block2; 4 | #[cfg(feature = "audio")] 5 | extern crate core_audio_types; 6 | extern crate core_foundation; 7 | extern crate core_graphics2 as core_graphics; 8 | extern crate core_media; 9 | #[cfg(feature = "video")] 10 | extern crate core_video; 11 | extern crate dispatch2; 12 | extern crate libc; 13 | #[macro_use] 14 | extern crate objc2; 15 | extern crate objc2_foundation; 16 | 17 | #[cfg(target_os = "macos")] 18 | #[link(name = "ScreenCaptureKit", kind = "framework")] 19 | extern "C" {} 20 | 21 | pub mod encode; 22 | pub mod error; 23 | pub mod shareable_content; 24 | pub mod stream; 25 | -------------------------------------------------------------------------------- /screen-capture-kit/src/shareable_content.rs: -------------------------------------------------------------------------------- 1 | use block2::RcBlock; 2 | use core_graphics::{display::CGDirectDisplayID, window::CGWindowID}; 3 | use libc::pid_t; 4 | use objc2::{extern_class, msg_send, msg_send_id, mutability::InteriorMutable, rc::Id, ClassType}; 5 | use objc2_foundation::{CGRect, NSArray, NSError, NSInteger, NSObject, NSObjectProtocol, NSString}; 6 | 7 | extern_class!( 8 | #[derive(Debug, PartialEq, Eq, Hash)] 9 | pub struct SCRunningApplication; 10 | 11 | unsafe impl ClassType for SCRunningApplication { 12 | type Super = NSObject; 13 | type Mutability = InteriorMutable; 14 | } 15 | ); 16 | 17 | unsafe impl NSObjectProtocol for SCRunningApplication {} 18 | 19 | impl SCRunningApplication { 20 | pub fn new() -> Id { 21 | unsafe { msg_send_id![SCRunningApplication::class(), new] } 22 | } 23 | 24 | pub fn bundle_identifier(&self) -> Id { 25 | unsafe { msg_send_id![self, bundleIdentifier] } 26 | } 27 | 28 | pub fn application_name(&self) -> Id { 29 | unsafe { msg_send_id![self, applicationName] } 30 | } 31 | 32 | pub fn process_id(&self) -> pid_t { 33 | unsafe { msg_send![self, processID] } 34 | } 35 | } 36 | 37 | extern_class!( 38 | #[derive(Debug, PartialEq, Eq, Hash)] 39 | pub struct SCWindow; 40 | 41 | unsafe impl ClassType for SCWindow { 42 | type Super = NSObject; 43 | type Mutability = InteriorMutable; 44 | } 45 | ); 46 | 47 | unsafe impl NSObjectProtocol for SCWindow {} 48 | 49 | impl SCWindow { 50 | pub fn new() -> Id { 51 | unsafe { msg_send_id![SCWindow::class(), new] } 52 | } 53 | 54 | pub fn window_id(&self) -> CGWindowID { 55 | unsafe { msg_send![self, windowID] } 56 | } 57 | 58 | pub fn frame(&self) -> CGRect { 59 | unsafe { msg_send![self, frame] } 60 | } 61 | 62 | pub fn title(&self) -> Option> { 63 | unsafe { msg_send_id![self, title] } 64 | } 65 | 66 | pub fn window_layer(&self) -> NSInteger { 67 | unsafe { msg_send![self, windowLayer] } 68 | } 69 | 70 | pub fn owning_application(&self) -> Option> { 71 | unsafe { msg_send_id![self, owningApplication] } 72 | } 73 | 74 | pub fn on_screen(&self) -> bool { 75 | unsafe { msg_send![self, isOnScreen] } 76 | } 77 | 78 | pub fn active(&self) -> bool { 79 | unsafe { msg_send![self, isActive] } 80 | } 81 | } 82 | 83 | extern_class!( 84 | #[derive(Debug, PartialEq, Eq, Hash)] 85 | pub struct SCDisplay; 86 | 87 | unsafe impl ClassType for SCDisplay { 88 | type Super = NSObject; 89 | type Mutability = InteriorMutable; 90 | } 91 | ); 92 | 93 | unsafe impl NSObjectProtocol for SCDisplay {} 94 | 95 | impl SCDisplay { 96 | pub fn new() -> Id { 97 | unsafe { msg_send_id![SCDisplay::class(), new] } 98 | } 99 | 100 | pub fn display_id(&self) -> CGDirectDisplayID { 101 | unsafe { msg_send![self, displayID] } 102 | } 103 | 104 | pub fn width(&self) -> NSInteger { 105 | unsafe { msg_send![self, width] } 106 | } 107 | 108 | pub fn height(&self) -> NSInteger { 109 | unsafe { msg_send![self, height] } 110 | } 111 | 112 | pub fn frame(&self) -> CGRect { 113 | unsafe { msg_send![self, frame] } 114 | } 115 | } 116 | 117 | extern_class!( 118 | #[derive(Debug, PartialEq, Eq, Hash)] 119 | pub struct SCShareableContent; 120 | 121 | unsafe impl ClassType for SCShareableContent { 122 | type Super = NSObject; 123 | type Mutability = InteriorMutable; 124 | } 125 | ); 126 | 127 | unsafe impl NSObjectProtocol for SCShareableContent {} 128 | 129 | type CompletionHandler = RcBlock; 130 | 131 | impl SCShareableContent { 132 | pub fn new() -> Id { 133 | unsafe { msg_send_id![SCShareableContent::class(), new] } 134 | } 135 | 136 | fn new_completion_handler(closure: F) -> CompletionHandler 137 | where 138 | F: Fn(Option>, Option>) + 'static, 139 | { 140 | RcBlock::new(move |sc: *mut Self, error: *mut NSError| { 141 | closure( 142 | if sc.is_null() { 143 | None 144 | } else { 145 | unsafe { Id::retain(sc) } 146 | }, 147 | if error.is_null() { 148 | None 149 | } else { 150 | unsafe { Id::retain(error) } 151 | }, 152 | ); 153 | }) 154 | } 155 | 156 | pub fn get_shareable_content_with_completion_closure(closure: F) 157 | where 158 | F: Fn(Option>, Option>) + 'static, 159 | { 160 | let handler = Self::new_completion_handler(closure); 161 | unsafe { msg_send![class!(SCShareableContent), getShareableContentWithCompletionHandler: &*handler] } 162 | } 163 | 164 | pub fn get_shareable_content_excluding_desktop_windows(exclude_desktop_windows: bool, on_screen_windows_only: bool, closure: F) 165 | where 166 | F: Fn(Option>, Option>) + 'static, 167 | { 168 | let handler = Self::new_completion_handler(closure); 169 | unsafe { 170 | msg_send![class!(SCShareableContent), getShareableContentExcludingDesktopWindows: exclude_desktop_windows onScreenWindowsOnly: on_screen_windows_only completionHandler: &*handler] 171 | } 172 | } 173 | 174 | pub fn get_shareable_content_excluding_desktop_windows_below_window(exclude_desktop_windows: bool, window: &SCWindow, closure: F) 175 | where 176 | F: Fn(Option>, Option>) + 'static, 177 | { 178 | let handler = Self::new_completion_handler(closure); 179 | unsafe { 180 | msg_send![class!(SCShareableContent), getShareableContentExcludingDesktopWindows: exclude_desktop_windows onScreenWindowsOnlyBelowWindow: window completionHandler: &*handler] 181 | } 182 | } 183 | 184 | pub fn get_shareable_content_excluding_desktop_windows_above_window(&self, exclude_desktop_windows: bool, window: &SCWindow, closure: F) 185 | where 186 | F: Fn(Option>, Option>) + 'static, 187 | { 188 | let handler = Self::new_completion_handler(closure); 189 | unsafe { 190 | msg_send![class!(SCShareableContent), getShareableContentExcludingDesktopWindows: exclude_desktop_windows onScreenWindowsOnlyAboveWindow: window completionHandler: &*handler] 191 | } 192 | } 193 | 194 | pub fn windows(&self) -> Id> { 195 | unsafe { msg_send_id![self, windows] } 196 | } 197 | 198 | pub fn displays(&self) -> Id> { 199 | unsafe { msg_send_id![self, displays] } 200 | } 201 | 202 | pub fn applications(&self) -> Id> { 203 | unsafe { msg_send_id![self, applications] } 204 | } 205 | } 206 | --------------------------------------------------------------------------------