Skip to content
This repository was archived by the owner on Jul 10, 2023. It is now read-only.

Commit

Permalink
Auto merge of #94 - pcwalton:mac-transparent-border-radius-servo, r=m…
Browse files Browse the repository at this point in the history
…etajack

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.

r? @metajack

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/glutin/94)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Jun 2, 2016
2 parents ac40d01 + e78b723 commit c53ecc6
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ objc = "0.2"
cgl = "0.1"
cocoa = "0.4.1"
core-foundation = "0.2.2"
core-graphics = ">=0.2, <0.4"
core-graphics = "0.3.1"

[target.i686-pc-windows-gnu.dependencies]
winapi = "0.2"
Expand Down
54 changes: 52 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,43 @@ extern fn draw_rect_in_glutin_content_view(this: &Object, _: Sel, _: NSRect) {
}
}

/// Determines what part of the window is guaranteed to be opaque (via `transparent_corner_radius`)
/// and supplies the Mac OS X window server with that information.
///
/// This allows the window server to perform occlusion culling. Normal (non-borderless) Cocoa
/// windows use this API internally.
///
/// NB: This uses two private Cocoa APIs: `-[NSCGSWindow windowWithWindowID]` and
/// `[_NSCGSWindow setOpaqueShape]`. It also uses the private `CGSRegion` API. This code attempts
/// to be defensive, but if Apple changes these APIs in future versions of Mac OS X, this function
/// may need to be updated.
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 c53ecc6

Please sign in to comment.