ferroui_native/
screen_impl.rs1use crate::frn_string::frn_string_to_string;
4use crate::helpers::*;
5use crate::interop::*;
6use crate::top_level_impl::top_level_of;
7use ferroui_controls::platform::{
8 ITopLevelImpl, PlatformHandle, PlatformScreen, Screen, ScreenOrientation, ScreensBase, ScreensBaseImpl,
9 ScreensBaseImplExt,
10};
11use ferroui_microcom::{ComPtr, HResult};
12use std::cell::RefCell;
13use std::rc::{Rc, Weak};
14
15fn to_screen_orientation(orientation: FrnScreenOrientation) -> Option<ScreenOrientation> {
18 Some(match orientation.0 {
19 0 => ScreenOrientation::None,
20 1 => ScreenOrientation::Landscape,
21 2 => ScreenOrientation::Portrait,
22 3 => ScreenOrientation::LandscapeFlipped,
23 4 => ScreenOrientation::PortraitFlipped,
24 _ => return None,
25 })
26}
27
28pub struct NativeScreen {
30 base: PlatformScreen,
31 display_id: u32,
32}
33
34impl NativeScreen {
35 fn new(display_id: u32) -> Self {
36 Self {
37 base: PlatformScreen::new(Rc::new(PlatformHandle::new(display_id as isize, Some("CGDirectDisplayID")))),
38 display_id,
39 }
40 }
41
42 fn refresh(&self, native: &IFrnScreens) {
43 let mut localized_name: *mut std::ffi::c_void = std::ptr::null_mut();
44 let screen = unsafe { native.get_screen(self.display_id, &mut localized_name) }.check();
47
48 self.base.set_is_primary(screen.is_primary);
49 self.base.set_scaling(screen.scaling as f64);
50 self.base.set_bounds(to_ferro_pixel_rect(screen.bounds));
51 self.base.set_working_area(to_ferro_pixel_rect(screen.working_area));
52 self.base.set_current_orientation(match to_screen_orientation(screen.orientation) {
53 Some(orientation) => orientation,
54 None => panic!("Specified argument was out of the range of valid values: {:?}", screen.orientation),
55 });
56
57 let name = unsafe { ComPtr::from_raw(localized_name as *mut IFrnString) };
59 self.base.set_display_name(name.and_then(|name| frn_string_to_string(&name)));
60 }
61}
62
63impl AsRef<PlatformScreen> for NativeScreen {
64 fn as_ref(&self) -> &PlatformScreen {
65 &self.base
66 }
67}
68
69pub struct ScreenImpl {
71 base: ScreensBase<u32, NativeScreen>,
72 native: RefCell<Option<ComPtr<IFrnScreens>>>,
73}
74
75struct ScreenEvents(Weak<ScreenImpl>);
76
77impl IFrnScreenEventsImpl for ScreenEvents {
78 fn on_changed(&self) -> Result<(), HResult> {
79 crate::callback_base::guard(Ok(()), || {
80 if let Some(screen_impl) = self.0.upgrade() {
81 screen_impl.on_changed();
82 }
83 Ok(())
84 })
85 }
86}
87
88impl ScreenImpl {
89 pub fn new(factory: impl FnOnce(&IFrnScreenEvents) -> Option<ComPtr<IFrnScreens>>) -> Rc<ScreenImpl> {
92 Rc::new_cyclic(|weak_self| {
93 let events = IFrnScreenEvents::from_impl(ScreenEvents(weak_self.clone()));
94 ScreenImpl { base: ScreensBase::new(), native: RefCell::new(factory(&events)) }
95 })
96 }
97
98 #[track_caller]
99 fn native(&self) -> ComPtr<IFrnScreens> {
100 match self.native.borrow().clone() {
101 Some(native) => native,
102 None => panic!("Cannot access a disposed object: the native screens"),
103 }
104 }
105
106 pub fn dispose(&self) {
108 let native = self.native.borrow_mut().take();
109 drop(native);
110 }
111}
112
113impl ScreensBaseImpl for ScreenImpl {
114 type Key = u32;
115 type Screen = NativeScreen;
116
117 fn screens_base(&self) -> &ScreensBase<u32, NativeScreen> {
118 &self.base
119 }
120
121 fn get_screen_count(&self) -> i32 {
122 unsafe { self.native().get_screen_ids(std::ptr::null_mut()) }.check()
124 }
125
126 fn get_all_screen_keys(&self) -> Vec<u32> {
127 let native = self.native();
128 let screen_count = unsafe { native.get_screen_ids(std::ptr::null_mut()) }.check();
130 let mut display_ids = vec![0u32; screen_count.max(0) as usize];
131 if !display_ids.is_empty() {
132 unsafe { native.get_screen_ids(display_ids.as_mut_ptr()) }.check();
135 }
136
137 display_ids
138 }
139
140 fn create_screen_from_key(&self, key: &u32) -> Rc<NativeScreen> {
141 Rc::new(NativeScreen::new(*key))
142 }
143
144 fn screen_changed(&self, screen: &Rc<NativeScreen>) {
145 screen.refresh(&self.native());
146 }
147
148 fn screen_from_top_level_core(&self, top_level: &dyn ITopLevelImpl) -> Option<Rc<Screen>> {
149 let Some(top_level) = top_level_of(top_level) else {
150 panic!("The top-level belongs to a different windowing platform.");
151 };
152 let display_id = top_level.native()?.get_current_display_id().check();
153 let screen = self.try_get_screen(&display_id)?;
154 Some((*screen).as_ref().screen().clone())
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn orientations() {
164 assert_eq!(to_screen_orientation(FrnScreenOrientation::UnknownOrientation), Some(ScreenOrientation::None));
165 assert_eq!(to_screen_orientation(FrnScreenOrientation::Landscape), Some(ScreenOrientation::Landscape));
166 assert_eq!(to_screen_orientation(FrnScreenOrientation::Portrait), Some(ScreenOrientation::Portrait));
167 assert_eq!(
168 to_screen_orientation(FrnScreenOrientation::LandscapeFlipped),
169 Some(ScreenOrientation::LandscapeFlipped)
170 );
171 assert_eq!(
172 to_screen_orientation(FrnScreenOrientation::PortraitFlipped),
173 Some(ScreenOrientation::PortraitFlipped)
174 );
175 assert_eq!(to_screen_orientation(FrnScreenOrientation(5)), None);
176 }
177}