From 90ddbb60b241bd6043f60685115591c8f5307970 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 1 Nov 2018 19:39:21 -0700 Subject: [PATCH 1/7] Allow a `CFArray` to be constructed from `Copy` data. This is necessary for the bindings to Quartz Window Services to work, since those APIs don't wrap `CGWindowID` objects in `CFNumber` wrappers. --- core-foundation/src/array.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core-foundation/src/array.rs b/core-foundation/src/array.rs index 516b1da..14b5088 100644 --- a/core-foundation/src/array.rs +++ b/core-foundation/src/array.rs @@ -15,6 +15,7 @@ use core_foundation_sys::base::{CFTypeRef, CFRelease, kCFAllocatorDefault}; use std::mem; use std::marker::PhantomData; use std::os::raw::c_void; +use std::ptr; use ConcreteCFType; use base::{CFIndexConvertible, TCFType, CFRange}; @@ -61,6 +62,17 @@ impl_CFTypeDescription!(CFArray); unsafe impl ConcreteCFType for CFArray<*const c_void> {} impl CFArray { + /// Creates a new `CFArray` with the given elements, which must implement `Copy`. + pub fn from_copyable(elems: &[T]) -> CFArray where T: Copy { + unsafe { + let array_ref = CFArrayCreate(kCFAllocatorDefault, + mem::transmute(elems.as_ptr()), + elems.len().to_CFIndex(), + ptr::null()); + TCFType::wrap_under_create_rule(array_ref) + } + } + /// Creates a new `CFArray` with the given elements, which must be `CFType` objects. pub fn from_CFTypes(elems: &[T]) -> CFArray where T: TCFType { unsafe { From 1cdd18a0e24f027972a92b80fd4c7dea6b88add2 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 1 Nov 2018 19:46:25 -0700 Subject: [PATCH 2/7] Add a binding to `CACurrentMediaTime()` --- cocoa/src/quartzcore.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cocoa/src/quartzcore.rs b/cocoa/src/quartzcore.rs index eb882a5..83a15b6 100644 --- a/cocoa/src/quartzcore.rs +++ b/cocoa/src/quartzcore.rs @@ -24,6 +24,14 @@ use std::ptr; use base::{BOOL, id, nil, YES}; use foundation::NSUInteger; +// CABase.h + +pub fn current_media_time() -> CFTimeInterval { + unsafe { + CACurrentMediaTime() + } +} + // CALayer.h pub struct CALayer(id); @@ -1581,6 +1589,8 @@ impl CATransform3D { #[link(name = "QuartzCore", kind = "framework")] extern { + fn CACurrentMediaTime() -> CFTimeInterval; + fn CATransform3DIsIdentity(t: CATransform3D) -> bool; fn CATransform3DEqualToTransform(a: CATransform3D, b: CATransform3D) -> bool; fn CATransform3DMakeTranslation(tx: CGFloat, ty: CGFloat, tz: CGFloat) -> CATransform3D; From ee0269808db239574f4e8298637e07643623a517 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 1 Nov 2018 19:47:36 -0700 Subject: [PATCH 3/7] Add a few bindings to Core Video to go along with the `CARenderer` bindings --- cocoa/src/quartzcore.rs | 64 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/cocoa/src/quartzcore.rs b/cocoa/src/quartzcore.rs index 83a15b6..2c1b29e 100644 --- a/cocoa/src/quartzcore.rs +++ b/cocoa/src/quartzcore.rs @@ -1609,3 +1609,67 @@ extern { fn CATransform3DIsAffine(t: CATransform3D) -> bool; fn CATransform3DGetAffineTransform(t: CATransform3D) -> CGAffineTransform; } + +// Miscellaneous structures in other frameworks. +// +// These should be moved elsewhere eventually. + +// CoreVideo/CVBase.h + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CVTimeStamp { + pub version: u32, + pub videoTimeScale: i32, + pub videoTime: i64, + pub hostTime: u64, + pub rateScalar: f64, + pub videoRefreshPeriod: i64, + pub smpteTime: CVSMPTETime, + pub flags: u64, + pub reserved: u64, +} + +pub type CVTimeStampFlags = u64; + +pub const kCVTimeStampVideoTimeValid: CVTimeStampFlags = 1 << 0; +pub const kCVTimeStampHostTimeValid: CVTimeStampFlags = 1 << 1; +pub const kCVTimeStampSMPTETimeValid: CVTimeStampFlags = 1 << 2; +pub const kCVTimeStampVideoRefreshPeriodValid: CVTimeStampFlags = 1 << 3; +pub const kCVTimeStampRateScalarValid: CVTimeStampFlags = 1 << 4; +pub const kCVTimeStampTopField: CVTimeStampFlags = 1 << 16; +pub const kCVTimeStampBottomField: CVTimeStampFlags = 1 << 17; +pub const kCVTimeStampVideoHostTimeValid: CVTimeStampFlags = + kCVTimeStampVideoTimeValid | kCVTimeStampHostTimeValid; +pub const kCVTimeStampIsInterlaced: CVTimeStampFlags = + kCVTimeStampTopField | kCVTimeStampBottomField; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CVSMPTETime { + pub subframes: i16, + pub subframeDivisor: i16, + pub counter: u32, + pub time_type: u32, + pub flags: u32, + pub hours: i16, + pub minutes: i16, + pub seconds: i16, + pub frames: i16, +} + +pub type CVSMPTETimeType = u32; + +pub const kCVSMPTETimeType24: CVSMPTETimeType = 0; +pub const kCVSMPTETimeType25: CVSMPTETimeType = 1; +pub const kCVSMPTETimeType30Drop: CVSMPTETimeType = 2; +pub const kCVSMPTETimeType30: CVSMPTETimeType = 3; +pub const kCVSMPTETimeType2997: CVSMPTETimeType = 4; +pub const kCVSMPTETimeType2997Drop: CVSMPTETimeType = 5; +pub const kCVSMPTETimeType60: CVSMPTETimeType = 6; +pub const kCVSMPTETimeType5994: CVSMPTETimeType = 7; + +pub type CVSMPTETimeFlags = u32; + +pub const kCVSMPTETimeValid: CVSMPTETimeFlags = 1 << 0; +pub const kCVSMPTETimeRunning: CVSMPTETimeFlags = 1 << 1; From b56fda0a1301f6aeceef2f538389121b676ed45c Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 1 Nov 2018 19:50:02 -0700 Subject: [PATCH 4/7] Add bindings to `CARenderer` --- cocoa/src/quartzcore.rs | 166 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 1 deletion(-) diff --git a/cocoa/src/quartzcore.rs b/cocoa/src/quartzcore.rs index 2c1b29e..cc7ab21 100644 --- a/cocoa/src/quartzcore.rs +++ b/cocoa/src/quartzcore.rs @@ -7,13 +7,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![allow(non_upper_case_globals)] + use core_foundation::array::{CFArray, CFArrayRef}; -use core_foundation::base::{CFTypeRef, TCFType}; +use core_foundation::base::{CFType, CFTypeRef, TCFType}; use core_foundation::date::CFTimeInterval; use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; use core_foundation::string::{CFString, CFStringRef}; use core_graphics::base::CGFloat; use core_graphics::color::{CGColor, SysCGColorRef}; +use core_graphics::color_space::CGColorSpace; use core_graphics::context::CGContext; use core_graphics::geometry::{CGAffineTransform, CGPoint, CGRect, CGSize}; use core_graphics::path::{CGPath, SysCGPathRef}; @@ -21,6 +24,7 @@ use foreign_types::ForeignType; use std::ops::Mul; use std::ptr; +use appkit::CGLContextObj; use base::{BOOL, id, nil, YES}; use foundation::NSUInteger; @@ -1364,6 +1368,163 @@ bitflags! { } } +// CARenderer.h + +pub struct CARenderer(id); + +unsafe impl Send for CARenderer {} +unsafe impl Sync for CARenderer {} + +impl Clone for CARenderer { + #[inline] + fn clone(&self) -> CARenderer { + unsafe { + CARenderer(msg_send![self.id(), retain]) + } + } +} + +impl Drop for CARenderer { + #[inline] + fn drop(&mut self) { + unsafe { + msg_send![self.id(), release] + } + } +} + +impl CARenderer { + #[inline] + pub fn id(&self) -> id { + self.0 + } + + #[inline] + pub unsafe fn from_cgl_context(context: CGLContextObj, color_space: Option) + -> CARenderer { + let mut pairs: Vec<(CFString, CFType)> = vec![]; + if let Some(color_space) = color_space { + pairs.push((CFString::wrap_under_get_rule(kCARendererColorSpace), + CFType::wrap_under_get_rule(color_space.as_ptr() as *const _ as *const _))) + } + + let options: CFDictionary = CFDictionary::from_CFType_pairs(&pairs); + + let renderer: id = + msg_send![class!(CARenderer), rendererWithCGLContext:context + options:options.as_CFTypeRef()]; + debug_assert!(renderer != nil); + CARenderer(renderer) + } + + #[inline] + pub unsafe fn from_metal_texture(metal_texture: id, + metal_command_queue: id, + color_space: Option) + -> CARenderer { + let mut pairs: Vec<(CFString, CFType)> = vec![ + (CFString::wrap_under_get_rule(kCARendererMetalCommandQueue), + CFType::wrap_under_get_rule(metal_command_queue as *const _ as *const _)), + ]; + if let Some(color_space) = color_space { + pairs.push((CFString::wrap_under_get_rule(kCARendererColorSpace), + CFType::wrap_under_get_rule(color_space.as_ptr() as *const _ as *const _))) + } + + let options: CFDictionary = CFDictionary::from_CFType_pairs(&pairs); + + let renderer: id = + msg_send![class!(CARenderer), rendererWithMTLTexture:metal_texture + options:options.as_CFTypeRef()]; + debug_assert!(renderer != nil); + CARenderer(renderer) + } + + #[inline] + pub fn layer(&self) -> Option { + unsafe { + let layer: id = msg_send![self.id(), layer]; + if layer.is_null() { + None + } else { + Some(CALayer(layer)) + } + } + } + + #[inline] + pub fn set_layer(&self, layer: Option) { + unsafe { + let layer = match layer { + Some(ref layer) => layer.id(), + None => nil, + }; + msg_send![self.id(), setLayer:layer]; + } + } + + #[inline] + pub fn bounds(&self) -> CGRect { + unsafe { + msg_send![self.id(), bounds] + } + } + + #[inline] + pub fn set_bounds(&self, bounds: CGRect) { + unsafe { + msg_send![self.id(), setBounds:bounds] + } + } + + #[inline] + pub fn begin_frame_at(&self, time: CFTimeInterval, timestamp: Option<&CVTimeStamp>) { + unsafe { + msg_send![self.id(), beginFrameAtTime:time timeStamp:timestamp] + } + } + + #[inline] + pub fn update_bounds(&self) -> CGRect { + unsafe { + msg_send![self.id(), updateBounds] + } + } + + #[inline] + pub fn add_update_rect(&self, rect: CGRect) { + unsafe { + msg_send![self.id(), addUpdateRect:rect] + } + } + + #[inline] + pub fn render(&self) { + unsafe { + msg_send![self.id(), render] + } + } + + #[inline] + pub fn next_frame_time(&self) -> CFTimeInterval { + unsafe { + msg_send![self.id(), nextFrameTime] + } + } + + #[inline] + pub fn end_frame(&self) { + unsafe { + msg_send![self.id(), endFrame] + } + } + + #[inline] + pub unsafe fn set_destination(&self, metal_texture: id) { + msg_send![self.id(), setDestination:metal_texture] + } +} + // CATransaction.h // You can't actually construct any `CATransaction` objects, so that class is @@ -1589,6 +1750,9 @@ impl CATransform3D { #[link(name = "QuartzCore", kind = "framework")] extern { + static kCARendererColorSpace: CFStringRef; + static kCARendererMetalCommandQueue: CFStringRef; + fn CACurrentMediaTime() -> CFTimeInterval; fn CATransform3DIsIdentity(t: CATransform3D) -> bool; From 692ba80aec3494a21c9fe3639167b5f17f646a88 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 1 Nov 2018 19:51:32 -0700 Subject: [PATCH 5/7] Add bindings to `-[CATransaction completionBlock]` and `-[CATransaction setCompletionBlock:]` We have support for blocks, so there's no reason to not include these. --- cocoa/src/quartzcore.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cocoa/src/quartzcore.rs b/cocoa/src/quartzcore.rs index cc7ab21..e139147 100644 --- a/cocoa/src/quartzcore.rs +++ b/cocoa/src/quartzcore.rs @@ -1530,6 +1530,7 @@ impl CARenderer { // You can't actually construct any `CATransaction` objects, so that class is // really just a module. pub mod transaction { + use block::{Block, ConcreteBlock, IntoConcreteBlock, RcBlock}; use core_foundation::date::CFTimeInterval; use core_foundation::string::CFString; @@ -1611,8 +1612,27 @@ pub mod transaction { } } - // Omitted: `completionBlock`; depends on blocks. - // Omitted: `setCompletionBlock:`; depends on blocks. + #[inline] + pub fn completion_block() -> Option> { + unsafe { + let completion_block: *mut Block<(), ()> = + msg_send![class!(CATransaction), completionBlock]; + if completion_block.is_null() { + None + } else { + Some(RcBlock::new(completion_block)) + } + } + } + + #[inline] + pub fn set_completion_block(block: ConcreteBlock<(), (), F>) + where F: 'static + IntoConcreteBlock<(), Ret = ()> { + unsafe { + let block = block.copy(); + msg_send![class!(CATransaction), setCompletionBlock:&*block] + } + } #[inline] pub fn value_for_key(key: &str) -> id { From ee0ada3d28226b7b5002102c6fc2bc6dd637f974 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 1 Nov 2018 19:53:02 -0700 Subject: [PATCH 6/7] Add bindings to Quartz Window Services. This series of functions allows us to take screenshots, which are useful for integration testing. --- core-graphics/src/lib.rs | 2 + core-graphics/src/window.rs | 149 ++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 core-graphics/src/window.rs diff --git a/core-graphics/src/lib.rs b/core-graphics/src/lib.rs index 71f94b8..981883c 100644 --- a/core-graphics/src/lib.rs +++ b/core-graphics/src/lib.rs @@ -33,6 +33,8 @@ pub mod event_source; pub mod font; pub mod geometry; #[cfg(target_os = "macos")] +pub mod window; +#[cfg(target_os = "macos")] pub mod private; pub mod image; pub mod path; diff --git a/core-graphics/src/window.rs b/core-graphics/src/window.rs new file mode 100644 index 0000000..aa7293f --- /dev/null +++ b/core-graphics/src/window.rs @@ -0,0 +1,149 @@ +// Copyright 2018 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::{CFType, TCFType}; +use core_foundation::dictionary::CFDictionary; +use core_foundation::string::{CFString, CFStringRef}; +use foreign_types::ForeignType; + +use geometry::CGRect; +use image::CGImage; +use sys; + +pub type CGWindowID = u32; + +pub type CGWindowSharingType = u32; +pub const kCGWindowSharingNone: CGWindowSharingType = 0; +pub const kCGWindowSharingReadOnly: CGWindowSharingType = 1; +pub const kCGWindowSharingReadWrite: CGWindowSharingType = 1; + +pub type CGWindowBackingType = u32; +pub const kCGWindowBackingStoreRetained: CGWindowBackingType = 0; +pub const kCGWindowBackingStoreNonretained: CGWindowBackingType = 1; +pub const kCGWindowBackingStoreBuffered: CGWindowBackingType = 2; + +pub type CGWindowListOption = u32; +pub const kCGWindowListOptionAll: CGWindowListOption = 1 << 0; +pub const kCGWindowListOptionOnScreenOnly: CGWindowListOption = 1 << 1; +pub const kCGWindowListOptionOnScreenAboveWindow: CGWindowListOption = 1 << 2; +pub const kCGWindowListOptionOnScreenBelowWindow: CGWindowListOption = 1 << 3; +pub const kCGWindowListOptionIncludingWindow: CGWindowListOption = 1 << 4; +pub const kCGWindowListOptionExcludeDesktopElements: CGWindowListOption = 1 << 5; + +pub type CGWindowImageOption = u32; +pub const kCGWindowImageDefault: CGWindowImageOption = 0; +pub const kCGWindowImageBoundsIgnoreFraming: CGWindowImageOption = 1 << 0; +pub const kCGWindowImageShouldBeOpaque: CGWindowImageOption = 1 << 1; +pub const kCGWindowImageOnlyShadows: CGWindowImageOption = 1 << 2; +pub const kCGWindowImageBestResolution: CGWindowImageOption = 1 << 3; +pub const kCGWindowImageNominalResolution: CGWindowImageOption = 1 << 4; + +pub const kCGNullWindowID: CGWindowID = 0; + +pub fn copy_window_info(option: CGWindowListOption, relative_to_window: CGWindowID) + -> Option { + unsafe { + let array = CGWindowListCopyWindowInfo(option, relative_to_window); + if array.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(array)) + } + } +} + +pub fn create_window_list(option: CGWindowListOption, relative_to_window: CGWindowID) + -> Option> { + unsafe { + let array = CGWindowListCreate(option, relative_to_window); + if array.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(array)) + } + } +} + +pub fn create_description_from_array(window_array: CFArray) -> + Option>> { + unsafe { + let array = CGWindowListCreateDescriptionFromArray(window_array.as_concrete_TypeRef()); + if array.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(array)) + } + } +} + +pub fn create_image(screen_bounds: CGRect, + list_option: CGWindowListOption, + window_id: CGWindowID, + image_option: CGWindowImageOption) + -> Option { + unsafe { + let image = CGWindowListCreateImage(screen_bounds, list_option, window_id, image_option); + if image.is_null() { + None + } else { + Some(CGImage::from_ptr(image)) + } + } +} + +pub fn create_image_from_array(screen_bounds: CGRect, + window_array: CFArray, + image_option: CGWindowImageOption) + -> Option { + unsafe { + let image = CGWindowListCreateImageFromArray(screen_bounds, + window_array.as_concrete_TypeRef(), + image_option); + if image.is_null() { + None + } else { + Some(CGImage::from_ptr(image)) + } + } +} + +#[link(name = "CoreGraphics", kind = "framework")] +extern { + pub static kCGWindowNumber: CFStringRef; + pub static kCGWindowStoreType: CFStringRef; + pub static kCGWindowLayer: CFStringRef; + pub static kCGWindowBounds: CFStringRef; + pub static kCGWindowSharingState: CFStringRef; + pub static kCGWindowAlpha: CFStringRef; + pub static kCGWindowOwnerPID: CFStringRef; + pub static kCGWindowMemoryUsage: CFStringRef; + pub static kCGWindowWorkspace: CFStringRef; + pub static kCGWindowOwnerName: CFStringRef; + pub static kCGWindowName: CFStringRef; + pub static kCGWindowIsOnscreen: CFStringRef; + pub static kCGWindowBackingLocationVideoMemory: CFStringRef; + + pub fn CGWindowListCopyWindowInfo(option: CGWindowListOption, relativeToWindow: CGWindowID) + -> CFArrayRef; + pub fn CGWindowListCreate(option: CGWindowListOption, relativeToWindow: CGWindowID) + -> CFArrayRef; + pub fn CGWindowListCreateDescriptionFromArray(windowArray: CFArrayRef) -> CFArrayRef; + pub fn CGWindowListCreateImage(screenBounds: CGRect, + listOption: CGWindowListOption, + windowID: CGWindowID, + imageOption: CGWindowImageOption) + -> *mut sys::CGImage; + pub fn CGWindowListCreateImageFromArray(screenBounds: CGRect, + windowArray: CFArrayRef, + imageOption: CGWindowImageOption) + -> *mut sys::CGImage; +} From fd4736bb9f9d41715d3d4496c18b7c675ad12fa2 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 1 Nov 2018 19:54:02 -0700 Subject: [PATCH 7/7] Update version number to 0.17.3 --- core-graphics/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-graphics/Cargo.toml b/core-graphics/Cargo.toml index b8ce43c..0137d48 100644 --- a/core-graphics/Cargo.toml +++ b/core-graphics/Cargo.toml @@ -3,7 +3,7 @@ name = "core-graphics" description = "Bindings to Core Graphics for OS X" homepage = "https://github.com/servo/core-graphics-rs" repository = "https://github.com/servo/core-foundation-rs" -version = "0.17.2" +version = "0.17.3" authors = ["The Servo Project Developers"] license = "MIT / Apache-2.0"