fltk/app/
screen.rs

1// https://www.fltk.org/doc-1.4/group__fl__screen.html
2
3use fltk_sys::fl;
4
5use crate::{
6    draw::{Coordinates, Rect},
7    prelude::{FltkError, FltkErrorKind},
8};
9type Coord = Coordinates<i32>; // TEMP
10
11/// An available screen
12///
13/// Unlike the standalone functions, it automatically checks the provided
14/// coordinates and screen numbers are currently valid and inside boundaries.
15#[derive(Debug, Copy, Clone)]
16pub struct Screen {
17    /// The screen number
18    pub n: i32,
19}
20
21impl Screen {
22    // constructors
23
24    /// Returns a vector containing all the `Screen`s, ordered by screen number
25    pub fn all_screens() -> Vec<Screen> {
26        let mut screens: Vec<Self> = vec![];
27        for n in 0..Self::count() {
28            screens.push(Self::new(n).unwrap());
29        }
30        screens
31    }
32
33    /// Returns the `Screen` associated with the given screen number
34    ///
35    /// Returns an error if the provided `number` is not a valid screen number.
36    pub fn new(number: i32) -> Result<Screen, FltkError> {
37        let s = Screen { n: number };
38        if s.is_valid() {
39            Ok(s)
40        } else {
41            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
42        }
43    }
44
45    /// Returns the `Screen` that contains the specified screen position
46    ///
47    /// Returns an error if the provided coordinates are out of bounds.
48    pub fn new_at<C: Into<Coord> + Copy>(pos: C) -> Result<Screen, FltkError> {
49        let pos: Coord = pos.into();
50
51        let s = Screen {
52            n: unsafe { fl::Fl_screen_num(pos.x, pos.y) },
53        };
54
55        if Self::is_coord_inside_any_work_area(pos) {
56            Ok(s)
57        } else {
58            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
59        }
60    }
61
62    /// Returns the `Screen` which intersects the most with the provided rectangle
63    ///
64    /// Returns an error if any coordinates on the provided retangle are out of bounds.
65    pub fn new_inside<R: Into<Rect> + Copy>(rect: R) -> Result<Screen, FltkError> {
66        let r: Rect = rect.into();
67        let s = Screen {
68            n: unsafe { fl::Fl_screen_num_inside(r.x, r.y, r.w, r.h) },
69        };
70
71        if Self::is_rect_inside_any_work_area(r) {
72            Ok(s)
73        } else {
74            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
75        }
76    }
77
78    // associated functions
79
80    /// Returns true if scaling factors are supported by this platform,
81    /// wether shared by all screens, or each screen having its own value
82    pub fn scaling_supported() -> bool {
83        unsafe { fl::Fl_screen_scaling_supported() != 0 }
84    }
85
86    /// Returns true if each screen can have its own scaling factor value
87    pub fn scaling_supported_separately() -> bool {
88        unsafe { fl::Fl_screen_scaling_supported() == 2 }
89    }
90
91    /// Returns the number of available screens
92    pub fn count() -> i32 {
93        unsafe { fl::Fl_screen_count() }
94    }
95
96    /// Returns the screen number of the screen that contains
97    /// the specified screen position coordinates
98    ///
99    /// Returns an error if the provided coordinates are out of bounds.
100    pub fn num_at<C: Into<Coord> + Copy>(pos: C) -> Result<i32, FltkError> {
101        let pos: Coord = pos.into();
102        if Self::is_coord_inside_any_work_area(pos) {
103            Ok(unsafe { fl::Fl_screen_num(pos.x, pos.y) })
104        } else {
105            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
106        }
107    }
108
109    /// Returns the bounding rectangle of the work area of the screen that
110    /// contains the specified screen position coordinates
111    ///
112    /// Returns an error if the provided coordinates are out of bounds.
113    pub fn work_area_at<C: Into<Coord> + Copy>(pos: C) -> Result<Rect, FltkError> {
114        let pos: Coord = pos.into();
115        if Self::is_coord_inside_any_work_area(pos) {
116            let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
117            unsafe { fl::Fl_screen_work_area_at(&mut x, &mut y, &mut w, &mut h, pos.x, pos.y) }
118            Ok(Rect { x, y, w, h })
119        } else {
120            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
121        }
122    }
123
124    /// Returns the bounding rectangle of the work area of the screen currently under
125    /// the mouse pointer coordinates
126    pub fn work_area_mouse() -> Rect {
127        let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
128        unsafe { fl::Fl_screen_work_area_mouse(&mut x, &mut y, &mut w, &mut h) }
129        Rect { x, y, w, h }
130    }
131
132    /// Returns the bounding rectangle of the work area
133    /// with the provided screen `number`
134    ///
135    /// Returns an error if the provided `number` is not a valid screen number.
136    pub fn work_area_num(number: i32) -> Result<Rect, FltkError> {
137        let s = Screen { n: number };
138        if s.is_valid() {
139            let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
140            unsafe { fl::Fl_screen_work_area(&mut x, &mut y, &mut w, &mut h, number) }
141            Ok(Rect { x, y, w, h })
142        } else {
143            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
144        }
145    }
146
147    /// Returns the bounding rectangle of the screen that
148    /// contains the specified screen position coordinates
149    ///
150    /// Returns an error if the provided coordinates are out of bounds.
151    pub fn xywh_at<C: Into<Coord> + Copy>(pos: C) -> Result<Rect, FltkError> {
152        let pos: Coord = pos.into();
153        if Self::is_coord_inside_any_xywh(pos) {
154            let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
155            unsafe { fl::Fl_screen_xywh_at(&mut x, &mut y, &mut w, &mut h, pos.x, pos.y) }
156            Ok(Rect { x, y, w, h })
157        } else {
158            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
159        }
160    }
161
162    /// Returns the bounding rectangle of the screen that
163    /// contains the specified screen position coordinates
164    ///
165    /// Returns an error if the provided coordinates are out of bounds.
166    pub fn xywh_inside<R: Into<Rect> + Copy>(rect: R) -> Result<Rect, FltkError> {
167        let r: Rect = rect.into();
168        if Self::is_rect_inside_any_xywh(r) {
169            let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
170            unsafe { fl::Fl_screen_xywh_inside(&mut x, &mut y, &mut w, &mut h, r.x, r.y, r.w, r.h) }
171            Ok(Rect { x, y, w, h })
172        } else {
173            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
174        }
175    }
176
177    /// Gets the bounding rectangle of the screen currently under
178    /// the mouse pointer coordinates
179    pub fn xywh_mouse() -> Rect {
180        let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
181        unsafe { fl::Fl_screen_xywh_mouse(&mut x, &mut y, &mut w, &mut h) }
182        Rect { x, y, w, h }
183    }
184
185    /// Returns the bounding rectangle of the screen
186    /// with the provided screen `number`
187    ///
188    /// Returns an error if the provided `number` is not a valid screen number.
189    pub fn xywh_num(number: i32) -> Result<Rect, FltkError> {
190        let s = Screen { n: number };
191        if s.is_valid() {
192            let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
193            unsafe {
194                fl::Fl_screen_xywh(&mut x, &mut y, &mut w, &mut h, number);
195            }
196            Ok(Rect { x, y, w, h })
197        } else {
198            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
199        }
200    }
201
202    /// Controls the possibility to scale all windows by ctrl/+/-/0/ or cmd/+/-/0/
203    ///
204    /// This function should be called before `app::open_display` runs.
205    /// If it is not called, the default is to handle these keys for window scaling.
206    ///
207    /// Pass a `value` of `false` to stop recognition of ctrl/+/-/0/
208    /// (or cmd/+/-/0/ under macOS) keys as window scaling.
209    pub fn keyboard_scaling(value: bool) {
210        unsafe { fl::Fl_keyboard_screen_scaling(value.into()) }
211    }
212
213    // methods
214
215    /// Returns `true` if the current screen's number corresponds
216    /// to a currently connected screen, or `false` otherwise.
217    pub fn is_valid(&self) -> bool {
218        self.n >= 0 && self.n < Self::count()
219    }
220
221    /// Returns the current screen (vertical, horizontal) resolution in dots-per-inch
222    pub fn dpi(&self) -> (f32, f32) {
223        let mut h: f32 = 0.;
224        let mut v: f32 = 0.;
225        unsafe {
226            fl::Fl_screen_dpi(&mut h, &mut v, self.n);
227        }
228        (h, v)
229    }
230
231    /// Sets the value of the GUI scaling factor for the current screen
232    pub fn set_scale(&self, factor: f32) {
233        unsafe { fl::Fl_set_screen_scale(self.n, factor) }
234    }
235
236    /// Returns the value of the GUI scaling factor for the current screen
237    pub fn scale(&self) -> f32 {
238        unsafe { fl::Fl_screen_scale(self.n) }
239    }
240
241    /// Returns the the work area of the current screen
242    pub fn work_area(&self) -> Rect {
243        let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
244        unsafe { fl::Fl_screen_work_area(&mut x, &mut y, &mut w, &mut h, self.n) }
245        Rect { x, y, w, h }
246    }
247
248    /// Returns the topmost y coordinate of the current screen's work area
249    pub fn y(&self) -> i32 {
250        self.work_area().y
251    }
252
253    /// Returns the bottom-right x coordinate of the current screen's work area
254    pub fn x(&self) -> i32 {
255        self.work_area().x
256    }
257
258    /// Returns the width in pixels of the current screen's work area
259    pub fn w(&self) -> i32 {
260        self.work_area().w
261    }
262
263    /// Returns the height in pixels of the current screen's work area
264    pub fn h(&self) -> i32 {
265        self.work_area().h
266    }
267
268    /// Returns the top-left `x,y` coordinates of the current screen's work area
269    pub fn top_left(&self) -> Coord {
270        self.work_area().top_left()
271    }
272
273    /// Returns the bottom-right `x+w, y+h` coordinates of the current screen's work area
274    pub fn bottom_right(&self) -> Coord {
275        self.work_area().bottom_right()
276    }
277
278    // private methods
279
280    // returns `true` if the provided position is inside the bounds
281    // of any current work area boundaries, or `false` otherwise.
282    fn is_coord_inside_any_work_area<C: Into<Coord> + Copy>(c: C) -> bool {
283        let c: Coord = c.into();
284        let main_wa: Rect = screen_work_area(0).into();
285        // returns false if we get `0` but the coords are outside main screen's
286        !(screen_num(c.x, c.y) == 0
287            && (c.x < main_wa.x
288                || c.y < main_wa.y
289                || c.x >= main_wa.bottom_right().x
290                || c.y >= main_wa.bottom_right().y))
291    }
292    // returns `true` if the provided rect is fully inside the bounds
293    // of any current work area boundaries, or `false` otherwise.
294    fn is_rect_inside_any_work_area<R: Into<Rect> + Copy>(r: R) -> bool {
295        let r: Rect = r.into();
296        Self::is_coord_inside_any_work_area(r.top_left())
297            && Self::is_coord_inside_any_work_area(r.bottom_right())
298    }
299
300    // returns `true` if the provided position is inside the bounds
301    // of any current screen xywh boundaries, or `false` otherwise.
302    fn is_coord_inside_any_xywh<C: Into<Coord> + Copy>(c: C) -> bool {
303        let c: Coord = c.into();
304        let main_xywh: Rect = screen_xywh(0).into();
305        // returns false if we get `0` but the coords are outside main screen's
306        !(screen_num(c.x, c.y) == 0
307            && (c.x < main_xywh.x
308                || c.y < main_xywh.y
309                || c.x >= main_xywh.bottom_right().x
310                || c.y >= main_xywh.bottom_right().y))
311    }
312    // returns `true` if the provided rect is fully inside the bounds
313    // of any current screeen xywh boundaries, or `false` otherwise.
314    fn is_rect_inside_any_xywh<R: Into<Rect> + Copy>(r: R) -> bool {
315        let r: Rect = r.into();
316        Self::is_coord_inside_any_xywh(r.top_left())
317            && Self::is_coord_inside_any_xywh(r.bottom_right())
318    }
319}
320
321// standalone functions
322
323/// Returns a pair of the width and height of the screen
324pub fn screen_size() -> (i32, i32) {
325    unsafe { (fl::Fl_screen_w(), fl::Fl_screen_h()) }
326}
327
328/// Returns a pair of the x & y coords of the screen
329pub fn screen_coords() -> (i32, i32) {
330    unsafe { (fl::Fl_screen_x(), fl::Fl_screen_y()) }
331}
332
333/// Sets the screen scale
334pub fn set_screen_scale(n: i32, factor: f32) {
335    unsafe { fl::Fl_set_screen_scale(n, factor) }
336}
337
338/// Returns the screen scale
339///
340/// If `screen_num` doesn't correspond to a valid screen number,
341/// the main screen's number (`0`) will be used instead.
342pub fn screen_scale(screen_num: i32) -> f32 {
343    unsafe { fl::Fl_screen_scale(screen_num) }
344}
345
346/// Returns whether scaling the screen is supported
347pub fn screen_scaling_supported() -> bool {
348    unsafe { fl::Fl_screen_scaling_supported() != 0 }
349}
350
351/// Returns the screen count
352pub fn screen_count() -> i32 {
353    unsafe { fl::Fl_screen_count() }
354}
355
356/// Returns the screen number of a screen that contains the specified position
357///
358/// If the coordinates are out of bounds, the main screen's number (`0`)
359/// will be returned instead.
360pub fn screen_num(x: i32, y: i32) -> i32 {
361    unsafe { fl::Fl_screen_num(x, y) }
362}
363
364/// Returns the screen number for the screen which intersects the most with
365/// the provided rectangle
366pub fn screen_num_inside<R: Into<Rect> + Copy>(rect: R) -> i32 {
367    let r: Rect = rect.into();
368    unsafe { fl::Fl_screen_num_inside(r.x, r.y, r.w, r.h) }
369}
370
371/// Returns a screen's (vertical, horizontal) dpi resolution
372///
373/// If `screen_num` doesn't correspond to a valid screen number,
374/// the main screen's number (`0`) will be used instead.
375pub fn screen_dpi(screen_num: i32) -> (f32, f32) {
376    let (mut h, mut v) = (0_f32, 0_f32);
377    unsafe {
378        fl::Fl_screen_dpi(&mut h, &mut v, screen_num);
379    }
380    (h, v)
381}
382
383/// Get a screen's xywh
384///
385/// If `screen_num` doesn't correspond to a valid screen number,
386/// the main screen's number (`0`) will be used instead.
387pub fn screen_xywh(screen_num: i32) -> (i32, i32, i32, i32) {
388    let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
389    unsafe {
390        fl::Fl_screen_xywh(&mut x, &mut y, &mut w, &mut h, screen_num);
391    }
392    (x, y, w, h)
393}
394
395/// Get a screen's working area
396///
397/// If `screen_num` doesn't correspond to a valid screen number,
398/// the main screen's number (`0`) will be used instead.
399pub fn screen_work_area(screen_num: i32) -> (i32, i32, i32, i32) {
400    let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
401    unsafe {
402        fl::Fl_screen_work_area(&mut x, &mut y, &mut w, &mut h, screen_num);
403    }
404    (x, y, w, h)
405}
406
407/// Controls the possibility to scale all windows by ctrl/+/-/0/ or cmd/+/-/0/
408///
409/// This function should be called before `app::open_display` runs.
410/// If it is not called, the default is to handle these keys for window scaling.
411///
412/// Pass a `value` of `false` to stop recognition of ctrl/+/-/0/
413/// (or cmd/+/-/0/ under macOS) keys as window scaling.
414pub fn keyboard_screen_scaling(value: bool) {
415    unsafe { fl::Fl_keyboard_screen_scaling(value.into()) }
416}