Skip to main content

gdk/
window.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::Cursor;
4use crate::EventMask;
5use crate::Visual;
6use crate::Window;
7use cairo::{self, Surface};
8use glib::object::IsA;
9use glib::translate::*;
10use libc::{c_char, c_int};
11use std::ptr;
12
13use crate::{WindowType, WindowTypeHint, WindowWindowClass};
14
15pub struct WindowAttr {
16    pub title: Option<String>,
17    pub event_mask: EventMask,
18    pub x: Option<i32>,
19    pub y: Option<i32>,
20    pub width: i32,
21    pub height: i32,
22    pub wclass: WindowWindowClass,
23    pub visual: Option<Visual>,
24    pub window_type: WindowType,
25    pub cursor: Option<Cursor>,
26    pub override_redirect: bool,
27    pub type_hint: Option<WindowTypeHint>,
28}
29
30impl Default for WindowAttr {
31    fn default() -> Self {
32        skip_assert_initialized!();
33        Self {
34            title: None,
35            event_mask: EventMask::empty(),
36            x: None,
37            y: None,
38            width: 400,
39            height: 300,
40            wclass: WindowWindowClass::InputOutput,
41            visual: None,
42            window_type: WindowType::Toplevel,
43            cursor: None,
44            override_redirect: false,
45            type_hint: None,
46        }
47    }
48}
49
50impl WindowAttr {
51    #[doc(alias = "get_mask")]
52    fn mask(&self) -> u32 {
53        let mut mask: ffi::GdkWindowAttributesType = 0;
54        if self.title.is_some() {
55            mask |= ffi::GDK_WA_TITLE;
56        }
57        if self.x.is_some() {
58            mask |= ffi::GDK_WA_X;
59        }
60        if self.y.is_some() {
61            mask |= ffi::GDK_WA_Y;
62        }
63        if self.cursor.is_some() {
64            mask |= ffi::GDK_WA_CURSOR;
65        }
66        if self.visual.is_some() {
67            mask |= ffi::GDK_WA_VISUAL;
68        }
69        if self.override_redirect {
70            mask |= ffi::GDK_WA_NOREDIR;
71        }
72        if self.type_hint.is_some() {
73            mask |= ffi::GDK_WA_TYPE_HINT;
74        }
75        mask
76    }
77}
78
79#[allow(clippy::type_complexity)]
80impl<'a> ToGlibPtr<'a, *mut ffi::GdkWindowAttr> for WindowAttr {
81    type Storage = (
82        Box<ffi::GdkWindowAttr>,
83        Stash<'a, *mut ffi::GdkVisual, Option<Visual>>,
84        Stash<'a, *mut ffi::GdkCursor, Option<Cursor>>,
85        Stash<'a, *const c_char, Option<String>>,
86    );
87
88    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::GdkWindowAttr, Self> {
89        let title = self.title.to_glib_none();
90        let visual = self.visual.to_glib_none();
91        let cursor = self.cursor.to_glib_none();
92
93        let mut attrs = Box::new(ffi::GdkWindowAttr {
94            title: title.0 as *mut c_char,
95            event_mask: self.event_mask.bits() as i32,
96            x: self.x.unwrap_or(0),
97            y: self.y.unwrap_or(0),
98            width: self.width,
99            height: self.height,
100            wclass: self.wclass.into_glib(),
101            visual: visual.0,
102            window_type: self.window_type.into_glib(),
103            cursor: cursor.0,
104            wmclass_name: ptr::null_mut(),
105            wmclass_class: ptr::null_mut(),
106            override_redirect: self.override_redirect.into_glib(),
107            type_hint: self.type_hint.unwrap_or(WindowTypeHint::Normal).into_glib(),
108        });
109
110        Stash(&mut *attrs, (attrs, visual, cursor, title))
111    }
112}
113
114impl Window {
115    /// Creates a new [`Window`][crate::Window] using the attributes from
116    /// `attributes`. See `GdkWindowAttr` and `GdkWindowAttributesType` for
117    /// more details. Note: to use this on displays other than the default
118    /// display, `parent` must be specified.
119    /// ## `parent`
120    /// a [`Window`][crate::Window], or [`None`] to create the window as a child of
121    ///  the default root window for the default display.
122    /// ## `attributes`
123    /// attributes of the new window
124    /// ## `attributes_mask`
125    /// mask indicating which
126    ///  fields in `attributes` are valid
127    ///
128    /// # Returns
129    ///
130    /// the new [`Window`][crate::Window]
131    #[doc(alias = "gdk_window_new")]
132    pub fn new(parent: Option<&Window>, attributes: &WindowAttr) -> Window {
133        assert_initialized_main_thread!();
134        unsafe {
135            from_glib_full(ffi::gdk_window_new(
136                parent.to_glib_none().0,
137                attributes.to_glib_none().0,
138                attributes.mask() as c_int,
139            ))
140        }
141    }
142
143    /// Create a new surface that is as compatible as possible with the
144    /// given `self`. For example the new surface will have the same
145    /// fallback resolution and font options as `self`. Generally, the new
146    /// surface will also use the same backend as `self`, unless that is
147    /// not possible for some reason. The type of the returned surface may
148    /// be examined with `cairo_surface_get_type()`.
149    ///
150    /// Initially the surface contents are all 0 (transparent if contents
151    /// have transparency, black otherwise.)
152    /// ## `content`
153    /// the content for the new surface
154    /// ## `width`
155    /// width of the new surface
156    /// ## `height`
157    /// height of the new surface
158    ///
159    /// # Returns
160    ///
161    /// a pointer to the newly allocated surface. The caller
162    /// owns the surface and should call `cairo_surface_destroy()` when done
163    /// with it.
164    ///
165    /// This function always returns a valid pointer, but it will return a
166    /// pointer to a “nil” surface if `other` is already in an error state
167    /// or any other error occurs.
168    #[doc(alias = "gdk_window_create_similar_surface")]
169    pub fn create_similar_surface(
170        &self,
171        content: cairo::Content,
172        width: i32,
173        height: i32,
174    ) -> Option<Surface> {
175        unsafe {
176            from_glib_full(ffi::gdk_window_create_similar_surface(
177                self.to_glib_none().0,
178                content.into(),
179                width,
180                height,
181            ))
182        }
183    }
184
185    /// Create a new image surface that is efficient to draw on the
186    /// given `self`.
187    ///
188    /// Initially the surface contents are all 0 (transparent if contents
189    /// have transparency, black otherwise.)
190    ///
191    /// The `width` and `height` of the new surface are not affected by
192    /// the scaling factor of the `self`, or by the `scale` argument; they
193    /// are the size of the surface in device pixels. If you wish to create
194    /// an image surface capable of holding the contents of `self` you can
195    /// use:
196    ///
197    ///
198    ///
199    /// **⚠️ The following code is in C ⚠️**
200    ///
201    /// ```C
202    ///   int scale = gdk_window_get_scale_factor (window);
203    ///   int width = gdk_window_get_width (window) * scale;
204    ///   int height = gdk_window_get_height (window) * scale;
205    ///
206    ///   // format is set elsewhere
207    ///   cairo_surface_t *surface =
208    ///     gdk_window_create_similar_image_surface (window,
209    ///                                              format,
210    ///                                              width, height,
211    ///                                              scale);
212    /// ```
213    ///
214    /// Note that unlike `cairo_surface_create_similar_image()`, the new
215    /// surface's device scale is set to `scale`, or to the scale factor of
216    /// `self` if `scale` is 0.
217    /// ## `format`
218    /// the format for the new surface
219    /// ## `width`
220    /// width of the new surface
221    /// ## `height`
222    /// height of the new surface
223    /// ## `scale`
224    /// the scale of the new surface, or 0 to use same as `self`
225    ///
226    /// # Returns
227    ///
228    /// a pointer to the newly allocated surface. The caller
229    /// owns the surface and should call `cairo_surface_destroy()` when done
230    /// with it.
231    ///
232    /// This function always returns a valid pointer, but it will return a
233    /// pointer to a “nil” surface if `other` is already in an error state
234    /// or any other error occurs.
235    #[doc(alias = "gdk_window_create_similar_image_surface")]
236    pub fn create_similar_image_surface(
237        &self,
238        format: cairo::Format,
239        width: i32,
240        height: i32,
241        scale: i32,
242    ) -> Option<cairo::Surface> {
243        unsafe {
244            from_glib_full(ffi::gdk_window_create_similar_image_surface(
245                self.to_glib_none().0,
246                format.into(),
247                width,
248                height,
249                scale,
250            ))
251        }
252    }
253}
254
255mod sealed {
256    pub trait Sealed {}
257    impl<T: glib::IsA<crate::Window>> Sealed for T {}
258}
259
260pub trait WindowExtManual: IsA<Window> + sealed::Sealed + 'static {
261    #[doc(alias = "gdk_window_set_user_data")]
262    unsafe fn set_user_data<T>(&self, user_data: &mut T) {
263        ffi::gdk_window_set_user_data(
264            self.as_ref().to_glib_none().0,
265            user_data as *mut T as *mut _,
266        )
267    }
268
269    #[allow(clippy::mut_from_ref)]
270    #[doc(alias = "gdk_window_get_user_data")]
271    #[doc(alias = "get_user_data")]
272    unsafe fn user_data<T>(&self) -> &mut T {
273        let mut pointer = ::std::ptr::null_mut();
274        ffi::gdk_window_get_user_data(self.as_ref().to_glib_none().0, &mut pointer);
275        &mut *(pointer as *mut T)
276    }
277
278    #[doc(alias = "gdk_get_default_root_window")]
279    #[doc(alias = "get_default_root_window")]
280    fn default_root_window() -> Window {
281        assert_initialized_main_thread!();
282        unsafe { from_glib_none(ffi::gdk_get_default_root_window()) }
283    }
284
285    #[doc(alias = "gdk_offscreen_window_set_embedder")]
286    fn offscreen_window_set_embedder(&self, embedder: &Window) {
287        unsafe {
288            ffi::gdk_offscreen_window_set_embedder(
289                self.as_ref().to_glib_none().0,
290                embedder.to_glib_none().0,
291            )
292        }
293    }
294
295    #[doc(alias = "gdk_offscreen_window_get_embedder")]
296    fn offscreen_window_get_embedder(&self) -> Option<Window> {
297        unsafe {
298            from_glib_none(ffi::gdk_offscreen_window_get_embedder(
299                self.as_ref().to_glib_none().0,
300            ))
301        }
302    }
303
304    #[doc(alias = "gdk_offscreen_window_get_surface")]
305    fn offscreen_window_get_surface(&self) -> Option<Surface> {
306        skip_assert_initialized!();
307        unsafe {
308            from_glib_none(ffi::gdk_offscreen_window_get_surface(
309                self.as_ref().to_glib_none().0,
310            ))
311        }
312    }
313
314    #[doc(alias = "gdk_pixbuf_get_from_window")]
315    #[doc(alias = "get_pixbuf")]
316    fn pixbuf(
317        &self,
318        src_x: i32,
319        src_y: i32,
320        width: i32,
321        height: i32,
322    ) -> Option<gdk_pixbuf::Pixbuf> {
323        skip_assert_initialized!();
324        unsafe {
325            from_glib_full(ffi::gdk_pixbuf_get_from_window(
326                self.as_ref().to_glib_none().0,
327                src_x,
328                src_y,
329                width,
330                height,
331            ))
332        }
333    }
334
335    #[doc(alias = "gdk_window_get_background_pattern")]
336    #[doc(alias = "get_background_pattern")]
337    fn background_pattern(&self) -> Option<cairo::Pattern> {
338        unsafe {
339            let ret = ffi::gdk_window_get_background_pattern(self.as_ref().to_glib_none().0);
340            if ret.is_null() {
341                None
342            } else {
343                Some(cairo::Pattern::from_raw_none(ret))
344            }
345        }
346    }
347
348    #[doc(alias = "gdk_window_set_background_pattern")]
349    fn set_background_pattern(&self, pattern: Option<&cairo::Pattern>) {
350        unsafe {
351            let ptr = if let Some(pattern) = pattern {
352                pattern.to_raw_none()
353            } else {
354                ::std::ptr::null_mut()
355            };
356            ffi::gdk_window_set_background_pattern(self.as_ref().to_glib_none().0, ptr);
357        }
358    }
359}
360
361impl<O: IsA<Window>> WindowExtManual for O {}