Skip to content
Merged
31 changes: 25 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ authors = [
"Robbert van der Helm <mail@robbertvanderhelm.nl>",
"Adrien Prokopowicz <prokopylmc@gmail.com>"
]
edition = "2018"
edition = "2021"
Comment thread
micahrj marked this conversation as resolved.
license = "MIT OR Apache-2.0"
description = "Low-level windowing system geared towards making audio plugin UIs."
keywords = ["windowing", "audio", "plugin"]
Expand All @@ -23,7 +23,13 @@ exclude = [".github"]

[features]
default = []
opengl = ["uuid"]
opengl = [
"uuid",
"objc2-core-foundation/CFBundle",
"objc2-app-kit/NSOpenGL",
"objc2-app-kit/NSOpenGLView",
"objc2-app-kit/objc2-open-gl"
]

[dependencies]
keyboard-types = { version = "0.6.1", default-features = false }
Expand All @@ -39,10 +45,23 @@ winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "
uuid = { version = "0.8", features = ["v4"], optional = true }

[target.'cfg(target_os="macos")'.dependencies]
cocoa = "0.24.0"
core-foundation = "0.9.1"
objc = "0.2.7"
uuid = { version = "0.8", features = ["v4"] }
uuid = { version = "1.23.1", features = ["v4"] }
objc2 = "0.6.4"
objc2-core-foundation = { version = "0.3.2", default-features = false, features = ["std", "CFString"] }
objc2-foundation = { version = "0.3.2", default-features = false, features = ["std", "NSEnumerator"] }
objc2-app-kit = { version = "0.3.2", default-features = false, features = [
"NSApplication",
"NSDragging",
"NSEvent",
"NSGraphics",
"NSPasteboard",
"NSResponder",
"NSRunningApplication",
"NSTrackingArea",
"NSView",
"NSWindow",
"objc2-core-foundation"
] }

[dev-dependencies]
rtrb = "0.2"
Expand Down
111 changes: 44 additions & 67 deletions src/gl/macos.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
use std::ffi::c_void;
use std::str::FromStr;

use raw_window_handle::RawWindowHandle;
#![allow(deprecated)] // OpenGL is deprecated on macOS

use cocoa::appkit::{
use super::{GlConfig, GlError, Profile};
use objc2::rc::Retained;
use objc2::AllocAnyThread;
use objc2::{MainThreadMarker, MainThreadOnly};
use objc2_app_kit::{
NSOpenGLContext, NSOpenGLContextParameter, NSOpenGLPFAAccelerated, NSOpenGLPFAAlphaSize,
NSOpenGLPFAColorSize, NSOpenGLPFADepthSize, NSOpenGLPFADoubleBuffer, NSOpenGLPFAMultisample,
NSOpenGLPFAOpenGLProfile, NSOpenGLPFASampleBuffers, NSOpenGLPFASamples, NSOpenGLPFAStencilSize,
NSOpenGLPixelFormat, NSOpenGLProfileVersion3_2Core, NSOpenGLProfileVersion4_1Core,
NSOpenGLProfileVersionLegacy, NSOpenGLView, NSView,
};
use cocoa::base::{id, nil, YES};
use cocoa::foundation::NSSize;

use core_foundation::base::TCFType;
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
use core_foundation::string::CFString;

use objc::{msg_send, sel, sel_impl};

use super::{GlConfig, GlError, Profile};
use objc2_core_foundation::{CFBundle, CFString};
use objc2_foundation::NSSize;
use raw_window_handle::RawWindowHandle;
use std::ffi::c_void;
use std::ptr::NonNull;

pub type CreationFailedError = ();
pub struct GlContext {
view: id,
context: id,
view: Retained<NSOpenGLView>,
context: Retained<NSOpenGLContext>,
}

impl GlContext {
Expand All @@ -35,11 +31,10 @@ impl GlContext {
return Err(GlError::InvalidWindowHandle);
};

if handle.ns_view.is_null() {
let parent_view = handle.ns_view.cast::<NSView>();
let Some(parent_view) = parent_view.as_ref() else {
return Err(GlError::InvalidWindowHandle);
}

let parent_view = handle.ns_view as id;
};

let version = if config.version < (3, 2) && config.profile == Profile::Compatibility {
NSOpenGLProfileVersionLegacy
Expand Down Expand Up @@ -76,33 +71,29 @@ impl GlContext {

attrs.push(0);

let pixel_format = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attrs);

if pixel_format == nil {
return Err(GlError::CreationFailed(()));
}

let view =
NSOpenGLView::alloc(nil).initWithFrame_pixelFormat_(parent_view.frame(), pixel_format);
let pixel_format = NSOpenGLPixelFormat::initWithAttributes(
NSOpenGLPixelFormat::alloc(),
NonNull::new(attrs.as_mut_ptr()).unwrap(),
)
.ok_or(GlError::CreationFailed(()))?;

if view == nil {
return Err(GlError::CreationFailed(()));
}
let view = NSOpenGLView::initWithFrame_pixelFormat(
NSOpenGLView::alloc(MainThreadMarker::new().unwrap()),
parent_view.frame(),
Some(&pixel_format),
)
.ok_or(GlError::CreationFailed(()))?;

view.setWantsBestResolutionOpenGLSurface_(YES);
view.setWantsBestResolutionOpenGLSurface(true);

NSOpenGLView::display_(view);
parent_view.addSubview_(view);
view.display();
parent_view.addSubview(&view);

let context: id = msg_send![view, openGLContext];
let () = msg_send![context, retain];
let context = view.openGLContext().ok_or(GlError::CreationFailed(()))?;

context.setValues_forParameter_(
&(config.vsync as i32),
NSOpenGLContextParameter::NSOpenGLCPSwapInterval,
);
let value = config.vsync as i32;

let () = msg_send![pixel_format, release];
context.setValues_forParameter((&value).into(), NSOpenGLContextParameter::SwapInterval);

Ok(GlContext { view, context })
}
Expand All @@ -112,47 +103,33 @@ impl GlContext {
}

pub unsafe fn make_not_current(&self) {
NSOpenGLContext::clearCurrentContext(self.context);
NSOpenGLContext::clearCurrentContext();
}

pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
let symbol_name = CFString::from_str(symbol).unwrap();
let framework_name = CFString::from_str("com.apple.opengl").unwrap();
let framework =
unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) };
let symbol_name = CFString::from_str(symbol);
let framework_name = CFString::from_static_str("com.apple.opengl");
let framework = CFBundle::bundle_with_identifier(Some(&framework_name)).unwrap();

