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::EventMask;
4use crate::Visual;
5use crate::Window;
6use crate::{Cursor, ffi};
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
255pub trait WindowExtManual: IsA<Window> + 'static {
256    #[doc(alias = "gdk_window_set_user_data")]
257    unsafe fn set_user_data<T>(&self, user_data: &mut T) {
258        unsafe {
259            ffi::gdk_window_set_user_data(
260                self.as_ref().to_glib_none().0,
261                user_data as *mut T as *mut _,
262            )
263        }
264    }
265
266    #[allow(clippy::mut_from_ref)]
267    #[doc(alias = "gdk_window_get_user_data")]
268    #[doc(alias = "get_user_data")]
269    unsafe fn user_data<T>(&self) -> &mut T {
270        unsafe {
271            let mut pointer = ::std::ptr::null_mut();
272            ffi::gdk_window_get_user_data(self.as_ref().to_glib_none().0, &mut pointer);
273            &mut *(pointer as *mut T)
274        }
275    }
276
277    #[doc(alias = "gdk_get_default_root_window")]
278    #[doc(alias = "get_default_root_window")]
279    fn default_root_window() -> Window {
280        assert_initialized_main_thread!();
281        unsafe { from_glib_none(ffi::gdk_get_default_root_window()) }
282    }
283
284    #[doc(alias = "gdk_offscreen_window_set_embedder")]
285    fn offscreen_window_set_embedder(&self, embedder: &Window) {
286        unsafe {
287            ffi::gdk_offscreen_window_set_embedder(
288                self.as_ref().to_glib_none().0,
289                embedder.to_glib_none().0,
290            )
291        }
292    }
293
294    #[doc(alias = "gdk_offscreen_window_get_embedder")]
295    fn offscreen_window_get_embedder(&self) -> Option<Window> {
296        unsafe {
297            from_glib_none(ffi::gdk_offscreen_window_get_embedder(
298                self.as_ref().to_glib_none().0,
299            ))
300        }
301    }
302
303    #[doc(alias = "gdk_offscreen_window_get_surface")]
304    fn offscreen_window_get_surface(&self) -> Option<Surface> {
305        skip_assert_initialized!();
306        unsafe {
307            from_glib_none(ffi::gdk_offscreen_window_get_surface(
308                self.as_ref().to_glib_none().0,
309            ))
310        }
311    }
312
313    #[doc(alias = "gdk_pixbuf_get_from_window")]
314    #[doc(alias = "get_pixbuf")]
315    fn pixbuf(
316        &self,
317        src_x: i32,
318        src_y: i32,
319        width: i32,
320        height: i32,
321    ) -> Option<gdk_pixbuf::Pixbuf> {
322        skip_assert_initialized!();
323        unsafe {
324            from_glib_full(ffi::gdk_pixbuf_get_from_window(
325                self.as_ref().to_glib_none().0,
326                src_x,
327                src_y,
328                width,
329                height,
330            ))
331        }
332    }
333
334    #[doc(alias = "gdk_window_get_background_pattern")]
335    #[doc(alias = "get_background_pattern")]
336    fn background_pattern(&self) -> Option<cairo::Pattern> {
337        unsafe {
338            let ret = ffi::gdk_window_get_background_pattern(self.as_ref().to_glib_none().0);
339            if ret.is_null() {
340                None
341            } else {
342                Some(cairo::Pattern::from_raw_none(ret))
343            }
344        }
345    }
346
347    #[doc(alias = "gdk_window_set_background_pattern")]
348    fn set_background_pattern(&self, pattern: Option<&cairo::Pattern>) {
349        unsafe {
350            let ptr = if let Some(pattern) = pattern {
351                pattern.to_raw_none()
352            } else {
353                ::std::ptr::null_mut()
354            };
355            ffi::gdk_window_set_background_pattern(self.as_ref().to_glib_none().0, ptr);
356        }
357    }
358}
359
360impl<O: IsA<Window>> WindowExtManual for O {}