fltk/
window.rs

1#![allow(unused_imports)]
2
3//! Window widgets
4//!
5//! **Multithreaded** applications should always create/show/open/close windows
6//! from the main thread (This might or might not work on your specific target,
7//! due to fltk calling the underlying platform's window code. If you want
8//! portability, avoid it.) If you need to trigger showing a windows from
9//! another thread, use [`messages`](crate::app::channel) to notify the main
10//! thread that the window needs showing. An alternative to that is
11//! [`awake_callback`](crate::app::awake_callback)
12
13use crate::app::screen_size;
14use crate::enums::{Align, Color, Cursor, Damage, Event, Font, FrameType, LabelType, Mode, When};
15use crate::image::Image;
16use crate::prelude::*;
17use crate::utils::FlString;
18use crate::widget::Widget;
19use fltk_sys::window::*;
20#[cfg(feature = "rwh06")]
21use rwh06::*;
22use std::{
23    ffi::{CStr, CString},
24    mem,
25    ops::{Deref, DerefMut},
26    os::raw,
27};
28
29// Opaque raw window handle on 32-bit linux running on a Raspberry Pi
30#[cfg(all(
31    not(any(
32        target_os = "windows",
33        target_os = "macos",
34        target_os = "ios",
35        target_os = "android",
36        target_os = "emscripten",
37    )),
38    any(
39        target_arch = "arm",
40        target_arch = "mips",
41        target_arch = "powerpc",
42        target_arch = "sparc",
43        target_arch = "wasm32",
44        target_arch = "x86",
45    )
46))]
47type RawXlibHandle = u32;
48
49/// Opaque raw window handle (`*mut c_void` to `HWND` on Windows and `NSWindow` on macOS),
50/// `XID` (`u64`) raw window handle for X11, and `wl_suface *` for wayland
51#[cfg(all(
52    not(any(
53        target_os = "windows",
54        target_os = "macos",
55        target_os = "ios",
56        target_os = "android",
57        target_os = "emscripten"
58    )),
59    any(
60        target_arch = "aarch64",
61        target_arch = "loongarch64",
62        target_arch = "mips64",
63        target_arch = "powerpc64",
64        target_arch = "s390x",
65        target_arch = "sparc64",
66        target_arch = "x86_64",
67    )
68))]
69type RawXlibHandle = u64;
70
71/// Opaque raw window handle (`*mut c_void` to `HWND` on Windows and `NSWindow` on macOS),
72/// `XID` (`u64`) raw window handle for X11
73#[cfg(any(
74    target_os = "windows",
75    target_os = "macos",
76    target_os = "ios",
77    target_os = "android",
78    target_os = "emscripten",
79))]
80pub type RawHandle = *mut raw::c_void;
81
82/// Opaque raw window handle (`*mut c_void` to `HWND` on Windows and `NSWindow` on macOS),
83/// `XID` (`u64`) raw window handle for X11
84#[cfg(all(
85    not(any(
86        target_os = "windows",
87        target_os = "macos",
88        target_os = "ios",
89        target_os = "android",
90        target_os = "emscripten"
91    )),
92    feature = "no-wayland"
93))]
94pub type RawHandle = RawXlibHandle;
95
96/// Opaque raw window handle (`*mut c_void` to `HWND` on Windows and `NSWindow` on macOS),
97/// `XID` (`u64`) raw window handle for X11
98#[cfg(all(
99    not(any(
100        target_os = "windows",
101        target_os = "macos",
102        target_os = "ios",
103        target_os = "android",
104        target_os = "emscripten"
105    )),
106    not(feature = "no-wayland")
107))]
108pub type RawHandle = *mut std::os::raw::c_void;
109
110/// Creates a window widget
111pub type Window = DoubleWindow;
112
113/// Defines the window type
114#[repr(i32)]
115#[derive(Debug, Copy, Clone, PartialEq, Eq)]
116pub enum WindowType {
117    /// Single window
118    Single = 240,
119    /// Double window
120    Double = 241,
121}
122
123crate::macros::widget::impl_widget_type!(WindowType);
124
125macro_rules! impl_ppu {
126    ($name:ident) => {
127        impl $name {
128            /// Returns the pixels per unit/point
129            pub fn pixels_per_unit(&self) -> f32 {
130                #[allow(unused_mut)]
131                let mut factor = 1.0;
132                #[cfg(target_os = "macos")]
133                {
134                    unsafe extern "C" {
135                        pub fn cfltk_getScalingFactor(handle: *mut raw::c_void) -> f64;
136                    }
137                    let mac_version = unsafe { fltk_sys::fl::Fl_mac_os_version() };
138                    if mac_version >= 100700 {
139                        factor = unsafe { cfltk_getScalingFactor(self.raw_handle()) };
140                    }
141                }
142                let s = crate::app::screen_scale(self.screen_num());
143                s * factor as f32
144            }
145
146            /// Gets the window's width in pixels
147            pub fn pixel_w(&self) -> i32 {
148                (self.pixels_per_unit() as f64 * self.w() as f64) as i32
149            }
150
151            /// Gets the window's height in pixels
152            pub fn pixel_h(&self) -> i32 {
153                (self.pixels_per_unit() as f64 * self.h() as f64) as i32
154            }
155        }
156    };
157}
158
159macro_rules! impl_top_win {
160    ($name:ident) => {
161        impl $name {
162            /// Find an `Fl_Window` through a raw handle. The window must have been instantiated by the app.
163            /// `void *` to: (Windows: `HWND`, X11: `Xid` (`u64`), macOS: `NSWindow`)
164            /// # Safety
165            /// The data must be valid and is OS-dependent.
166            pub unsafe fn find_by_handle(handle: RawHandle) -> Option<impl WindowExt> {
167                unsafe {
168                    let ptr = Fl_Window_find_by_handle(handle as *const raw::c_void as *mut _);
169                    if ptr.is_null() {
170                        None
171                    } else {
172                        Some(Window::from_widget_ptr(
173                            ptr as *mut fltk_sys::widget::Fl_Widget,
174                        ))
175                    }
176                }
177            }
178
179            /// Use FLTK specific arguments for the application:
180            /// More info [here](https://www.fltk.org/doc-1.3/classFl.html#a1576b8c9ca3e900daaa5c36ca0e7ae48).
181            /// The options are:
182            /// - `-bg2 color`
183            /// - `-bg color`
184            /// - `-di[splay] host:n.n`
185            /// - `-dn[d]`
186            /// - `-fg color`
187            /// - `-g[eometry] WxH+X+Y`
188            /// - `-i[conic]`
189            /// - `-k[bd]`
190            /// - `-na[me] classname`
191            /// - `-nod[nd]`
192            /// - `-nok[bd]`
193            /// - `-not[ooltips]`
194            /// - `-s[cheme] scheme`
195            /// - `-ti[tle] windowtitle`
196            /// - `-to[oltips]`
197            pub fn show_with_env_args(&mut self) {
198                unsafe {
199                    let args: Vec<String> = std::env::args().collect();
200                    let len = args.len() as i32;
201                    let mut v: Vec<*mut raw::c_char> = vec![];
202                    for arg in args {
203                        let c = CString::safe_new(arg.as_str());
204                        v.push(c.into_raw() as *mut raw::c_char);
205                    }
206                    let mut v = mem::ManuallyDrop::new(v);
207                    Fl_Window_show_with_args(
208                        self.inner.widget() as *mut Fl_Window,
209                        len,
210                        v.as_mut_ptr(),
211                    )
212                }
213            }
214
215            /// Use FLTK specific arguments for the application:
216            /// More info [here](https://www.fltk.org/doc-1.3/classFl.html#a1576b8c9ca3e900daaa5c36ca0e7ae48).
217            /// The options are:
218            /// - `-bg2 color`
219            /// - `-bg color`
220            /// - `-di[splay] host:n.n`
221            /// - `-dn[d]`
222            /// - `-fg color`
223            /// - `-g[eometry] WxH+X+Y`
224            /// - `-i[conic]`
225            /// - `-k[bd]`
226            /// - `-na[me] classname`
227            /// - `-nod[nd]`
228            /// - `-nok[bd]`
229            /// - `-not[ooltips]`
230            /// - `-s[cheme] scheme`
231            /// - `-ti[tle] windowtitle`
232            /// - `-to[oltips]`
233            pub fn show_with_args(&mut self, args: &[&str]) {
234                unsafe {
235                    let mut temp = vec![""];
236                    temp.extend(args);
237                    let len = temp.len() as i32;
238                    let mut v: Vec<*mut raw::c_char> = vec![];
239                    for arg in temp {
240                        let c = CString::safe_new(arg);
241                        v.push(c.into_raw() as *mut raw::c_char);
242                    }
243                    let mut v = mem::ManuallyDrop::new(v);
244                    Fl_Window_show_with_args(
245                        self.inner.widget() as *mut Fl_Window,
246                        len,
247                        v.as_mut_ptr(),
248                    )
249                }
250            }
251
252            /// Set the window to be on top of other windows.
253            /// Must only be called after the window has been shown.
254            pub fn set_on_top(&mut self) {
255                assert!(self.raw_handle() as isize != 0);
256                #[cfg(target_os = "macos")]
257                {
258                    unsafe extern "C" {
259                        pub fn cfltk_setOnTop(handle: *mut raw::c_void);
260                    }
261                    unsafe {
262                        cfltk_setOnTop(self.raw_handle());
263                    }
264                }
265                #[cfg(target_os = "windows")]
266                {
267                    unsafe extern "system" {
268                        fn SetWindowPos(
269                            hwnd: *mut raw::c_void,
270                            insert_after: isize,
271                            x: i32,
272                            y: i32,
273                            cx: i32,
274                            cy: i32,
275                            flags: u32,
276                        ) -> bool;
277                    }
278                    const TOP_MOST: isize = -1;
279                    const SWP_NOSIZE: u32 = 1;
280                    const SWP_NOMOVE: u32 = 2;
281                    unsafe {
282                        SetWindowPos(
283                            self.raw_handle(),
284                            TOP_MOST,
285                            0,
286                            0,
287                            0,
288                            0,
289                            SWP_NOSIZE | SWP_NOMOVE,
290                        );
291                    }
292                }
293                #[cfg(not(any(
294                    target_os = "macos",
295                    target_os = "android",
296                    target_os = "windows",
297                    target_os = "emscripten"
298                )))]
299                {
300                    unsafe extern "C" {
301                        pub fn cfltk_setOnTop(handle: RawXlibHandle);
302                    }
303                    if !crate::app::using_wayland() {
304                        unsafe {
305                            cfltk_setOnTop(self.raw_handle() as RawXlibHandle);
306                        }
307                    }
308                }
309            }
310
311            /// Maximize the window
312            pub fn maximize(&mut self) {
313                unsafe { Fl_Window_maximize(self.inner.widget() as _) }
314            }
315
316            /// Unmaximize the window
317            pub fn un_maximize(&mut self) {
318                unsafe { Fl_Window_un_maximize(self.inner.widget() as _) }
319            }
320
321            /// Checks whether the window is maximized
322            pub fn maximize_active(&self) -> bool {
323                unsafe { Fl_Window_maximize_active(self.inner.widget() as _) != 0 }
324            }
325
326            /// Get the default `XA_WM_CLASS` property for all windows of your application
327            pub fn default_xclass() -> Option<String> {
328                unsafe {
329                    let ptr = Fl_Single_Window_default_xclass();
330                    if ptr.is_null() {
331                        None
332                    } else {
333                        Some(CStr::from_ptr(ptr).to_string_lossy().to_string())
334                    }
335                }
336            }
337
338            /// Set the default `XA_WM_CLASS` property for all windows of your application.
339            /// This should be called before showing with window
340            pub fn set_default_xclass(s: &str) {
341                let s = CString::safe_new(s);
342                unsafe { Fl_Single_Window_set_default_xclass(s.as_ptr()) }
343            }
344        }
345    };
346}
347
348/// Creates a single (buffered) window widget
349#[derive(Debug)]
350pub struct SingleWindow {
351    inner: crate::widget::WidgetTracker,
352    is_derived: bool,
353}
354
355crate::macros::widget::impl_widget_ext!(SingleWindow, Fl_Single_Window);
356crate::macros::widget::impl_widget_base!(SingleWindow, Fl_Single_Window);
357crate::macros::group::impl_group_ext!(SingleWindow, Fl_Single_Window);
358crate::macros::window::impl_window_ext!(SingleWindow, Fl_Single_Window);
359
360impl SingleWindow {
361    /// Creates a new window, with title as its window title if the window is decorated
362    pub fn new<'a, T: Into<Option<&'a str>>>(x: i32, y: i32, w: i32, h: i32, title: T) -> Self {
363        let temp = if let Some(title) = title.into() {
364            CString::safe_new(title).into_raw()
365        } else {
366            std::ptr::null_mut()
367        };
368        unsafe {
369            let widget_ptr = Fl_Single_Window_new(x, y, w, h, temp);
370            assert!(!widget_ptr.is_null());
371            assert!(crate::app::is_ui_thread());
372            let tracker = crate::widget::WidgetTracker::new(widget_ptr as _);
373            unsafe extern "C" fn shim(wid: *mut Fl_Widget, _data: *mut std::os::raw::c_void) {
374                unsafe {
375                    let user_data = Fl_Single_Window_user_data(wid as _);
376                    let draw_data = Fl_Single_Window_draw_data(wid as _);
377                    let handle_data = Fl_Single_Window_handle_data(wid as _);
378                    crate::app::add_timeout(0., move |h| {
379                        if !user_data.is_null() {
380                            let _x = Box::from_raw(user_data as *mut Box<dyn FnMut()>);
381                        }
382                        if !draw_data.is_null() {
383                            let _x = Box::from_raw(draw_data as *mut Box<dyn FnMut()>);
384                        }
385                        if !handle_data.is_null() {
386                            let _x = Box::from_raw(handle_data as *mut Box<dyn FnMut()>);
387                        }
388                        crate::app::remove_timeout(h);
389                    });
390                }
391            }
392            Fl_Single_Window_set_deletion_callback(widget_ptr, Some(shim), std::ptr::null_mut());
393            Self {
394                inner: tracker,
395                is_derived: true,
396            }
397        }
398    }
399}
400
401impl Default for SingleWindow {
402    fn default() -> Self {
403        assert!(crate::app::is_ui_thread());
404        let mut win = SingleWindow::new(0, 0, 0, 0, None);
405        win.free_position();
406        win
407    }
408}
409
410impl_top_win!(SingleWindow);
411impl_ppu!(SingleWindow);
412
413/// Creates a double (buffered) window widget
414#[derive(Debug)]
415pub struct DoubleWindow {
416    inner: crate::widget::WidgetTracker,
417    is_derived: bool,
418}
419
420crate::macros::widget::impl_widget_ext!(DoubleWindow, Fl_Double_Window);
421crate::macros::widget::impl_widget_base!(DoubleWindow, Fl_Double_Window);
422crate::macros::group::impl_group_ext!(DoubleWindow, Fl_Double_Window);
423crate::macros::window::impl_window_ext!(DoubleWindow, Fl_Double_Window);
424
425impl Default for DoubleWindow {
426    fn default() -> Self {
427        assert!(crate::app::is_ui_thread());
428        let mut win = DoubleWindow::new(0, 0, 0, 0, None);
429        win.free_position();
430        win
431    }
432}
433
434impl_top_win!(DoubleWindow);
435impl_ppu!(DoubleWindow);
436
437impl DoubleWindow {
438    /// Creates a new window, with title as its window title if the window is decorated
439    pub fn new<'a, T: Into<Option<&'a str>>>(x: i32, y: i32, w: i32, h: i32, title: T) -> Self {
440        let temp = if let Some(title) = title.into() {
441            CString::safe_new(title).into_raw()
442        } else {
443            std::ptr::null_mut()
444        };
445        unsafe {
446            let widget_ptr = Fl_Double_Window_new(x, y, w, h, temp);
447            assert!(!widget_ptr.is_null());
448            assert!(crate::app::is_ui_thread());
449            let tracker = crate::widget::WidgetTracker::new(widget_ptr as _);
450            unsafe extern "C" fn shim(wid: *mut Fl_Widget, _data: *mut std::os::raw::c_void) {
451                unsafe {
452                    let user_data = Fl_Double_Window_user_data(wid as _);
453                    let draw_data = Fl_Double_Window_draw_data(wid as _);
454                    let handle_data = Fl_Double_Window_handle_data(wid as _);
455                    crate::app::add_timeout(0., move |h| {
456                        if !user_data.is_null() {
457                            let _x = Box::from_raw(user_data as *mut Box<dyn FnMut()>);
458                        }
459                        if !draw_data.is_null() {
460                            let _x = Box::from_raw(draw_data as *mut Box<dyn FnMut()>);
461                        }
462                        if !handle_data.is_null() {
463                            let _x = Box::from_raw(handle_data as *mut Box<dyn FnMut()>);
464                        }
465                        crate::app::remove_timeout(h);
466                    });
467                }
468            }
469            Fl_Double_Window_set_deletion_callback(widget_ptr, Some(shim), std::ptr::null_mut());
470            Self {
471                inner: tracker,
472                is_derived: true,
473            }
474        }
475    }
476    /// Forces the window to be drawn, this window is also made current and calls `draw()`
477    pub fn flush(&mut self) {
478        unsafe { Fl_Double_Window_flush(self.inner.widget() as _) }
479    }
480
481    /// Show a window after it had been hidden. Works on Windows and X11 systems
482    pub fn platform_show(&self) {
483        #[allow(unused_unsafe)]
484        unsafe {
485            #[cfg(target_os = "windows")]
486            {
487                unsafe extern "system" {
488                    fn ShowWindow(hwnd: *mut raw::c_void, nCmdShow: raw::c_int) -> raw::c_int;
489                }
490                ShowWindow(self.raw_handle(), 9);
491            }
492            #[cfg(target_os = "macos")]
493            {
494                unsafe extern "C" {
495                    fn cfltk_winShow(xid: *mut raw::c_void);
496                }
497                cfltk_winShow(self.raw_handle());
498            }
499            #[cfg(not(any(
500                target_os = "macos",
501                target_os = "android",
502                target_os = "windows",
503                target_os = "emscripten"
504            )))]
505            {
506                if crate::app::using_wayland() {
507                    Fl_Double_Window_show(self.inner.widget() as _);
508                } else {
509                    unsafe extern "C" {
510                        fn cfltk_platform_show(proxy: *mut raw::c_void);
511                    }
512                    cfltk_platform_show(self.raw_handle() as *mut raw::c_void);
513                }
514            }
515        }
516    }
517
518    /// Hide a window using the platforms hide call. Works on Windows and X11 systems
519    pub fn platform_hide(&self) {
520        #[allow(unused_unsafe)]
521        unsafe {
522            #[cfg(target_os = "windows")]
523            {
524                unsafe extern "system" {
525                    fn ShowWindow(hwnd: *mut raw::c_void, nCmdShow: raw::c_int) -> raw::c_int;
526                }
527                ShowWindow(self.raw_handle(), 0);
528            }
529            #[cfg(target_os = "macos")]
530            {
531                unsafe extern "C" {
532                    fn cfltk_winHide(xid: *mut raw::c_void);
533                }
534                cfltk_winHide(self.raw_handle());
535            }
536            #[cfg(not(any(
537                target_os = "macos",
538                target_os = "android",
539                target_os = "windows",
540                target_os = "emscripten"
541            )))]
542            {
543                if crate::app::using_wayland() {
544                    Fl_Double_Window_hide(self.inner.widget() as _);
545                } else {
546                    unsafe extern "C" {
547                        fn cfltk_platform_hide(proxy: *mut raw::c_void);
548                    }
549                    cfltk_platform_hide(self.raw_handle() as *mut raw::c_void);
550                }
551            }
552        }
553    }
554}
555
556/// Creates a Menu window widget
557#[derive(Debug)]
558pub struct MenuWindow {
559    inner: crate::widget::WidgetTracker,
560    is_derived: bool,
561}
562
563crate::macros::widget::impl_widget_ext!(MenuWindow, Fl_Menu_Window);
564crate::macros::widget::impl_widget_base!(MenuWindow, Fl_Menu_Window);
565crate::macros::group::impl_group_ext!(MenuWindow, Fl_Menu_Window);
566crate::macros::window::impl_window_ext!(MenuWindow, Fl_Menu_Window);
567
568impl Default for MenuWindow {
569    fn default() -> Self {
570        assert!(crate::app::is_ui_thread());
571        let mut win = MenuWindow::new(0, 0, 0, 0, None);
572        win.free_position();
573        win
574    }
575}
576
577impl MenuWindow {
578    /// Creates a new window, with title as its window title if the window is decorated
579    pub fn new<'a, T: Into<Option<&'a str>>>(x: i32, y: i32, w: i32, h: i32, title: T) -> Self {
580        let temp = if let Some(title) = title.into() {
581            CString::safe_new(title).into_raw()
582        } else {
583            std::ptr::null_mut()
584        };
585        unsafe {
586            let widget_ptr = Fl_Menu_Window_new(x, y, w, h, temp);
587            assert!(!widget_ptr.is_null());
588            assert!(crate::app::is_ui_thread());
589            let tracker = crate::widget::WidgetTracker::new(widget_ptr as _);
590            unsafe extern "C" fn shim(wid: *mut Fl_Widget, _data: *mut std::os::raw::c_void) {
591                unsafe {
592                    let user_data = Fl_Menu_Window_user_data(wid as _);
593                    let draw_data = Fl_Menu_Window_draw_data(wid as _);
594                    let handle_data = Fl_Menu_Window_handle_data(wid as _);
595                    crate::app::add_timeout(0., move |h| {
596                        if !user_data.is_null() {
597                            let _x = Box::from_raw(user_data as *mut Box<dyn FnMut()>);
598                        }
599                        if !draw_data.is_null() {
600                            let _x = Box::from_raw(draw_data as *mut Box<dyn FnMut()>);
601                        }
602                        if !handle_data.is_null() {
603                            let _x = Box::from_raw(handle_data as *mut Box<dyn FnMut()>);
604                        }
605                        crate::app::remove_timeout(h);
606                    });
607                }
608            }
609            Fl_Menu_Window_set_deletion_callback(widget_ptr, Some(shim), std::ptr::null_mut());
610            Self {
611                inner: tracker,
612                is_derived: true,
613            }
614        }
615    }
616}
617
618/// Creates an overlay (buffered) window widget
619#[derive(Debug)]
620pub struct OverlayWindow {
621    inner: crate::widget::WidgetTracker,
622    is_derived: bool,
623}
624
625crate::macros::widget::impl_widget_ext!(OverlayWindow, Fl_Overlay_Window);
626crate::macros::widget::impl_widget_base!(OverlayWindow, Fl_Overlay_Window);
627crate::macros::group::impl_group_ext!(OverlayWindow, Fl_Overlay_Window);
628crate::macros::window::impl_window_ext!(OverlayWindow, Fl_Overlay_Window);
629
630impl Default for OverlayWindow {
631    fn default() -> Self {
632        assert!(crate::app::is_ui_thread());
633        OverlayWindow::new(0, 0, 0, 0, None)
634    }
635}
636
637impl_top_win!(OverlayWindow);
638impl_ppu!(OverlayWindow);
639
640impl OverlayWindow {
641    /// Creates a new window, with title as its window title if the window is decorated
642    pub fn new<'a, T: Into<Option<&'a str>>>(x: i32, y: i32, w: i32, h: i32, title: T) -> Self {
643        let temp = if let Some(title) = title.into() {
644            CString::safe_new(title).into_raw()
645        } else {
646            std::ptr::null_mut()
647        };
648        unsafe {
649            let widget_ptr = Fl_Overlay_Window_new(x, y, w, h, temp);
650            assert!(!widget_ptr.is_null());
651            assert!(crate::app::is_ui_thread());
652            let tracker = crate::widget::WidgetTracker::new(widget_ptr as _);
653            unsafe extern "C" fn shim(wid: *mut Fl_Widget, _data: *mut std::os::raw::c_void) {
654                unsafe {
655                    let user_data = Fl_Overlay_Window_user_data(wid as _);
656                    let draw_data = Fl_Overlay_Window_draw_data(wid as _);
657                    let handle_data = Fl_Overlay_Window_handle_data(wid as _);
658                    crate::app::add_timeout(0., move |h| {
659                        if !user_data.is_null() {
660                            let _x = Box::from_raw(user_data as *mut Box<dyn FnMut()>);
661                        }
662                        if !draw_data.is_null() {
663                            let _x = Box::from_raw(draw_data as *mut Box<dyn FnMut()>);
664                        }
665                        if !handle_data.is_null() {
666                            let _x = Box::from_raw(handle_data as *mut Box<dyn FnMut()>);
667                        }
668                        crate::app::remove_timeout(h);
669                    });
670                }
671            }
672            Fl_Overlay_Window_set_deletion_callback(widget_ptr, Some(shim), std::ptr::null_mut());
673            Self {
674                inner: tracker,
675                is_derived: true,
676            }
677        }
678    }
679    /// Forces the window to be drawn, this window is also made current and calls `draw()`
680    pub fn flush(&mut self) {
681        unsafe { Fl_Double_Window_flush(self.inner.widget() as _) }
682    }
683
684    /// Draw overlay
685    pub fn draw_overlay<F: FnMut(&mut Self) + 'static>(&mut self, cb: F) {
686        assert!(self.is_derived);
687        unsafe {
688            unsafe extern "C" fn shim(wid: *mut Fl_Widget, data: *mut raw::c_void) {
689                unsafe {
690                    let mut wid = OverlayWindow::from_widget_ptr(wid as *mut _);
691                    wid.assume_derived();
692                    let a: *mut Box<dyn FnMut(&mut OverlayWindow)> =
693                        data as *mut Box<dyn FnMut(&mut OverlayWindow)>;
694                    let f: &mut (dyn FnMut(&mut OverlayWindow)) = &mut **a;
695                    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&mut wid)));
696                }
697            }
698            let mut _old_data = None;
699            if self.is_derived {
700                _old_data = self.draw_data();
701            }
702            let a: *mut Box<dyn FnMut(&mut Self)> = Box::into_raw(Box::new(Box::new(cb)));
703            let data: *mut raw::c_void = a as *mut raw::c_void;
704            let callback: custom_draw_callback = Some(shim);
705            Fl_Overlay_Window_draw_overlay(self.inner.widget() as _, callback, data);
706        }
707    }
708
709    /// Redraw overlay
710    pub fn redraw_overlay(&self) {
711        unsafe { Fl_Overlay_Window_redraw_overlay(self.inner.widget() as _) }
712    }
713
714    /// Returns whether the overlay window can do hardware backed overlay
715    pub fn can_do_overlay(&self) -> bool {
716        unsafe { Fl_Overlay_Window_can_do_overlay(self.inner.widget() as _) != 0 }
717    }
718}
719
720#[cfg(feature = "enable-glwindow")]
721/// A wrapper around a raw OpenGL context
722pub struct GlContext(*mut raw::c_void);
723
724/// Creates a OpenGL Glut window widget
725#[cfg(feature = "enable-glwindow")]
726#[derive(Debug)]
727pub struct GlutWindow {
728    inner: crate::widget::WidgetTracker,
729    is_derived: bool,
730}
731
732#[cfg(feature = "enable-glwindow")]
733crate::macros::widget::impl_widget_ext!(GlutWindow, Fl_Glut_Window);
734#[cfg(feature = "enable-glwindow")]
735crate::macros::widget::impl_widget_base!(GlutWindow, Fl_Glut_Window);
736#[cfg(feature = "enable-glwindow")]
737crate::macros::group::impl_group_ext!(GlutWindow, Fl_Glut_Window);
738#[cfg(feature = "enable-glwindow")]
739crate::macros::window::impl_window_ext!(GlutWindow, Fl_Glut_Window);
740
741#[cfg(feature = "enable-glwindow")]
742impl_top_win!(GlutWindow);
743
744#[cfg(feature = "enable-glwindow")]
745impl Default for GlutWindow {
746    fn default() -> GlutWindow {
747        assert!(crate::app::is_ui_thread());
748        let mut win = GlutWindow::new(0, 0, 0, 0, None);
749        win.free_position();
750        win
751    }
752}
753
754#[cfg(feature = "enable-glwindow")]
755impl GlutWindow {
756    /// Creates a new window, with title as its window title if the window is decorated
757    pub fn new<'a, T: Into<Option<&'a str>>>(x: i32, y: i32, w: i32, h: i32, title: T) -> Self {
758        let temp = if let Some(title) = title.into() {
759            CString::safe_new(title).into_raw()
760        } else {
761            std::ptr::null_mut()
762        };
763        unsafe {
764            let widget_ptr = Fl_Glut_Window_new(x, y, w, h, temp);
765            assert!(!widget_ptr.is_null());
766            assert!(crate::app::is_ui_thread());
767            let tracker = crate::widget::WidgetTracker::new(widget_ptr as _);
768            unsafe extern "C" fn shim(wid: *mut Fl_Widget, _data: *mut std::os::raw::c_void) {
769                unsafe {
770                    let user_data = Fl_Glut_Window_user_data(wid as _);
771                    let draw_data = Fl_Glut_Window_draw_data(wid as _);
772                    let handle_data = Fl_Glut_Window_handle_data(wid as _);
773                    crate::app::add_timeout(0., move |h| {
774                        if !user_data.is_null() {
775                            let _x = Box::from_raw(user_data as *mut Box<dyn FnMut()>);
776                        }
777                        if !draw_data.is_null() {
778                            let _x = Box::from_raw(draw_data as *mut Box<dyn FnMut()>);
779                        }
780                        if !handle_data.is_null() {
781                            let _x = Box::from_raw(handle_data as *mut Box<dyn FnMut()>);
782                        }
783                        crate::app::remove_timeout(h);
784                    });
785                }
786            }
787            Fl_Glut_Window_set_deletion_callback(widget_ptr, Some(shim), std::ptr::null_mut());
788            Self {
789                inner: tracker,
790                is_derived: true,
791            }
792        }
793    }
794
795    /// Gets an opengl function address
796    pub fn get_proc_address(&self, s: &str) -> *const raw::c_void {
797        unsafe extern "C" {
798            pub fn get_proc(name: *const raw::c_char) -> *mut raw::c_void;
799        }
800        let s = CString::safe_new(s);
801        let ret = unsafe { get_proc(s.as_ptr() as _) };
802        if !ret.is_null() {
803            ret as *const _
804        } else {
805            unsafe {
806                Fl_Glut_Window_get_proc_address(self.inner.widget() as _, s.as_ptr()) as *const _
807            }
808        }
809    }
810
811    /// Forces the window to be drawn, this window is also made current and calls draw()
812    pub fn flush(&mut self) {
813        unsafe { Fl_Glut_Window_flush(self.inner.widget() as _) }
814    }
815
816    /// Returns whether the OpeGL context is still valid
817    pub fn valid(&self) -> bool {
818        unsafe { Fl_Glut_Window_valid(self.inner.widget() as _) != 0 }
819    }
820
821    /// Mark the OpeGL context as still valid
822    pub fn set_valid(&mut self, v: bool) {
823        unsafe { Fl_Glut_Window_set_valid(self.inner.widget() as _, v as raw::c_char) }
824    }
825
826    /// Returns whether the context is valid upon creation
827    pub fn context_valid(&self) -> bool {
828        unsafe { Fl_Glut_Window_context_valid(self.inner.widget() as _) != 0 }
829    }
830
831    /// Mark the context as valid upon creation
832    pub fn set_context_valid(&mut self, v: bool) {
833        unsafe { Fl_Glut_Window_set_context_valid(self.inner.widget() as _, v as raw::c_char) }
834    }
835
836    /// Returns the GlContext
837    pub fn context(&self) -> Option<GlContext> {
838        unsafe {
839            let ctx = Fl_Glut_Window_context(self.inner.widget() as _);
840            if ctx.is_null() {
841                None
842            } else {
843                Some(GlContext(ctx))
844            }
845        }
846    }
847
848    /// Sets the GlContext
849    pub fn set_context(&mut self, ctx: GlContext, destroy_flag: bool) {
850        assert!(!ctx.0.is_null());
851        unsafe {
852            Fl_Glut_Window_set_context(self.inner.widget() as _, ctx.0, i32::from(destroy_flag))
853        }
854    }
855
856    /// Swaps the back and front buffers
857    pub fn swap_buffers(&mut self) {
858        unsafe { Fl_Glut_Window_swap_buffers(self.inner.widget() as _) }
859    }
860
861    /// Gets the swap interval
862    pub fn swap_interval(&self) -> i32 {
863        unsafe { Fl_Glut_Window_swap_interval(self.inner.widget() as _) }
864    }
865
866    /// Sets the swap interval
867    pub fn set_swap_interval(&mut self, frames: i32) {
868        unsafe { Fl_Glut_Window_set_swap_interval(self.inner.widget() as _, frames) }
869    }
870
871    /// Sets the projection so 0,0 is in the lower left of the window
872    /// and each pixel is 1 unit wide/tall.
873    pub fn ortho(&mut self) {
874        unsafe { Fl_Glut_Window_ortho(self.inner.widget() as _) }
875    }
876
877    /// Returns whether the GlutWindow can do overlay
878    pub fn can_do_overlay(&self) -> bool {
879        unsafe { Fl_Glut_Window_can_do_overlay(self.inner.widget() as _) != 0 }
880    }
881
882    /// Redraws the overlay
883    pub fn redraw_overlay(&mut self) {
884        unsafe { Fl_Glut_Window_redraw_overlay(self.inner.widget() as _) }
885    }
886
887    /// Hides the overlay
888    pub fn hide_overlay(&mut self) {
889        unsafe { Fl_Glut_Window_hide_overlay(self.inner.widget() as _) }
890    }
891
892    /// Makes the overlay current
893    pub fn make_overlay_current(&mut self) {
894        unsafe { Fl_Glut_Window_make_overlay_current(self.inner.widget() as _) }
895    }
896
897    /// Returns the pixels per unit/point
898    pub fn pixels_per_unit(&self) -> f32 {
899        unsafe { Fl_Glut_Window_pixels_per_unit(self.inner.widget() as _) }
900    }
901
902    /// Gets the window's width in pixels
903    pub fn pixel_w(&self) -> i32 {
904        unsafe { Fl_Glut_Window_pixel_w(self.inner.widget() as _) }
905    }
906
907    /// Gets the window's height in pixels
908    pub fn pixel_h(&self) -> i32 {
909        unsafe { Fl_Glut_Window_pixel_h(self.inner.widget() as _) }
910    }
911
912    /// Get the Mode of the GlutWindow
913    pub fn mode(&self) -> Mode {
914        unsafe { mem::transmute(Fl_Glut_Window_mode(self.inner.widget() as _)) }
915    }
916
917    /// Set the Mode of the GlutWindow
918    pub fn set_mode(&mut self, mode: Mode) {
919        unsafe {
920            Fl_Glut_Window_set_mode(self.inner.widget() as _, mode.bits());
921        }
922    }
923}
924
925#[cfg(feature = "enable-glwindow")]
926/// Alias GlutWindow as GlWindow
927pub type GlWindow = GlutWindow;
928
929/// Creates a OpenGL Glut window widget
930#[cfg(feature = "enable-glwindow")]
931#[derive(Debug)]
932pub struct GlWidgetWindow {
933    inner: crate::widget::WidgetTracker,
934    is_derived: bool,
935}
936
937#[cfg(feature = "enable-glwindow")]
938crate::macros::widget::impl_widget_ext!(GlWidgetWindow, Fl_Gl_Window);
939#[cfg(feature = "enable-glwindow")]
940crate::macros::widget::impl_widget_base!(GlWidgetWindow, Fl_Gl_Window);
941#[cfg(feature = "enable-glwindow")]
942crate::macros::group::impl_group_ext!(GlWidgetWindow, Fl_Gl_Window);
943#[cfg(feature = "enable-glwindow")]
944crate::macros::window::impl_window_ext!(GlWidgetWindow, Fl_Gl_Window);
945
946#[cfg(feature = "enable-glwindow")]
947impl_top_win!(GlWidgetWindow);
948
949#[cfg(feature = "enable-glwindow")]
950impl Default for GlWidgetWindow {
951    fn default() -> GlWidgetWindow {
952        assert!(crate::app::is_ui_thread());
953        let mut win = GlWidgetWindow::new(0, 0, 0, 0, None);
954        win.free_position();
955        win.set_frame(FrameType::FlatBox);
956        win.begin();
957        win
958    }
959}
960
961#[cfg(feature = "enable-glwindow")]
962impl GlWidgetWindow {
963    /// Creates a new window, with title as its window title if the window is decorated
964    pub fn new<'a, T: Into<Option<&'a str>>>(x: i32, y: i32, w: i32, h: i32, title: T) -> Self {
965        let temp = if let Some(title) = title.into() {
966            CString::safe_new(title).into_raw()
967        } else {
968            std::ptr::null_mut()
969        };
970        unsafe {
971            let widget_ptr = Fl_Gl_Window_new(x, y, w, h, temp);
972            assert!(!widget_ptr.is_null());
973            assert!(crate::app::is_ui_thread());
974            let tracker = crate::widget::WidgetTracker::new(widget_ptr as _);
975            unsafe extern "C" fn shim(wid: *mut Fl_Widget, _data: *mut std::os::raw::c_void) {
976                unsafe {
977                    let user_data = Fl_Gl_Window_user_data(wid as _);
978                    let draw_data = Fl_Gl_Window_draw_data(wid as _);
979                    let handle_data = Fl_Gl_Window_handle_data(wid as _);
980                    crate::app::add_timeout(0., move |h| {
981                        if !user_data.is_null() {
982                            let _x = Box::from_raw(user_data as *mut Box<dyn FnMut()>);
983                        }
984                        if !draw_data.is_null() {
985                            let _x = Box::from_raw(draw_data as *mut Box<dyn FnMut()>);
986                        }
987                        if !handle_data.is_null() {
988                            let _x = Box::from_raw(handle_data as *mut Box<dyn FnMut()>);
989                        }
990                        crate::app::remove_timeout(h);
991                    });
992                }
993            }
994            Fl_Gl_Window_set_deletion_callback(widget_ptr, Some(shim), std::ptr::null_mut());
995            Self {
996                inner: tracker,
997                is_derived: true,
998            }
999        }
1000    }
1001
1002    /// Gets an opengl function address
1003    pub fn get_proc_address(&self, s: &str) -> *const raw::c_void {
1004        unsafe extern "C" {
1005            pub fn get_proc(name: *const raw::c_char) -> *mut raw::c_void;
1006        }
1007        let s = CString::safe_new(s);
1008        let ret = unsafe { get_proc(s.as_ptr() as _) };
1009        if !ret.is_null() {
1010            ret as *const _
1011        } else {
1012            unsafe {
1013                Fl_Gl_Window_get_proc_address(self.inner.widget() as _, s.as_ptr()) as *const _
1014            }
1015        }
1016    }
1017
1018    /// Forces the window to be drawn, this window is also made current and calls draw()
1019    pub fn flush(&mut self) {
1020        unsafe { Fl_Gl_Window_flush(self.inner.widget() as _) }
1021    }
1022
1023    /// Returns whether the OpeGL context is still valid
1024    pub fn valid(&self) -> bool {
1025        unsafe { Fl_Gl_Window_valid(self.inner.widget() as _) != 0 }
1026    }
1027
1028    /// Mark the OpeGL context as still valid
1029    pub fn set_valid(&mut self, v: bool) {
1030        unsafe { Fl_Gl_Window_set_valid(self.inner.widget() as _, v as raw::c_char) }
1031    }
1032
1033    /// Returns whether the context is valid upon creation
1034    pub fn context_valid(&self) -> bool {
1035        unsafe { Fl_Gl_Window_context_valid(self.inner.widget() as _) != 0 }
1036    }
1037
1038    /// Mark the context as valid upon creation
1039    pub fn set_context_valid(&mut self, v: bool) {
1040        unsafe { Fl_Gl_Window_set_context_valid(self.inner.widget() as _, v as raw::c_char) }
1041    }
1042
1043    /// Returns the GlContext
1044    pub fn context(&self) -> Option<GlContext> {
1045        unsafe {
1046            let ctx = Fl_Gl_Window_context(self.inner.widget() as _);
1047            if ctx.is_null() {
1048                None
1049            } else {
1050                Some(GlContext(ctx))
1051            }
1052        }
1053    }
1054
1055    /// Sets the GlContext
1056    pub fn set_context(&mut self, ctx: GlContext, destroy_flag: bool) {
1057        assert!(!ctx.0.is_null());
1058        unsafe {
1059            Fl_Gl_Window_set_context(self.inner.widget() as _, ctx.0, i32::from(destroy_flag))
1060        }
1061    }
1062
1063    /// Swaps the back and front buffers
1064    pub fn swap_buffers(&mut self) {
1065        unsafe { Fl_Gl_Window_swap_buffers(self.inner.widget() as _) }
1066    }
1067
1068    /// Gets the swap interval
1069    pub fn swap_interval(&self) -> i32 {
1070        unsafe { Fl_Gl_Window_swap_interval(self.inner.widget() as _) }
1071    }
1072
1073    /// Sets the swap interval
1074    pub fn set_swap_interval(&mut self, frames: i32) {
1075        unsafe { Fl_Gl_Window_set_swap_interval(self.inner.widget() as _, frames) }
1076    }
1077
1078    /// Sets the projection so 0,0 is in the lower left of the window
1079    /// and each pixel is 1 unit wide/tall.
1080    pub fn ortho(&mut self) {
1081        unsafe { Fl_Gl_Window_ortho(self.inner.widget() as _) }
1082    }
1083
1084    /// Returns whether the GlutWindow can do overlay
1085    pub fn can_do_overlay(&self) -> bool {
1086        unsafe { Fl_Gl_Window_can_do_overlay(self.inner.widget() as _) != 0 }
1087    }
1088
1089    /// Redraws the overlay
1090    pub fn redraw_overlay(&mut self) {
1091        unsafe { Fl_Gl_Window_redraw_overlay(self.inner.widget() as _) }
1092    }
1093
1094    /// Hides the overlay
1095    pub fn hide_overlay(&mut self) {
1096        unsafe { Fl_Gl_Window_hide_overlay(self.inner.widget() as _) }
1097    }
1098
1099    /// Makes the overlay current
1100    pub fn make_overlay_current(&mut self) {
1101        unsafe { Fl_Gl_Window_make_overlay_current(self.inner.widget() as _) }
1102    }
1103
1104    /// Returns the pixels per unit/point
1105    pub fn pixels_per_unit(&self) -> f32 {
1106        unsafe { Fl_Gl_Window_pixels_per_unit(self.inner.widget() as _) }
1107    }
1108
1109    /// Gets the window's width in pixels
1110    pub fn pixel_w(&self) -> i32 {
1111        unsafe { Fl_Gl_Window_pixel_w(self.inner.widget() as _) }
1112    }
1113
1114    /// Gets the window's height in pixels
1115    pub fn pixel_h(&self) -> i32 {
1116        unsafe { Fl_Gl_Window_pixel_h(self.inner.widget() as _) }
1117    }
1118
1119    /// Get the Mode of the GlutWindow
1120    pub fn mode(&self) -> Mode {
1121        unsafe { mem::transmute(Fl_Gl_Window_mode(self.inner.widget() as _)) }
1122    }
1123
1124    /// Set the Mode of the GlutWindow
1125    pub fn set_mode(&mut self, mode: Mode) {
1126        unsafe {
1127            Fl_Gl_Window_set_mode(self.inner.widget() as _, mode.bits());
1128        }
1129    }
1130}