mirror of
https://github.com/zed-industries/zed.git
synced 2026-10-28 08:55:27 +08:00
gpui_macos: Migrate display bindings to objc2 (#65714)
Use retained NSScreen objects and objc2 main-thread checks while preserving refresh-rate and display-bounds fallbacks. Release Notes: - [GPUI] Migrated macOS display and display-link bindings to objc2
This commit is contained in:
1 parent
0a9eb81e6c
commit
cb1937a8e7
2 files changed
+51
-78
No files matched your search
@@ -1,17 +1,13 @@
|
||||
use crate::ns_string;
|
||||
use anyhow::Result;
|
||||
use cocoa::{
|
||||
appkit::NSScreen,
|
||||
base::{BOOL, NO, YES, id, nil},
|
||||
foundation::{NSArray, NSDictionary},
|
||||
};
|
||||
use core_foundation::base::CFRelease;
|
||||
use core_foundation::uuid::{CFUUIDGetUUIDBytes, CFUUIDRef};
|
||||
use core_graphics::display::{
|
||||
CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGGetActiveDisplayList,
|
||||
};
|
||||
use gpui::{Bounds, DisplayId, Pixels, PlatformDisplay, point, px, refresh_interval_from_hz, size};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use objc2::{MainThreadMarker, msg_send, rc::Retained, sel};
|
||||
use objc2_app_kit::NSScreen;
|
||||
use objc2_foundation::{NSObjectProtocol, ns_string};
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -36,15 +32,15 @@ impl MacDisplay {
|
||||
// The following is what Chromium does too:
|
||||
//
|
||||
// https://chromium.googlesource.com/chromium/src/+/66.0.3359.158/ui/display/mac/screen_mac.mm#56
|
||||
unsafe {
|
||||
let screens = NSScreen::screens(nil);
|
||||
let screen = cocoa::foundation::NSArray::objectAtIndex(screens, 0);
|
||||
let device_description = NSScreen::deviceDescription(screen);
|
||||
let screen_number_key: id = ns_string("NSScreenNumber");
|
||||
let screen_number = device_description.objectForKey_(screen_number_key);
|
||||
let screen_number: CGDirectDisplayID = msg_send![screen_number, unsignedIntegerValue];
|
||||
Self(screen_number)
|
||||
}
|
||||
let main_thread = MainThreadMarker::new().expect("displays require the main thread");
|
||||
let screens = NSScreen::screens(main_thread);
|
||||
let screen = screens.firstObject().expect("no primary screen");
|
||||
let screen_number = screen
|
||||
.deviceDescription()
|
||||
.objectForKey(ns_string!("NSScreenNumber"))
|
||||
.map(|number| unsafe { msg_send![&*number, unsignedIntValue] })
|
||||
.unwrap_or_default();
|
||||
Self(screen_number)
|
||||
}
|
||||
|
||||
/// Obtains an iterator over all currently active system displays.
|
||||
@@ -128,51 +124,37 @@ impl PlatformDisplay for MacDisplay {
|
||||
}
|
||||
|
||||
fn visible_bounds(&self) -> Bounds<Pixels> {
|
||||
unsafe {
|
||||
let dominated_screen = self.get_nsscreen();
|
||||
let Some(dominated_screen) = self.get_nsscreen() else {
|
||||
return self.bounds();
|
||||
};
|
||||
|
||||
if dominated_screen == nil {
|
||||
return self.bounds();
|
||||
}
|
||||
let screen_frame = dominated_screen.frame();
|
||||
let visible_frame = dominated_screen.visibleFrame();
|
||||
|
||||
let screen_frame = NSScreen::frame(dominated_screen);
|
||||
let visible_frame = NSScreen::visibleFrame(dominated_screen);
|
||||
// Convert from bottom-left origin (AppKit) to top-left origin
|
||||
let origin_y =
|
||||
screen_frame.size.height - visible_frame.origin.y - visible_frame.size.height
|
||||
+ screen_frame.origin.y;
|
||||
|
||||
// Convert from bottom-left origin (AppKit) to top-left origin
|
||||
let origin_y =
|
||||
screen_frame.size.height - visible_frame.origin.y - visible_frame.size.height
|
||||
+ screen_frame.origin.y;
|
||||
|
||||
Bounds {
|
||||
origin: point(
|
||||
px(visible_frame.origin.x as f32 - screen_frame.origin.x as f32),
|
||||
px(origin_y as f32),
|
||||
),
|
||||
size: size(
|
||||
px(visible_frame.size.width as f32),
|
||||
px(visible_frame.size.height as f32),
|
||||
),
|
||||
}
|
||||
Bounds {
|
||||
origin: point(
|
||||
px(visible_frame.origin.x as f32 - screen_frame.origin.x as f32),
|
||||
px(origin_y as f32),
|
||||
),
|
||||
size: size(
|
||||
px(visible_frame.size.width as f32),
|
||||
px(visible_frame.size.height as f32),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh_interval(&self) -> Option<Duration> {
|
||||
// `maximumFramesPerSecond` is the ProMotion maximum, where the
|
||||
// display mode's rate is 0 on built-in panels.
|
||||
let screen_hertz = unsafe {
|
||||
let screen = self.get_nsscreen();
|
||||
let supports_maximum: BOOL = if screen == nil {
|
||||
NO
|
||||
} else {
|
||||
msg_send![screen, respondsToSelector: sel!(maximumFramesPerSecond)]
|
||||
};
|
||||
if supports_maximum == YES {
|
||||
let frames_per_second: isize = msg_send![screen, maximumFramesPerSecond];
|
||||
frames_per_second as f64
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let screen_hertz = self
|
||||
.get_nsscreen()
|
||||
.filter(|screen| screen.respondsToSelector(sel!(maximumFramesPerSecond)))
|
||||
.map_or(0.0, |screen| screen.maximumFramesPerSecond() as f64);
|
||||
refresh_interval_from_hz(screen_hertz).or_else(|| {
|
||||
CGDisplay::new(self.0)
|
||||
.display_mode()
|
||||
@@ -183,20 +165,17 @@ impl PlatformDisplay for MacDisplay {
|
||||
|
||||
impl MacDisplay {
|
||||
/// Find the NSScreen corresponding to this display
|
||||
unsafe fn get_nsscreen(&self) -> id {
|
||||
let screens = unsafe { NSScreen::screens(nil) };
|
||||
let count = unsafe { NSArray::count(screens) };
|
||||
let screen_number_key: id = unsafe { ns_string("NSScreenNumber") };
|
||||
|
||||
for i in 0..count {
|
||||
let screen = unsafe { NSArray::objectAtIndex(screens, i) };
|
||||
let device_description = unsafe { NSScreen::deviceDescription(screen) };
|
||||
let screen_number = unsafe { device_description.objectForKey_(screen_number_key) };
|
||||
let screen_id: CGDirectDisplayID = msg_send![screen_number, unsignedIntegerValue];
|
||||
if screen_id == self.0 {
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
nil
|
||||
fn get_nsscreen(&self) -> Option<Retained<NSScreen>> {
|
||||
let main_thread = MainThreadMarker::new().expect("displays require the main thread");
|
||||
NSScreen::screens(main_thread).into_iter().find(|screen| {
|
||||
screen
|
||||
.deviceDescription()
|
||||
.objectForKey(ns_string!("NSScreenNumber"))
|
||||
.is_some_and(|number| {
|
||||
let screen_id: CGDirectDisplayID =
|
||||
unsafe { msg_send![&*number, unsignedIntValue] };
|
||||
screen_id == self.0
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -112,17 +112,11 @@ fn lock_registry() -> MutexGuard<'static, Registry> {
|
||||
}
|
||||
|
||||
fn debug_assert_main_thread() {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
let is_main_thread: objc::runtime::BOOL =
|
||||
unsafe { msg_send![class!(NSThread), isMainThread] };
|
||||
debug_assert!(
|
||||
is_main_thread == objc::runtime::YES,
|
||||
"display link registry mutations must happen on the main thread; \
|
||||
the registry's lock ordering and state consistency depend on it"
|
||||
);
|
||||
}
|
||||
debug_assert!(
|
||||
objc2::MainThreadMarker::new().is_some(),
|
||||
"display link registry mutations must happen on the main thread; \
|
||||
the registry's lock ordering and state consistency depend on it"
|
||||
);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn display_link_output_callback(
|
||||
|
||||
Reference in new issue
Block a user