From 6417fecdb766b151bd9d12fdcaa0e1eeb269030f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 27 May 2016 13:29:44 -0700 Subject: [PATCH] cocoa: Add a new platform-specific API that allows library consumers to specify that only the corners of a window can be transparent. By doing this, we significantly improve performance by allowing the window server to perform occlusion culling under most of the window. This patch relies on the private `CGSRegion` and the private `-[NSCGSWindow setOpaqueRegion:]` APIs. Requires servo/core-graphics-rs#50 and servo/cocoa-rs#129. --- src/api/cocoa/mod.rs | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/os/macos.rs | 13 +++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs index cb892a7fd7..02532a5299 100644 --- a/src/api/cocoa/mod.rs +++ b/src/api/cocoa/mod.rs @@ -35,6 +35,7 @@ use core_foundation::bundle::{CFBundle, CFBundleGetBundleWithIdentifier}; use core_foundation::bundle::{CFBundleGetFunctionPointerForName}; use core_graphics::display::{CGAssociateMouseAndMouseCursorPosition, CGMainDisplayID, CGDisplayPixelsHigh, CGWarpMouseCursorPosition}; +use core_graphics::private::CGSRegion; use std::ffi::CStr; use std::collections::VecDeque; @@ -78,6 +79,9 @@ struct DelegateState { /// Events that have been retreived with XLib but not dispatched with iterators yet pending_events: Mutex>, + + /// The transparent corner radius of the window, if there is one. + transparent_corner_radius: Option, } struct WindowDelegate { @@ -113,6 +117,9 @@ impl WindowDelegate { (handler)((scale_factor * rect.size.width as f32) as u32, (scale_factor * rect.size.height as f32) as u32); } + + update_opaque_region_of_window_if_necessary(*state.window, + state.transparent_corner_radius); } } @@ -193,6 +200,7 @@ impl Drop for WindowDelegate { pub struct PlatformSpecificWindowBuilderAttributes { pub activation_policy: ActivationPolicy, pub app_name: Option, + pub transparent_corner_radius: Option, } pub struct Window { @@ -310,7 +318,7 @@ impl Window { None => { return Err(OsError(format!("Couldn't create NSApplication"))); }, }; - let window = match Window::create_window(win_attribs) + let window = match Window::create_window(win_attribs, pl_attribs.transparent_corner_radius) { Some(window) => window, None => { return Err(OsError(format!("Couldn't create NSWindow"))); }, @@ -344,6 +352,9 @@ impl Window { } else { window.makeKeyWindow(); } + + update_opaque_region_of_window_if_necessary(*window, + pl_attribs.transparent_corner_radius); } let ds = DelegateState { @@ -352,6 +363,7 @@ impl Window { window: window.clone(), resize_handler: None, pending_events: Mutex::new(VecDeque::new()), + transparent_corner_radius: pl_attribs.transparent_corner_radius, }; let window = Window { @@ -402,7 +414,8 @@ impl Window { } } - fn create_window(attrs: &WindowAttributes) -> Option { + fn create_window(attrs: &WindowAttributes, transparent_corner_radius: Option) + -> Option { unsafe { let screen = match attrs.monitor { Some(ref monitor_id) => { @@ -1127,6 +1140,33 @@ extern fn draw_rect_in_glutin_content_view(this: &Object, _: Sel, _: NSRect) { } } +fn update_opaque_region_of_window_if_necessary(window: id, transparent_corner_radius: Option) { + let transparent_corner_radius = match transparent_corner_radius { + Some(transparent_corner_radius) => transparent_corner_radius, + None => return, + }; + + unsafe { + let window_frame = NSRect { + origin: NSPoint { + x: 0.0, + y: 0.0, + }, + size: NSWindow::frame(window).size, + }; + let inset_window_frame = window_frame.inset(0.0, transparent_corner_radius as CGFloat); + let region = CGSRegion::from_rect(inset_window_frame.as_CGRect()); + + let ns_cgs_window = match Class::get("NSCGSWindow") { + Some(window) => window, + None => return, + }; + let window_number = window.windowNumber(); + let cgs_window: id = msg_send![ns_cgs_window, windowWithWindowID:window_number]; + msg_send![cgs_window, setOpaqueShape:region]; + } +} + thread_local! { static WAKEUP_EVENT: id = { unsafe { diff --git a/src/os/macos.rs b/src/os/macos.rs index 877b5b7b9d..a3d8380787 100644 --- a/src/os/macos.rs +++ b/src/os/macos.rs @@ -54,6 +54,8 @@ impl From for NSApplicationActivationPolicy { pub trait WindowBuilderExt<'a> { fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder<'a>; fn with_app_name(mut self, app_name: String) -> WindowBuilder<'a>; + fn with_transparent_corner_radius(mut self, transparent_corner_radius: u32) + -> WindowBuilder<'a>; } impl<'a> WindowBuilderExt<'a> for WindowBuilder<'a> { @@ -70,4 +72,15 @@ impl<'a> WindowBuilderExt<'a> for WindowBuilder<'a> { self.platform_specific.app_name = Some(app_name); self } + + /// Sets the maximum number of pixels from the corners that transparent content will be drawn + /// + /// This is used as an optimization so that Cocoa won't have to paint what's behind most of the + /// window. It improves performance dramatically when in use when videos, transparent Terminal + /// windows, etc. are behind the window. + fn with_transparent_corner_radius(mut self, transparent_corner_radius: u32) + -> WindowBuilder<'a> { + self.platform_specific.transparent_corner_radius = Some(transparent_corner_radius); + self + } }