Skip to content

Commit

Permalink
Use objc2 for macOS font smoothing determination
Browse files Browse the repository at this point in the history
`objc2` / `objc2-foundation` is much more type-safe than `cocoa`, and
automatically ensures that memory management-rules are upheld.

See also alacritty/copypasta#74.
  • Loading branch information
madsmtm authored May 22, 2024
1 parent 169fb22 commit 895ccc1
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 21 deletions.
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ freetype-rs = "0.36.0"
pkg-config = "0.3"

[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.25.0"
core-foundation = "0.9.3"
core-text = "20.1.0"
core-graphics = "0.23.1"
core-foundation-sys = "0.8.4"
objc = "0.2.7"
once_cell = "1.12"
objc2 = "0.5.1"
objc2-foundation = { version = "0.2.2", features = [
"NSString",
"NSUserDefaults",
"NSValue",
] }

[target.'cfg(windows)'.dependencies]
dwrote = { version = "0.11" }
Expand Down
44 changes: 26 additions & 18 deletions src/darwin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
//! Font rendering based on CoreText.
use std::collections::HashMap;
use std::ffi::c_char;
use std::ffi::CStr;
use std::iter;
use std::path::PathBuf;
use std::ptr;

use cocoa::base::{id, nil};
use cocoa::foundation::{NSInteger, NSString, NSUserDefaults};

use core_foundation::array::{CFArray, CFIndex};
use core_foundation::base::{CFType, ItemRef, TCFType};
use core_foundation::number::{CFNumber, CFNumberRef};
Expand All @@ -28,10 +24,10 @@ use core_text::font_descriptor::{
self, kCTFontColorGlyphsTrait, kCTFontDefaultOrientation, kCTFontEnabledAttribute,
CTFontDescriptor, SymbolicTraitAccessors,
};
use objc2::rc::{autoreleasepool, Retained};
use objc2_foundation::{ns_string, NSNumber, NSObject, NSObjectProtocol, NSString, NSUserDefaults};

use log::{trace, warn};
use objc::rc::autoreleasepool;
use objc::{class, msg_send, sel, sel_impl};
use once_cell::sync::Lazy;

pub mod byte_order;
Expand Down Expand Up @@ -274,30 +270,42 @@ fn descriptors_for_family(family: &str) -> Vec<Descriptor> {
// other integer, or a missing value (the default), or a value of any other type, as leaving it
// enabled.
static FONT_SMOOTHING_ENABLED: Lazy<bool> = Lazy::new(|| {
autoreleasepool(|| unsafe {
let key = NSString::alloc(nil).init_str("AppleFontSmoothing");
let value: id = msg_send![id::standardUserDefaults(), objectForKey: key];
autoreleasepool(|_| {
let value = unsafe {
NSUserDefaults::standardUserDefaults().objectForKey(ns_string!("AppleFontSmoothing"))
};

if msg_send![value, isKindOfClass: class!(NSNumber)] {
let num_type: *const c_char = msg_send![value, objCType];
if num_type.is_null() {
return true;
}
let value = match value {
Some(value) => value,
None => return true,
};

// SAFETY: The values in `NSUserDefaults` are always subclasses of
// `NSObject`.
let value: Retained<NSObject> = unsafe { Retained::cast(value) };

if value.is_kind_of::<NSNumber>() {
// SAFETY: Just checked that the value is a NSNumber
let value: Retained<NSNumber> = unsafe { Retained::cast(value) };

// NSNumber's objCType method returns one of these strings depending on the size:
// q = quad (long long), l = long, i = int, s = short.
// This is done to reject booleans, which are NSNumbers with an objCType of "c", but
// macOS does not treat them the same as an integer 0 or 1 for this setting,
// it just ignores it.
let int_specifiers: [&[u8]; 4] = [b"q", b"l", b"i", b"s"];
if !int_specifiers.contains(&CStr::from_ptr(num_type).to_bytes()) {

let encoding = unsafe { CStr::from_ptr(value.objCType().as_ptr()).to_bytes() };
if !int_specifiers.contains(&encoding) {
return true;
}

let smoothing: NSInteger = msg_send![value, integerValue];
let smoothing = value.integerValue();
smoothing != 0
} else if msg_send![value, isKindOfClass: class!(NSString)] {
let smoothing: NSInteger = msg_send![value, integerValue];
} else if value.is_kind_of::<NSString>() {
// SAFETY: Just checked that the value is a NSString
let value: Retained<NSString> = unsafe { Retained::cast(value) };
let smoothing = unsafe { value.integerValue() };
smoothing != 0
} else {
true
Expand Down
2 changes: 1 addition & 1 deletion src/ft/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ impl FreeTypeLoader {
font_key: FontKey,
) -> Result<Option<FontKey>, Error> {
if let Some(ft_face_location) = pattern.ft_face_location(0) {
if self.faces.get(&font_key).is_some() {
if self.faces.contains_key(&font_key) {
return Ok(Some(font_key));
}

Expand Down

0 comments on commit 895ccc1

Please sign in to comment.