Skip to content

Commit

Permalink
Use objc2 for determining whether font smoothing is enabled
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 committed May 21, 2024
1 parent 169fb22 commit c9c1430
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 20 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The sections should follow the order `Added`, `Changed`, `Fixed`, and `Removed`.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Changed

- On macOS, use `objc2` when determining whether font smoothing is enabled.

## 0.8.0

### Changed
Expand Down
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
42 changes: 24 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,40 @@ 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"))
};
let Some(value) = value else {
return true;
};

if msg_send![value, isKindOfClass: class!(NSNumber)] {
let num_type: *const c_char = msg_send![value, objCType];
if num_type.is_null() {
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

0 comments on commit c9c1430

Please sign in to comment.