gdk_pixbuf/
pixbuf.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{future::Future, io::Read, mem, path::Path, pin::Pin, ptr, slice};
4
5use glib::{Error, prelude::*, translate::*};
6use libc::{c_uchar, c_void};
7
8use crate::{Colorspace, Pixbuf, PixbufFormat, ffi};
9
10impl Pixbuf {
11    #[doc(alias = "gdk_pixbuf_new_from_data")]
12    pub fn from_mut_slice<T: AsMut<[u8]>>(
13        data: T,
14        colorspace: Colorspace,
15        has_alpha: bool,
16        bits_per_sample: i32,
17        width: i32,
18        height: i32,
19        row_stride: i32,
20    ) -> Pixbuf {
21        unsafe extern "C" fn destroy<T: AsMut<[u8]>>(_: *mut c_uchar, data: *mut c_void) {
22            unsafe {
23                let _data: Box<T> = Box::from_raw(data as *mut T); // the data will be destroyed now
24            }
25        }
26        assert!(width > 0, "width must be greater than 0");
27        assert!(height > 0, "height must be greater than 0");
28        assert!(row_stride > 0, "row_stride must be greater than 0");
29        assert_eq!(
30            bits_per_sample, 8,
31            "bits_per_sample == 8 is the only supported value"
32        );
33
34        let width = width as usize;
35        let height = height as usize;
36        let row_stride = row_stride as usize;
37        let bits_per_sample = bits_per_sample as usize;
38
39        let n_channels = if has_alpha { 4 } else { 3 };
40        let last_row_len = width * (n_channels * bits_per_sample).div_ceil(8);
41
42        let mut data: Box<T> = Box::new(data);
43
44        let ptr = {
45            let data: &mut [u8] = (*data).as_mut();
46            assert!(
47                data.len() >= ((height - 1) * row_stride + last_row_len),
48                "data.len() must fit the width, height, and row_stride"
49            );
50            data.as_mut_ptr()
51        };
52
53        unsafe {
54            from_glib_full(ffi::gdk_pixbuf_new_from_data(
55                ptr,
56                colorspace.into_glib(),
57                has_alpha.into_glib(),
58                bits_per_sample as i32,
59                width as i32,
60                height as i32,
61                row_stride as i32,
62                Some(destroy::<T>),
63                Box::into_raw(data) as *mut _,
64            ))
65        }
66    }
67
68    // rustdoc-stripper-ignore-next
69    /// Creates a `Pixbuf` from a type implementing `Read` (like `File`).
70    ///
71    /// ```no_run
72    /// use std::fs::File;
73    /// use gdk_pixbuf::Pixbuf;
74    ///
75    /// let f = File::open("some_file.png").expect("failed to open image");
76    /// let pixbuf = Pixbuf::from_read(f).expect("failed to load image");
77    /// ```
78    pub fn from_read<R: Read + Send + 'static>(r: R) -> Result<Pixbuf, Error> {
79        Pixbuf::from_stream(&gio::ReadInputStream::new(r), None::<&gio::Cancellable>)
80    }
81
82    #[doc(alias = "gdk_pixbuf_new_from_stream_async")]
83    pub fn from_stream_async<
84        P: IsA<gio::InputStream>,
85        Q: IsA<gio::Cancellable>,
86        R: FnOnce(Result<Pixbuf, Error>) + 'static,
87    >(
88        stream: &P,
89        cancellable: Option<&Q>,
90        callback: R,
91    ) {
92        let main_context = glib::MainContext::ref_thread_default();
93        let is_main_context_owner = main_context.is_owner();
94        let has_acquired_main_context = (!is_main_context_owner)
95            .then(|| main_context.acquire().ok())
96            .flatten();
97        assert!(
98            is_main_context_owner || has_acquired_main_context.is_some(),
99            "Async operations only allowed if the thread is owning the MainContext"
100        );
101
102        let cancellable = cancellable.map(|p| p.as_ref());
103        let user_data: Box<glib::thread_guard::ThreadGuard<R>> =
104            Box::new(glib::thread_guard::ThreadGuard::new(callback));
105        unsafe extern "C" fn from_stream_async_trampoline<
106            R: FnOnce(Result<Pixbuf, Error>) + 'static,
107        >(
108            _source_object: *mut glib::gobject_ffi::GObject,
109            res: *mut gio::ffi::GAsyncResult,
110            user_data: glib::ffi::gpointer,
111        ) {
112            unsafe {
113                let mut error = ptr::null_mut();
114                let ptr = ffi::gdk_pixbuf_new_from_stream_finish(res, &mut error);
115                let result = if error.is_null() {
116                    Ok(from_glib_full(ptr))
117                } else {
118                    Err(from_glib_full(error))
119                };
120                let callback: Box<glib::thread_guard::ThreadGuard<R>> =
121                    Box::from_raw(user_data as *mut _);
122                let callback = callback.into_inner();
123                callback(result);
124            }
125        }
126        let callback = from_stream_async_trampoline::<R>;
127        unsafe {
128            ffi::gdk_pixbuf_new_from_stream_async(
129                stream.as_ref().to_glib_none().0,
130                cancellable.to_glib_none().0,
131                Some(callback),
132                Box::into_raw(user_data) as *mut _,
133            );
134        }
135    }
136
137    pub fn from_stream_future<P: IsA<gio::InputStream> + Clone + 'static>(
138        stream: &P,
139    ) -> Pin<Box<dyn Future<Output = Result<Pixbuf, Error>> + 'static>> {
140        let stream = stream.clone();
141        Box::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
142            Self::from_stream_async(&stream, Some(cancellable), move |res| {
143                send.resolve(res);
144            });
145        }))
146    }
147
148    #[doc(alias = "gdk_pixbuf_new_from_stream_at_scale_async")]
149    pub fn from_stream_at_scale_async<
150        P: IsA<gio::InputStream>,
151        Q: IsA<gio::Cancellable>,
152        R: FnOnce(Result<Pixbuf, Error>) + 'static,
153    >(
154        stream: &P,
155        width: i32,
156        height: i32,
157        preserve_aspect_ratio: bool,
158        cancellable: Option<&Q>,
159        callback: R,
160    ) {
161        let main_context = glib::MainContext::ref_thread_default();
162        let is_main_context_owner = main_context.is_owner();
163        let has_acquired_main_context = (!is_main_context_owner)
164            .then(|| main_context.acquire().ok())
165            .flatten();
166        assert!(
167            is_main_context_owner || has_acquired_main_context.is_some(),
168            "Async operations only allowed if the thread is owning the MainContext"
169        );
170
171        let cancellable = cancellable.map(|p| p.as_ref());
172        let user_data: Box<glib::thread_guard::ThreadGuard<R>> =
173            Box::new(glib::thread_guard::ThreadGuard::new(callback));
174        unsafe extern "C" fn from_stream_at_scale_async_trampoline<
175            R: FnOnce(Result<Pixbuf, Error>) + 'static,
176        >(
177            _source_object: *mut glib::gobject_ffi::GObject,
178            res: *mut gio::ffi::GAsyncResult,
179            user_data: glib::ffi::gpointer,
180        ) {
181            unsafe {
182                let mut error = ptr::null_mut();
183                let ptr = ffi::gdk_pixbuf_new_from_stream_finish(res, &mut error);
184                let result = if error.is_null() {
185                    Ok(from_glib_full(ptr))
186                } else {
187                    Err(from_glib_full(error))
188                };
189                let callback: Box<glib::thread_guard::ThreadGuard<R>> =
190                    Box::from_raw(user_data as *mut _);
191                let callback = callback.into_inner();
192                callback(result);
193            }
194        }
195        let callback = from_stream_at_scale_async_trampoline::<R>;
196        unsafe {
197            ffi::gdk_pixbuf_new_from_stream_at_scale_async(
198                stream.as_ref().to_glib_none().0,
199                width,
200                height,
201                preserve_aspect_ratio.into_glib(),
202                cancellable.to_glib_none().0,
203                Some(callback),
204                Box::into_raw(user_data) as *mut _,
205            );
206        }
207    }
208
209    pub fn from_stream_at_scale_future<P: IsA<gio::InputStream> + Clone + 'static>(
210        stream: &P,
211        width: i32,
212        height: i32,
213        preserve_aspect_ratio: bool,
214    ) -> Pin<Box<dyn Future<Output = Result<Pixbuf, Error>> + 'static>> {
215        let stream = stream.clone();
216        Box::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
217            Self::from_stream_at_scale_async(
218                &stream,
219                width,
220                height,
221                preserve_aspect_ratio,
222                Some(cancellable),
223                move |res| {
224                    send.resolve(res);
225                },
226            );
227        }))
228    }
229
230    // rustdoc-stripper-ignore-next
231    /// Returns a mutable slice to the pixbuf's pixel data.
232    ///
233    /// This function will cause an implicit copy if the pixbuf was created from read-only data.
234    ///
235    /// Please see the section on [image data](#image-data) for information about how the pixel
236    /// data is stored in memory.
237    ///
238    /// # Safety
239    /// No other reference to this pixbuf's data must exist when this method is called.
240    ///
241    /// Until you drop the returned reference, you must not call any methods on the pixbuf which may read
242    /// or write to the data.
243    #[allow(clippy::mut_from_ref)]
244    #[allow(clippy::missing_safety_doc)]
245    #[doc(alias = "gdk_pixbuf_get_pixels_with_length")]
246    #[doc(alias = "get_pixels")]
247    pub unsafe fn pixels(&self) -> &mut [u8] {
248        unsafe {
249            let mut len = 0;
250            let ptr = ffi::gdk_pixbuf_get_pixels_with_length(self.to_glib_none().0, &mut len);
251            if len == 0 {
252                return &mut [];
253            }
254            slice::from_raw_parts_mut(ptr, len as usize)
255        }
256    }
257
258    pub fn put_pixel(&self, x: u32, y: u32, red: u8, green: u8, blue: u8, alpha: u8) {
259        assert!(
260            x < self.width() as u32,
261            "x must be less than the pixbuf's width"
262        );
263        assert!(
264            y < self.height() as u32,
265            "y must be less than the pixbuf's height"
266        );
267
268        unsafe {
269            let x = x as usize;
270            let y = y as usize;
271            let n_channels = self.n_channels() as usize;
272            assert!(n_channels == 3 || n_channels == 4);
273            let rowstride = self.rowstride() as usize;
274            let pixels = self.pixels();
275            let pos = y * rowstride + x * n_channels;
276
277            pixels[pos] = red;
278            pixels[pos + 1] = green;
279            pixels[pos + 2] = blue;
280            if n_channels == 4 {
281                pixels[pos + 3] = alpha;
282            }
283        }
284    }
285
286    /// Parses an image file far enough to determine its format and size.
287    /// ## `filename`
288    /// The name of the file to identify.
289    ///
290    /// # Returns
291    ///
292    /// A [`PixbufFormat`][crate::PixbufFormat] describing
293    ///   the image format of the file
294    ///
295    /// ## `width`
296    /// Return location for the width of the image
297    ///
298    /// ## `height`
299    /// Return location for the height of the image
300    #[doc(alias = "gdk_pixbuf_get_file_info")]
301    #[doc(alias = "get_file_info")]
302    pub fn file_info<T: AsRef<Path>>(filename: T) -> Option<(PixbufFormat, i32, i32)> {
303        unsafe {
304            let mut width = mem::MaybeUninit::uninit();
305            let mut height = mem::MaybeUninit::uninit();
306            let ret = ffi::gdk_pixbuf_get_file_info(
307                filename.as_ref().to_glib_none().0,
308                width.as_mut_ptr(),
309                height.as_mut_ptr(),
310            );
311            if !ret.is_null() {
312                Some((
313                    from_glib_none(ret),
314                    width.assume_init(),
315                    height.assume_init(),
316                ))
317            } else {
318                None
319            }
320        }
321    }
322
323    /// Asynchronously parses an image file far enough to determine its
324    /// format and size.
325    ///
326    /// For more details see gdk_pixbuf_get_file_info(), which is the synchronous
327    /// version of this function.
328    ///
329    /// When the operation is finished, @callback will be called in the
330    /// main thread. You can then call gdk_pixbuf_get_file_info_finish() to
331    /// get the result of the operation.
332    /// ## `filename`
333    /// The name of the file to identify
334    /// ## `cancellable`
335    /// optional `GCancellable` object, `NULL` to ignore
336    /// ## `callback`
337    /// a `GAsyncReadyCallback` to call when the file info is available
338    #[doc(alias = "gdk_pixbuf_get_file_info_async")]
339    #[doc(alias = "get_file_info_async")]
340    pub fn file_info_async<
341        P: IsA<gio::Cancellable>,
342        Q: FnOnce(Result<Option<(PixbufFormat, i32, i32)>, Error>) + 'static,
343        T: AsRef<Path>,
344    >(
345        filename: T,
346        cancellable: Option<&P>,
347        callback: Q,
348    ) {
349        let main_context = glib::MainContext::ref_thread_default();
350        let is_main_context_owner = main_context.is_owner();
351        let has_acquired_main_context = (!is_main_context_owner)
352            .then(|| main_context.acquire().ok())
353            .flatten();
354        assert!(
355            is_main_context_owner || has_acquired_main_context.is_some(),
356            "Async operations only allowed if the thread is owning the MainContext"
357        );
358
359        let cancellable = cancellable.map(|p| p.as_ref());
360        let user_data: Box<glib::thread_guard::ThreadGuard<Q>> =
361            Box::new(glib::thread_guard::ThreadGuard::new(callback));
362        unsafe extern "C" fn get_file_info_async_trampoline<
363            Q: FnOnce(Result<Option<(PixbufFormat, i32, i32)>, Error>) + 'static,
364        >(
365            _source_object: *mut glib::gobject_ffi::GObject,
366            res: *mut gio::ffi::GAsyncResult,
367            user_data: glib::ffi::gpointer,
368        ) {
369            unsafe {
370                let mut error = ptr::null_mut();
371                let mut width = mem::MaybeUninit::uninit();
372                let mut height = mem::MaybeUninit::uninit();
373                let ret = ffi::gdk_pixbuf_get_file_info_finish(
374                    res,
375                    width.as_mut_ptr(),
376                    height.as_mut_ptr(),
377                    &mut error,
378                );
379                let result = if !error.is_null() {
380                    Err(from_glib_full(error))
381                } else if ret.is_null() {
382                    Ok(None)
383                } else {
384                    Ok(Some((
385                        from_glib_none(ret),
386                        width.assume_init(),
387                        height.assume_init(),
388                    )))
389                };
390                let callback: Box<glib::thread_guard::ThreadGuard<Q>> =
391                    Box::from_raw(user_data as *mut _);
392                let callback = callback.into_inner();
393                callback(result);
394            }
395        }
396        let callback = get_file_info_async_trampoline::<Q>;
397        unsafe {
398            ffi::gdk_pixbuf_get_file_info_async(
399                filename.as_ref().to_glib_none().0,
400                cancellable.to_glib_none().0,
401                Some(callback),
402                Box::into_raw(user_data) as *mut _,
403            );
404        }
405    }
406
407    #[allow(clippy::type_complexity)]
408    #[doc(alias = "get_file_info_async")]
409    pub fn file_info_future<T: AsRef<Path> + Clone + 'static>(
410        filename: T,
411    ) -> Pin<Box<dyn Future<Output = Result<Option<(PixbufFormat, i32, i32)>, Error>> + 'static>>
412    {
413        Box::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
414            Self::file_info_async(filename, Some(cancellable), move |res| {
415                send.resolve(res);
416            });
417        }))
418    }
419
420    /// Vector version of `gdk_pixbuf_save_to_buffer()`.
421    ///
422    /// Saves pixbuf to a new buffer in format @type_, which is currently "jpeg",
423    /// "tiff", "png", "ico" or "bmp".
424    ///
425    /// See `GdkPixbuf::Pixbuf::save_to_buffer()` for more details.
426    /// ## `type_`
427    /// name of file format.
428    /// ## `option_keys`
429    /// name of options to set
430    /// ## `option_values`
431    /// values for named options
432    ///
433    /// # Returns
434    ///
435    /// whether an error was set
436    ///
437    /// ## `buffer`
438    ///
439    ///   location to receive a pointer to the new buffer.
440    #[doc(alias = "gdk_pixbuf_save_to_bufferv")]
441    pub fn save_to_bufferv(&self, type_: &str, options: &[(&str, &str)]) -> Result<Vec<u8>, Error> {
442        unsafe {
443            let mut buffer = ptr::null_mut();
444            let mut buffer_size = mem::MaybeUninit::uninit();
445            let mut error = ptr::null_mut();
446            let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect();
447            let option_values: Vec<&str> = options.iter().map(|o| o.1).collect();
448            let _ = ffi::gdk_pixbuf_save_to_bufferv(
449                self.to_glib_none().0,
450                &mut buffer,
451                buffer_size.as_mut_ptr(),
452                type_.to_glib_none().0,
453                option_keys.to_glib_none().0,
454                option_values.to_glib_none().0,
455                &mut error,
456            );
457            if error.is_null() {
458                Ok(FromGlibContainer::from_glib_full_num(
459                    buffer,
460                    buffer_size.assume_init() as _,
461                ))
462            } else {
463                Err(from_glib_full(error))
464            }
465        }
466    }
467
468    /// Saves `pixbuf` to an output stream.
469    ///
470    /// Supported file formats are currently "jpeg", "tiff", "png", "ico" or
471    /// "bmp".
472    ///
473    /// See `GdkPixbuf::Pixbuf::save_to_stream()` for more details.
474    /// ## `stream`
475    /// a `GOutputStream` to save the pixbuf to
476    /// ## `type_`
477    /// name of file format
478    /// ## `option_keys`
479    /// name of options to set
480    /// ## `option_values`
481    /// values for named options
482    /// ## `cancellable`
483    /// optional `GCancellable` object, `NULL` to ignore
484    ///
485    /// # Returns
486    ///
487    /// `TRUE` if the pixbuf was saved successfully, `FALSE` if an
488    ///   error was set.
489    #[doc(alias = "gdk_pixbuf_save_to_streamv")]
490    pub fn save_to_streamv<P: IsA<gio::OutputStream>, Q: IsA<gio::Cancellable>>(
491        &self,
492        stream: &P,
493        type_: &str,
494        options: &[(&str, &str)],
495        cancellable: Option<&Q>,
496    ) -> Result<(), Error> {
497        let cancellable = cancellable.map(|p| p.as_ref());
498        unsafe {
499            let mut error = ptr::null_mut();
500            let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect();
501            let option_values: Vec<&str> = options.iter().map(|o| o.1).collect();
502            let _ = ffi::gdk_pixbuf_save_to_streamv(
503                self.to_glib_none().0,
504                stream.as_ref().to_glib_none().0,
505                type_.to_glib_none().0,
506                option_keys.to_glib_none().0,
507                option_values.to_glib_none().0,
508                cancellable.to_glib_none().0,
509                &mut error,
510            );
511            if error.is_null() {
512                Ok(())
513            } else {
514                Err(from_glib_full(error))
515            }
516        }
517    }
518
519    /// Saves `pixbuf` to an output stream asynchronously.
520    ///
521    /// For more details see gdk_pixbuf_save_to_streamv(), which is the synchronous
522    /// version of this function.
523    ///
524    /// When the operation is finished, `callback` will be called in the main thread.
525    ///
526    /// You can then call gdk_pixbuf_save_to_stream_finish() to get the result of
527    /// the operation.
528    /// ## `stream`
529    /// a `GOutputStream` to which to save the pixbuf
530    /// ## `type_`
531    /// name of file format
532    /// ## `option_keys`
533    /// name of options to set
534    /// ## `option_values`
535    /// values for named options
536    /// ## `cancellable`
537    /// optional `GCancellable` object, `NULL` to ignore
538    /// ## `callback`
539    /// a `GAsyncReadyCallback` to call when the pixbuf is saved
540    #[doc(alias = "gdk_pixbuf_save_to_streamv_async")]
541    pub fn save_to_streamv_async<
542        P: IsA<gio::OutputStream>,
543        Q: IsA<gio::Cancellable>,
544        R: FnOnce(Result<(), Error>) + 'static,
545    >(
546        &self,
547        stream: &P,
548        type_: &str,
549        options: &[(&str, &str)],
550        cancellable: Option<&Q>,
551        callback: R,
552    ) {
553        let main_context = glib::MainContext::ref_thread_default();
554        let is_main_context_owner = main_context.is_owner();
555        let has_acquired_main_context = (!is_main_context_owner)
556            .then(|| main_context.acquire().ok())
557            .flatten();
558        assert!(
559            is_main_context_owner || has_acquired_main_context.is_some(),
560            "Async operations only allowed if the thread is owning the MainContext"
561        );
562
563        let cancellable = cancellable.map(|p| p.as_ref());
564        let user_data: Box<glib::thread_guard::ThreadGuard<R>> =
565            Box::new(glib::thread_guard::ThreadGuard::new(callback));
566        unsafe extern "C" fn save_to_streamv_async_trampoline<
567            R: FnOnce(Result<(), Error>) + 'static,
568        >(
569            _source_object: *mut glib::gobject_ffi::GObject,
570            res: *mut gio::ffi::GAsyncResult,
571            user_data: glib::ffi::gpointer,
572        ) {
573            unsafe {
574                let mut error = ptr::null_mut();
575                let _ = ffi::gdk_pixbuf_save_to_stream_finish(res, &mut error);
576                let result = if error.is_null() {
577                    Ok(())
578                } else {
579                    Err(from_glib_full(error))
580                };
581                let callback: Box<glib::thread_guard::ThreadGuard<R>> =
582                    Box::from_raw(user_data as *mut _);
583                let callback = callback.into_inner();
584                callback(result);
585            }
586        }
587        let callback = save_to_streamv_async_trampoline::<R>;
588        unsafe {
589            let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect();
590            let option_values: Vec<&str> = options.iter().map(|o| o.1).collect();
591            ffi::gdk_pixbuf_save_to_streamv_async(
592                self.to_glib_none().0,
593                stream.as_ref().to_glib_none().0,
594                type_.to_glib_none().0,
595                option_keys.to_glib_none().0,
596                option_values.to_glib_none().0,
597                cancellable.to_glib_none().0,
598                Some(callback),
599                Box::into_raw(user_data) as *mut _,
600            );
601        }
602    }
603
604    pub fn save_to_streamv_future<P: IsA<gio::OutputStream> + Clone + 'static>(
605        &self,
606        stream: &P,
607        type_: &str,
608        options: &[(&str, &str)],
609    ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + 'static>> {
610        let stream = stream.clone();
611        let type_ = String::from(type_);
612        let options = options
613            .iter()
614            .map(|&(k, v)| (String::from(k), String::from(v)))
615            .collect::<Vec<(String, String)>>();
616        Box::pin(gio::GioFuture::new(self, move |obj, cancellable, send| {
617            let options = options
618                .iter()
619                .map(|(k, v)| (k.as_str(), v.as_str()))
620                .collect::<Vec<(&str, &str)>>();
621
622            obj.save_to_streamv_async(
623                &stream,
624                &type_,
625                options.as_slice(),
626                Some(cancellable),
627                move |res| {
628                    send.resolve(res);
629                },
630            );
631        }))
632    }
633
634    /// Vector version of `gdk_pixbuf_save()`.
635    ///
636    /// Saves pixbuf to a file in `type`, which is currently "jpeg", "png", "tiff", "ico" or "bmp".
637    ///
638    /// If @error is set, `FALSE` will be returned.
639    ///
640    /// See `GdkPixbuf::Pixbuf::save()` for more details.
641    /// ## `filename`
642    /// name of file to save.
643    /// ## `type_`
644    /// name of file format.
645    /// ## `option_keys`
646    /// name of options to set
647    /// ## `option_values`
648    /// values for named options
649    ///
650    /// # Returns
651    ///
652    /// whether an error was set
653    #[doc(alias = "gdk_pixbuf_savev")]
654    pub fn savev<T: AsRef<Path>>(
655        &self,
656        filename: T,
657        type_: &str,
658        options: &[(&str, &str)],
659    ) -> Result<(), Error> {
660        unsafe {
661            let mut error = ptr::null_mut();
662            let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect();
663            let option_values: Vec<&str> = options.iter().map(|o| o.1).collect();
664            let _ = ffi::gdk_pixbuf_savev(
665                self.to_glib_none().0,
666                filename.as_ref().to_glib_none().0,
667                type_.to_glib_none().0,
668                option_keys.to_glib_none().0,
669                option_values.to_glib_none().0,
670                &mut error,
671            );
672            if error.is_null() {
673                Ok(())
674            } else {
675                Err(from_glib_full(error))
676            }
677        }
678    }
679}