#[allow(unused_imports)]
use raw_window_handle::{
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle as RwhRawDisplayHandle,
RawWindowHandle as RwhRawWindowHandle,
};
use truce_core::editor::RawWindowHandle;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
pub struct ParentWindow(pub RawWindowHandle);
unsafe impl HasRawWindowHandle for ParentWindow {
fn raw_window_handle(&self) -> RwhRawWindowHandle {
match self.0 {
RawWindowHandle::AppKit(ptr) => {
let mut handle = raw_window_handle::AppKitWindowHandle::empty();
handle.ns_view = ptr;
RwhRawWindowHandle::AppKit(handle)
}
RawWindowHandle::UiKit(ptr) => {
let mut handle = raw_window_handle::UiKitWindowHandle::empty();
handle.ui_view = ptr;
RwhRawWindowHandle::UiKit(handle)
}
RawWindowHandle::Win32(ptr) => {
let mut handle = raw_window_handle::Win32WindowHandle::empty();
handle.hwnd = ptr;
RwhRawWindowHandle::Win32(handle)
}
RawWindowHandle::X11(window_id) => {
let mut handle = raw_window_handle::XlibWindowHandle::empty();
#[allow(clippy::cast_possible_truncation)]
{
handle.window = window_id as _;
}
RwhRawWindowHandle::Xlib(handle)
}
}
}
}
#[cfg(target_os = "macos")]
#[must_use]
pub fn query_backing_scale(parent: &RawWindowHandle) -> f64 {
use objc::{msg_send, sel, sel_impl};
let ns_view_ptr = match parent {
RawWindowHandle::AppKit(ptr) => *ptr,
_ => return 1.0,
};
if ns_view_ptr.is_null() {
return 1.0;
}
unsafe {
let ns_view = ns_view_ptr.cast::<objc::runtime::Object>();
let window: *mut objc::runtime::Object = msg_send![ns_view, window];
let scale: f64 = if window.is_null() {
let screen: *mut objc::runtime::Object = msg_send![objc::class!(NSScreen), mainScreen];
if screen.is_null() {
2.0
} else {
msg_send![screen, backingScaleFactor]
}
} else {
msg_send![window, backingScaleFactor]
};
if scale < 1.0 { 1.0 } else { scale }
}
}
#[cfg(target_os = "windows")]
#[must_use]
pub fn query_backing_scale(parent: &RawWindowHandle) -> f64 {
let hwnd = match parent {
RawWindowHandle::Win32(ptr) => *ptr,
_ => return 1.0,
};
win32_dpi_scale(hwnd)
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn query_backing_scale(_parent: &RawWindowHandle) -> f64 {
main_screen_scale()
}
#[cfg(target_os = "ios")]
#[must_use]
pub fn query_backing_scale(parent: &RawWindowHandle) -> f64 {
use objc2::msg_send;
use objc2::runtime::AnyObject;
let ui_view_ptr = match parent {
RawWindowHandle::UiKit(ptr) => *ptr,
_ => return 1.0,
};
if ui_view_ptr.is_null() {
return main_screen_scale();
}
unsafe {
let ui_view: *mut AnyObject = ui_view_ptr.cast();
let scale: f64 = msg_send![ui_view, contentScaleFactor];
if scale > 0.0 { scale } else { 1.0 }
}
}
#[cfg(target_os = "ios")]
#[must_use]
pub fn main_screen_scale() -> f64 {
use objc2::msg_send;
use objc2::runtime::{AnyClass, AnyObject};
unsafe {
let Some(cls) = AnyClass::get(c"UIScreen") else {
return 1.0;
};
let screen: *mut AnyObject = msg_send![cls, mainScreen];
if screen.is_null() {
return 1.0;
}
let scale: f64 = msg_send![screen, scale];
if scale > 0.0 { scale } else { 1.0 }
}
}
#[cfg(target_os = "macos")]
#[must_use]
pub fn main_screen_scale() -> f64 {
use objc::{msg_send, sel, sel_impl};
unsafe {
let screen: *mut objc::runtime::Object = msg_send![objc::class!(NSScreen), mainScreen];
if screen.is_null() {
1.0
} else {
let scale: f64 = msg_send![screen, backingScaleFactor];
if scale < 1.0 { 1.0 } else { scale }
}
}
}
#[cfg(target_os = "windows")]
#[must_use]
pub fn main_screen_scale() -> f64 {
win32_dpi_scale(std::ptr::null_mut())
}
#[derive(Clone)]
pub struct EditorScale {
inner: Arc<AtomicU64>,
}
impl EditorScale {
#[must_use]
pub fn new(initial: f64) -> Self {
let v = if initial.is_finite() && initial > 0.0 {
initial
} else {
1.0
};
Self {
inner: Arc::new(AtomicU64::new(v.to_bits())),
}
}
#[must_use]
pub fn get(&self) -> f64 {
f64::from_bits(self.inner.load(Ordering::Relaxed))
}
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub fn get_f32(&self) -> f32 {
self.get() as f32
}
pub fn set(&self, scale: f64) {
if scale.is_finite() && scale > 0.0 {
self.inner.store(scale.to_bits(), Ordering::Relaxed);
} else {
log::warn!(
"EditorScale::set ignored a bad value ({scale}); \
expected finite, positive f64",
);
}
}
#[allow(clippy::cast_possible_truncation, clippy::float_cmp)]
pub fn take_change(&self, last: &mut f32) -> Option<f32> {
let cur = self.get() as f32;
if cur == *last {
None
} else {
*last = cur;
Some(cur)
}
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
#[inline]
#[must_use]
pub fn to_physical_px(logical: u32, scale: f64) -> u32 {
(f64::from(logical.max(1)) * scale).round().max(1.0) as u32
}
#[cfg(target_os = "linux")]
static LINUX_SCALE_BITS: AtomicU64 = AtomicU64::new(0);
pub fn note_linux_scale_factor(scale: f64) {
#[cfg(target_os = "linux")]
{
if scale.is_finite() && scale > 0.0 {
LINUX_SCALE_BITS.store(scale.to_bits(), Ordering::Relaxed);
}
}
#[cfg(not(target_os = "linux"))]
{
let _ = scale;
}
}
#[cfg(target_os = "linux")]
pub fn main_screen_scale() -> f64 {
if let Ok(s) = std::env::var("TRUCE_SCALE")
&& let Ok(v) = s.parse::<f64>()
&& v.is_finite()
&& v > 0.0
{
return v;
}
let bits = LINUX_SCALE_BITS.load(Ordering::Relaxed);
if bits == 0 {
return 1.0;
}
let v = f64::from_bits(bits);
if v.is_finite() && v > 0.0 { v } else { 1.0 }
}
#[cfg(target_os = "windows")]
fn win32_dpi_scale(hwnd: *mut std::ffi::c_void) -> f64 {
const DEFAULT_DPI: u32 = 96;
unsafe extern "system" {
fn GetDpiForWindow(hwnd: *mut std::ffi::c_void) -> u32;
fn GetDpiForSystem() -> u32;
}
let dpi = if hwnd.is_null() {
unsafe { GetDpiForSystem() }
} else {
let d = unsafe { GetDpiForWindow(hwnd) };
if d == 0 {
unsafe { GetDpiForSystem() }
} else {
d
}
};
if dpi == 0 {
1.0
} else {
f64::from(dpi) / f64::from(DEFAULT_DPI)
}
}
#[cfg(target_os = "windows")]
fn current_module_hinstance() -> Option<std::num::NonZeroIsize> {
unsafe extern "system" {
fn GetModuleHandleW(lpModuleName: *const u16) -> isize;
}
let hmodule = unsafe { GetModuleHandleW(std::ptr::null()) };
std::num::NonZeroIsize::new(hmodule)
}
#[cfg(not(target_os = "ios"))]
#[must_use]
pub unsafe fn create_wgpu_surface(
instance: &wgpu::Instance,
window: &baseview::Window,
) -> Option<wgpu::Surface<'static>> {
unsafe {
let rwh = window.raw_window_handle();
let surface_target = match rwh {
#[cfg(target_os = "macos")]
RwhRawWindowHandle::AppKit(handle) => {
let ns_view = handle.ns_view;
if ns_view.is_null() {
return None;
}
let rwh6_window = wgpu::rwh::RawWindowHandle::AppKit(
wgpu::rwh::AppKitWindowHandle::new(std::ptr::NonNull::new(ns_view)?),
);
let rwh6_display =
wgpu::rwh::RawDisplayHandle::AppKit(wgpu::rwh::AppKitDisplayHandle::new());
wgpu::SurfaceTargetUnsafe::RawHandle {
raw_display_handle: Some(rwh6_display),
raw_window_handle: rwh6_window,
}
}
#[cfg(target_os = "windows")]
RwhRawWindowHandle::Win32(handle) => {
let hwnd = handle.hwnd;
if hwnd.is_null() {
return None;
}
let mut win32 =
wgpu::rwh::Win32WindowHandle::new(std::num::NonZeroIsize::new(hwnd as isize)?);
win32.hinstance = current_module_hinstance();
let rwh6_window = wgpu::rwh::RawWindowHandle::Win32(win32);
let rwh6_display =
wgpu::rwh::RawDisplayHandle::Windows(wgpu::rwh::WindowsDisplayHandle::new());
wgpu::SurfaceTargetUnsafe::RawHandle {
raw_display_handle: Some(rwh6_display),
raw_window_handle: rwh6_window,
}
}
#[cfg(target_os = "linux")]
RwhRawWindowHandle::Xlib(handle) => {
let RwhRawDisplayHandle::Xlib(display_handle) = window.raw_display_handle() else {
return None;
};
let display_ptr = std::ptr::NonNull::new(display_handle.display);
let rwh6_window = wgpu::rwh::RawWindowHandle::Xlib(
wgpu::rwh::XlibWindowHandle::new(handle.window),
);
let rwh6_display = wgpu::rwh::RawDisplayHandle::Xlib(
wgpu::rwh::XlibDisplayHandle::new(display_ptr, display_handle.screen),
);
wgpu::SurfaceTargetUnsafe::RawHandle {
raw_display_handle: Some(rwh6_display),
raw_window_handle: rwh6_window,
}
}
_ => return None,
};
instance.create_surface_unsafe(surface_target).ok()
}
}