unsafe { CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) }
CFBundle::function_pointer_for_name(&framework, Some(&symbol_name))
}

pub fn swap_buffers(&self) {
unsafe {
self.context.flushBuffer();
let () = msg_send![self.view, setNeedsDisplay: YES];
}
self.context.flushBuffer();
self.view.setNeedsDisplay(true);
}

/// On macOS the `NSOpenGLView` needs to be resized separtely from our main view.
pub(crate) fn resize(&self, size: NSSize) {
unsafe { NSView::setFrameSize(self.view, size) };
unsafe {
let _: () = msg_send![self.view, setNeedsDisplay: YES];
}
self.view.setFrameSize(size);
self.view.setNeedsDisplay(true);
}

/// Pointer to the `NSOpenGLView` this context renders into. Used by
/// the parent `NSView`'s `hitTest:` override to collapse hits on the
/// render subview to the parent, so AppKit routes `mouseDown:` on
/// first click in non-key windows.
pub(crate) fn ns_view(&self) -> id {
self.view
}
}

impl Drop for GlContext {
fn drop(&mut self) {
unsafe {
let () = msg_send![self.context, release];
let () = msg_send![self.view, release];
}
pub(crate) fn ns_view(&self) -> &NSOpenGLView {
&self.view
}
}
4 changes: 2 additions & 2 deletions src/gl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl GlContext {

/// On macOS the `NSOpenGLView` needs to be resized separtely from our main view.
#[cfg(target_os = "macos")]
pub(crate) fn resize(&self, size: cocoa::foundation::NSSize) {
pub(crate) fn resize(&self, size: objc2_foundation::NSSize) {
self.context.resize(size);
}

Expand All @@ -118,7 +118,7 @@ impl GlContext {
/// render subview to the parent, so AppKit routes `mouseDown:` on
/// first click in non-key windows.
#[cfg(target_os = "macos")]
pub(crate) fn ns_view(&self) -> cocoa::base::id {
pub(crate) fn ns_view(&self) -> &objc2_app_kit::NSView {
self.context.ns_view()
}
}
4 changes: 0 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// This is required because the objc crate is causing a lot of warnings: https://github.com/SSheldon/rust-objc/issues/125
// Eventually we should migrate to the objc2 crate and remove this.
#![allow(unexpected_cfgs)]

#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "windows")]
Expand Down
Loading
Loading