Skip to content

Commit

Permalink
cocoa: Add a new platform-specific API that allows library consumers to
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
pcwalton committed May 27, 2016
1 parent d559be7 commit 6417fec
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 2 deletions.
44 changes: 42 additions & 2 deletions src/api/cocoa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -78,6 +79,9 @@ struct DelegateState {

/// Events that have been retreived with XLib but not dispatched with iterators yet
pending_events: Mutex<VecDeque<Event>>,

/// The transparent corner radius of the window, if there is one.
transparent_corner_radius: Option<u32>,
}

struct WindowDelegate {
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -193,6 +200,7 @@ impl Drop for WindowDelegate {
pub struct PlatformSpecificWindowBuilderAttributes {
pub activation_policy: ActivationPolicy,
pub app_name: Option<String>,
pub transparent_corner_radius: Option<u32>,
}

pub struct Window {
Expand Down Expand Up @@ -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"))); },
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -402,7 +414,8 @@ impl Window {
}
}

fn create_window(attrs: &WindowAttributes) -> Option<IdRef> {
fn create_window(attrs: &WindowAttributes, transparent_corner_radius: Option<u32>)
-> Option<IdRef> {
unsafe {
let screen = match attrs.monitor {
Some(ref monitor_id) => {
Expand Down Expand Up @@ -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<u32>) {
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 {
Expand Down
13 changes: 13 additions & 0 deletions src/os/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ impl From<ActivationPolicy> 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> {
Expand All @@ -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
}
}

0 comments on commit 6417fec

Please sign in to comment.