diff --git a/Cargo.toml b/Cargo.toml index b4c5d74ec6..f2bd0ed6b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,7 +105,9 @@ softbuffer = { version = "0.4.0", default-features = false, features = [ # Android [target.'cfg(target_os = "android")'.dependencies] android-activity = "0.6.0" +jni = "0.21" ndk = { version = "0.9.0", default-features = false } +ndk-context = "0.1" # AppKit or UIKit [target.'cfg(target_vendor = "apple")'.dependencies] diff --git a/examples/window.rs b/examples/window.rs index 78f96de610..2c791d5ac5 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -285,17 +285,14 @@ impl Application { } if let Some(current_mode) = monitor.current_video_mode() { - if let Some(PhysicalSize { width, height }) = current_mode.size() { - let bits = - current_mode.bit_depth().map(|bits| format!("x{bits}")).unwrap_or_default(); - let m_hz = current_mode - .refresh_rate_millihertz() - .map(|m_hz| format!(" @ {}.{} Hz", m_hz.get() / 1000, m_hz.get() % 1000)) - .unwrap_or_default(); - info!(" {width}x{height}{bits}{m_hz}"); - } else { - info!(" {current_mode:?}"); - } + let PhysicalSize { width, height } = current_mode.size(); + let bits = + current_mode.bit_depth().map(|bits| format!("x{bits}")).unwrap_or_default(); + let m_hz = current_mode + .refresh_rate_millihertz() + .map(|m_hz| format!(" @ {}.{} Hz", m_hz.get() / 1000, m_hz.get() % 1000)) + .unwrap_or_default(); + info!(" {width}x{height}{bits}{m_hz}"); } if let Some(PhysicalPosition { x, y }) = monitor.position() { @@ -306,16 +303,13 @@ impl Application { info!(" Available modes (width x height x bit-depth):"); for mode in monitor.video_modes() { - if let Some(PhysicalSize { width, height }) = mode.size() { - let bits = mode.bit_depth().map(|bits| format!("x{bits}")).unwrap_or_default(); - let m_hz = mode - .refresh_rate_millihertz() - .map(|m_hz| format!(" @ {}.{} Hz", m_hz.get() / 1000, m_hz.get() % 1000)) - .unwrap_or_default(); - info!(" {width}x{height}{bits}{m_hz}"); - } else { - info!(" {mode:?}"); - } + let PhysicalSize { width, height } = mode.size(); + let bits = mode.bit_depth().map(|bits| format!("x{bits}")).unwrap_or_default(); + let m_hz = mode + .refresh_rate_millihertz() + .map(|m_hz| format!(" @ {}.{} Hz", m_hz.get() / 1000, m_hz.get() % 1000)) + .unwrap_or_default(); + info!(" {width}x{height}{bits}{m_hz}"); } } } diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 113ff95914..da0e315716 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -55,6 +55,7 @@ changelog entry. information is available. This "detailed monitors" can be used in `Window::set_fullscreen()` as well. - Add `MonitorHandle::current_video_mode()`. +- On Android, fully implement `MonitorHandle` and `VideoModeHandle`. ### Changed @@ -85,7 +86,7 @@ changelog entry. - On Web, `CursorGrabMode::Locked` now lets `DeviceEvent::MouseMotion` return raw data, not OS accelerated, if the browser supports it. - `VideoModeHandle::refresh_rate_millihertz()` now returns an `Option`. -- `VideoModeHandle::size()` and `bit_depth()` now return an `Option`. +- `VideoModeHandle::bit_depth()` now returns an `Option`. ### Removed diff --git a/src/monitor.rs b/src/monitor.rs index 2d5e1ceac6..511ed2042d 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -55,7 +55,7 @@ impl VideoModeHandle { /// /// [`Window::inner_size()`]: crate::window::Window::inner_size #[inline] - pub fn size(&self) -> Option> { + pub fn size(&self) -> PhysicalSize { self.video_mode.size() } @@ -81,6 +81,19 @@ impl VideoModeHandle { } } +impl std::fmt::Display for VideoModeHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}x{} {}{}", + self.size().width, + self.size().height, + self.refresh_rate_millihertz().map(|rate| format!("@ {rate} mHz ")).unwrap_or_default(), + self.bit_depth().map(|bit_depth| format!("({bit_depth} bpp)")).unwrap_or_default(), + ) + } +} + /// Handle to a monitor. /// /// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation. diff --git a/src/platform_impl/android/ffi.rs b/src/platform_impl/android/ffi.rs new file mode 100644 index 0000000000..998a4278a4 --- /dev/null +++ b/src/platform_impl/android/ffi.rs @@ -0,0 +1,167 @@ +use android_activity::AndroidApp; +use jni::objects::{JObject, JObjectArray, JString, JValueOwned}; +use jni::{AttachGuard, JavaVM}; + +pub fn jvm(app: &AndroidApp) -> JavaVM { + unsafe { JavaVM::from_raw(app.vm_as_ptr() as _) }.expect("unexpected null pointer") +} + +pub fn env(jvm: &JavaVM) -> AttachGuard<'_> { + jvm.attach_current_thread().expect("unexpected failure accessing `JNIEnv`") +} + +pub fn activity<'e>(app: &AndroidApp, _: &AttachGuard<'e>) -> Activity<'e> { + Activity(unsafe { JObject::from_raw(app.activity_as_ptr() as _) }) +} + +pub struct Activity<'e>(JObject<'e>); + +impl Activity<'_> { + pub fn window_manager<'e>(&self, env: &mut AttachGuard<'e>) -> WindowManager<'e> { + WindowManager( + env.call_method(&self.0, "getWindowManager", "()Landroid/view/WindowManager;", &[]) + .and_then(JValueOwned::l) + .expect("`Activity.getWindowManager()` is not expected to fail"), + ) + } +} + +pub fn context<'e>(_: &mut AttachGuard<'e>) -> Context<'e> { + Context(unsafe { JObject::from_raw(ndk_context::android_context().context() as _) }) +} + +pub struct Context<'e>(JObject<'e>); + +impl Context<'_> { + pub fn display_manager<'e>(&self, env: &mut AttachGuard<'e>) -> DisplayManager<'e> { + let class = env.get_object_class(&self.0).expect("failed to find `Context` class"); + let display_service = env + .get_static_field(&class, "DISPLAY_SERVICE", "Ljava/lang/String;") + .expect("failed to find `Context.DISPLAY_SERVICE"); + let display_manager = env + .call_method(&self.0, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", &[ + (&display_service).into(), + ]) + .and_then(JValueOwned::l) + .expect( + "`Context.getSystemService()` is not expected to fail with \ + `Context.DISPLAY_SERVICE`", + ); + + DisplayManager(display_manager) + } +} + +pub struct DisplayManager<'e>(JObject<'e>); + +impl DisplayManager<'_> { + pub fn display<'e>(&self, env: &mut AttachGuard<'e>, display_id: i32) -> Option> { + env.call_method(&self.0, "getDisplay", "(I)Landroid/view/Display;", &[display_id.into()]) + .and_then(JValueOwned::l) + .map(Display) + .ok() + } + + pub fn displays<'e>(&self, env: &mut AttachGuard<'e>) -> Vec> { + let displays = JObjectArray::from( + env.call_method(&self.0, "getDisplays", "()[Landroid/view/Display;", &[]) + .and_then(JValueOwned::l) + .expect("`DisplayManager.getDisplays()` is not expected to fail"), + ); + + let length = env.get_array_length(&displays).unwrap(); + + (0..length) + .map(|index| env.get_object_array_element(&displays, index).map(Display).unwrap()) + .collect() + } +} + +pub struct WindowManager<'e>(JObject<'e>); + +impl WindowManager<'_> { + pub fn default_display<'e>(&self, env: &mut AttachGuard<'e>) -> Display<'e> { + Display( + env.call_method(&self.0, "getDefaultDisplay", "()Landroid/view/Display;", &[]) + .and_then(JValueOwned::l) + .expect("`WindowManager.getDefaultDisplay()` is not expected to fail"), + ) + } +} + +pub struct Display<'e>(JObject<'e>); + +impl Display<'_> { + pub fn default_display(env: &mut AttachGuard<'_>) -> i32 { + let class = env.find_class("android/view/Display").expect("failed to find `Display`"); + env.get_static_field(&class, "DEFAULT_DISPLAY", "I;") + .and_then(JValueOwned::i) + .expect("failed to find `Display.DEFAULT_DISPLAY") + } + + pub fn display_id(&self, env: &mut AttachGuard<'_>) -> i32 { + env.call_method(&self.0, "getDisplayId", "()I", &[]) + .and_then(JValueOwned::i) + .expect("`Display.getDisplayId()` is not expected to fail") + } + + pub fn name(&self, env: &mut AttachGuard<'_>) -> String { + let name = JString::from( + env.call_method(&self.0, "getName", "()Ljava/lang/String;", &[]) + .and_then(JValueOwned::l) + .expect("`Display.name()` is not expected to fail"), + ); + let name = env.get_string(&name).unwrap(); + name.to_string_lossy().to_string() + } + + pub fn mode<'e>(&self, env: &mut AttachGuard<'e>) -> Mode<'e> { + Mode( + env.call_method(&self.0, "getMode", "()Landroid/view/Display$Mode;", &[]) + .and_then(JValueOwned::l) + .expect("`Display.getMode()` is not expected to fail"), + ) + } + + pub fn supported_modes<'e>(&self, env: &mut AttachGuard<'e>) -> Vec> { + let modes = JObjectArray::from( + env.call_method(&self.0, "getSupportedModes", "()[Landroid/view/Display$Mode;", &[]) + .and_then(JValueOwned::l) + .expect("`Display.getSupportedModes()` is not expected to fail"), + ); + + let length = env.get_array_length(&modes).unwrap(); + + (0..length) + .map(|index| env.get_object_array_element(&modes, index).map(Mode).unwrap()) + .collect() + } +} + +pub struct Mode<'e>(JObject<'e>); + +impl Mode<'_> { + pub fn mode_id(&self, env: &mut AttachGuard<'_>) -> i32 { + env.call_method(&self.0, "getModeId", "()I", &[]) + .and_then(JValueOwned::i) + .expect("`Display.Mode.getModeId()` is not expected to fail") + } + + pub fn physical_width(&self, env: &mut AttachGuard<'_>) -> i32 { + env.call_method(&self.0, "getPhysicalWidth", "()I", &[]) + .and_then(JValueOwned::i) + .expect("`Display.Mode.getPhysicalWidth()` is not expected to fail") + } + + pub fn physical_height(&self, env: &mut AttachGuard<'_>) -> i32 { + env.call_method(&self.0, "getPhysicalHeight", "()I", &[]) + .and_then(JValueOwned::i) + .expect("`Display.Mode.getPhysicalHeight()` is not expected to fail") + } + + pub fn refresh_rate(&self, env: &mut AttachGuard<'_>) -> f32 { + env.call_method(&self.0, "getRefreshRate", "()F", &[]) + .and_then(JValueOwned::f) + .expect("`Display.Mode.getRefreshRate()` is not expected to fail") + } +} diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 7d1ccc25bc..42684b0596 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -1,5 +1,4 @@ use std::cell::Cell; -use std::collections::VecDeque; use std::hash::Hash; use std::marker::PhantomData; use std::num::NonZeroU32; @@ -27,6 +26,7 @@ use crate::window::{ WindowButtons, WindowLevel, }; +mod ffi; mod keycodes; pub(crate) use crate::cursor::{ @@ -200,11 +200,10 @@ impl EventLoop { app.window_event(self.window_target(), window_id, event); }, MainEvent::ConfigChanged { .. } => { - let monitor = MonitorHandle::new(self.android_app.clone()); - let old_scale_factor = monitor.scale_factor(); - let scale_factor = monitor.scale_factor(); + let old_scale_factor = scale_factor(&self.android_app); + let scale_factor = scale_factor(&self.android_app); if (scale_factor - old_scale_factor).abs() < f64::EPSILON { - let new_inner_size = Arc::new(Mutex::new(screen_size(&self.android_app))); + let new_inner_size = Arc::new(Mutex::new(surface_size(&self.android_app))); let window_id = window::WindowId(WindowId); let event = event::WindowEvent::ScaleFactorChanged { inner_size_writer: InnerSizeWriter::new(Arc::downgrade( @@ -587,7 +586,7 @@ impl ActiveEventLoop { } pub fn primary_monitor(&self) -> Option { - Some(MonitorHandle::new(self.app.clone())) + MonitorHandle::primary_monitor(self.app.clone()) } pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor { @@ -595,10 +594,8 @@ impl ActiveEventLoop { CustomCursor { inner: PlatformCustomCursor } } - pub fn available_monitors(&self) -> VecDeque { - let mut v = VecDeque::with_capacity(1); - v.push_back(MonitorHandle::new(self.app.clone())); - v + pub fn available_monitors(&self) -> Vec { + MonitorHandle::available_monitors(self.app.clone()) } #[inline] @@ -723,21 +720,19 @@ impl Window { } pub fn primary_monitor(&self) -> Option { - Some(MonitorHandle::new(self.app.clone())) + MonitorHandle::primary_monitor(self.app.clone()) } - pub fn available_monitors(&self) -> VecDeque { - let mut v = VecDeque::with_capacity(1); - v.push_back(MonitorHandle::new(self.app.clone())); - v + pub fn available_monitors(&self) -> Vec { + MonitorHandle::available_monitors(self.app.clone()) } pub fn current_monitor(&self) -> Option { - Some(MonitorHandle::new(self.app.clone())) + Some(MonitorHandle::current_monitor(self.app.clone())) } pub fn scale_factor(&self) -> f64 { - MonitorHandle::new(self.app.clone()).scale_factor() + scale_factor(&self.app) } pub fn request_redraw(&self) { @@ -767,7 +762,7 @@ impl Window { } pub fn outer_size(&self) -> PhysicalSize { - screen_size(&self.app) + surface_size(&self.app) } pub fn set_min_inner_size(&self, _: Option) {} @@ -974,6 +969,7 @@ impl Display for OsError { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct MonitorHandle { app: AndroidApp, + id: i32, } impl PartialOrd for MonitorHandle { fn partial_cmp(&self, other: &Self) -> Option { @@ -987,12 +983,59 @@ impl Ord for MonitorHandle { } impl MonitorHandle { - pub(crate) fn new(app: AndroidApp) -> Self { - Self { app } + fn primary_monitor(app: AndroidApp) -> Option { + let jvm = ffi::jvm(&app); + let mut env = ffi::env(&jvm); + + let default_display = ffi::Display::default_display(&mut env); + let id = ffi::context(&mut env) + .display_manager(&mut env) + .display(&mut env, default_display) + ? + .display_id(&mut env); + + Some(Self { app, id }) + } + + fn current_monitor(app: AndroidApp) -> Self { + let jvm = ffi::jvm(&app); + let mut env = ffi::env(&jvm); + + // TODO: at API level 30 use `Context.getDisplay()`. + // + let id = ffi::activity(&app, &env) + .window_manager(&mut env) + .default_display(&mut env) + .display_id(&mut env); + + Self { app, id } + } + + fn available_monitors(app: AndroidApp) -> Vec { + let jvm = ffi::jvm(&app); + let mut env = ffi::env(&jvm); + + let available_monitors = ffi::context(&mut env) + .display_manager(&mut env) + .displays(&mut env) + .into_iter() + .map(|display| Self { app: app.clone(), id: display.display_id(&mut env) }) + .collect(); + + available_monitors } pub fn name(&self) -> Option { - None + let jvm = ffi::jvm(&self.app); + let mut env = ffi::env(&jvm); + + Some( + ffi::context(&mut env) + .display_manager(&mut env) + .display(&mut env, self.id) + .unwrap() + .name(&mut env), + ) } pub fn position(&self) -> Option> { @@ -1000,27 +1043,67 @@ impl MonitorHandle { } pub fn scale_factor(&self) -> f64 { - self.app.config().density().map(|dpi| dpi as f64 / 160.0).unwrap_or(1.0) + scale_factor(&self.app) } pub fn current_video_mode(&self) -> Option { - Some(VideoModeHandle { monitor: self.clone() }) + let jvm = ffi::jvm(&self.app); + let mut env = ffi::env(&jvm); + + let id = ffi::context(&mut env) + .display_manager(&mut env) + .display(&mut env, self.id) + .unwrap() + .mode(&mut env) + .mode_id(&mut env); + + Some(VideoModeHandle { app: self.app.clone(), display_id: self.id, mode_id: id }) } pub fn video_modes(&self) -> impl Iterator { - // TODO: https://developer.android.com/reference/android/view/Display.Mode - self.current_video_mode().into_iter() + let jvm = ffi::jvm(&self.app); + let mut env = ffi::env(&jvm); + + ffi::context(&mut env) + .display_manager(&mut env) + .display(&mut env, self.id) + .unwrap() + .supported_modes(&mut env) + .into_iter() + .map(|mode| VideoModeHandle { + app: self.app.clone(), + display_id: self.id, + mode_id: mode.mode_id(&mut env), + }) + .collect::>() + .into_iter() } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct VideoModeHandle { - monitor: MonitorHandle, + app: AndroidApp, + display_id: i32, + mode_id: i32, } impl VideoModeHandle { - pub fn size(&self) -> Option> { - None + pub fn size(&self) -> PhysicalSize { + let jvm = ffi::jvm(&self.app); + let mut env = ffi::env(&jvm); + + let mode = ffi::context(&mut env) + .display_manager(&mut env) + .display(&mut env, self.display_id) + .unwrap() + .supported_modes(&mut env) + .into_iter() + .find(|mode| mode.mode_id(&mut env) == self.mode_id) + .unwrap(); + let width = mode.physical_width(&mut env); + let height = mode.physical_height(&mut env); + + PhysicalSize::new(width, height).cast() } pub fn bit_depth(&self) -> Option { @@ -1028,25 +1111,41 @@ impl VideoModeHandle { } pub fn refresh_rate_millihertz(&self) -> Option { - // TODO: get this via either `android.view.Display.getRefreshRate()` (requires JNI, but - // works all the way back to API level 1) or `AChoreographer_registerRefreshRateCallback` - // (only available since API level 30, related to the Frame Rate API). + // TODO: Use `AChoreographer_registerRefreshRateCallback` when on API level 30 (related to + // the Frame Rate API). // - // `android.view.Display.getRefreshRate()`: // `AChoreographer_registerRefreshRateCallback`: // Frame Rate API: - None + + let jvm = ffi::jvm(&self.app); + let mut env = ffi::env(&jvm); + + let refresh_rate = ffi::context(&mut env) + .display_manager(&mut env) + .display(&mut env, self.display_id) + .unwrap() + .supported_modes(&mut env) + .into_iter() + .find(|mode| mode.mode_id(&mut env) == self.mode_id) + .unwrap() + .refresh_rate(&mut env); + + NonZeroU32::new((refresh_rate * 1000.) as u32) } pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone() + MonitorHandle { app: self.app.clone(), id: self.display_id } } } -fn screen_size(app: &AndroidApp) -> PhysicalSize { +fn surface_size(app: &AndroidApp) -> PhysicalSize { if let Some(native_window) = app.native_window() { PhysicalSize::new(native_window.width() as _, native_window.height() as _) } else { PhysicalSize::new(0, 0) } } + +fn scale_factor(app: &AndroidApp) -> f64 { + app.config().density().map(|dpi| dpi as f64 / 160.0).unwrap_or(1.0) +} diff --git a/src/platform_impl/apple/appkit/monitor.rs b/src/platform_impl/apple/appkit/monitor.rs index 29091d7a27..2568954011 100644 --- a/src/platform_impl/apple/appkit/monitor.rs +++ b/src/platform_impl/apple/appkit/monitor.rs @@ -113,8 +113,8 @@ impl VideoModeHandle { } } - pub fn size(&self) -> Option> { - Some(self.size) + pub fn size(&self) -> PhysicalSize { + self.size } pub fn bit_depth(&self) -> Option { diff --git a/src/platform_impl/apple/uikit/monitor.rs b/src/platform_impl/apple/uikit/monitor.rs index 6b02fdc1f8..4e35854fec 100644 --- a/src/platform_impl/apple/uikit/monitor.rs +++ b/src/platform_impl/apple/uikit/monitor.rs @@ -66,8 +66,8 @@ impl VideoModeHandle { } } - pub fn size(&self) -> Option> { - Some(self.size.into()) + pub fn size(&self) -> PhysicalSize { + self.size.into() } pub fn bit_depth(&self) -> Option { diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 403f82cca8..cc8dbfcb0c 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -256,7 +256,7 @@ pub enum VideoModeHandle { impl VideoModeHandle { #[inline] - pub fn size(&self) -> Option> { + pub fn size(&self) -> PhysicalSize { x11_or_wayland!(match self; VideoModeHandle(m) => m.size()) } diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index 4e90de013e..f19a7ced1d 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -135,8 +135,8 @@ impl VideoModeHandle { } #[inline] - pub fn size(&self) -> Option> { - Some(self.size) + pub fn size(&self) -> PhysicalSize { + self.size } #[inline] diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index fbbe35c481..a565c810ac 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -30,8 +30,8 @@ pub struct VideoModeHandle { impl VideoModeHandle { #[inline] - pub fn size(&self) -> Option> { - Some(self.size.into()) + pub fn size(&self) -> PhysicalSize { + self.size.into() } #[inline] diff --git a/src/platform_impl/orbital/mod.rs b/src/platform_impl/orbital/mod.rs index 560358bda4..08b158959e 100644 --- a/src/platform_impl/orbital/mod.rs +++ b/src/platform_impl/orbital/mod.rs @@ -216,9 +216,9 @@ pub struct VideoModeHandle { } impl VideoModeHandle { - pub fn size(&self) -> Option> { + pub fn size(&self) -> PhysicalSize { // TODO - None + PhysicalSize::default() } pub fn bit_depth(&self) -> Option { diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index faf6556ad4..7e99f2f0dc 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -226,7 +226,7 @@ impl OrientationLockError { pub struct VideoModeHandle(Dispatcher); impl VideoModeHandle { - pub fn size(&self) -> Option> { + pub fn size(&self) -> PhysicalSize { self.0.queue(|inner| inner.size()) } @@ -330,15 +330,15 @@ impl Inner { matches!(self.screen, Screen::Detailed(_)) } - fn size(&self) -> Option> { + fn size(&self) -> PhysicalSize { let width = self.screen.width().unwrap(); let height = self.screen.height().unwrap(); - Some(if let Some(Engine::Chromium) = self.engine { + if let Some(Engine::Chromium) = self.engine { PhysicalSize::new(width, height).cast() } else { LogicalSize::new(width, height).to_physical(super::web_sys::scale_factor(&self.window)) - }) + } } pub fn bit_depth(&self) -> Option { diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 125ce3706f..3f1c387986 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -74,8 +74,8 @@ impl VideoModeHandle { } } - pub fn size(&self) -> Option> { - Some(self.size.into()) + pub fn size(&self) -> PhysicalSize { + self.size.into() } pub fn bit_depth(&self) -> Option {