| 1 | // Take a look at the license at the top of the repository in the LICENSE file. |
| 2 | |
| 3 | // rustdoc-stripper-ignore-next |
| 4 | //! `IMPL` The `wrapper!` macro and miscellaneous wrapper traits. |
| 5 | |
| 6 | // rustdoc-stripper-ignore-next |
| 7 | /// Defines a wrapper type and implements the appropriate traits. |
| 8 | /// |
| 9 | /// The basic syntax is |
| 10 | /// |
| 11 | /// ```ignore |
| 12 | /// wrapper! { |
| 13 | /// /// Your documentation goes here |
| 14 | /// pub struct $name($kind<$foreign>); |
| 15 | /// |
| 16 | /// match fn { |
| 17 | /// $fn_name => /* a closure-like expression */, |
| 18 | /// ... |
| 19 | /// } |
| 20 | /// } |
| 21 | /// ``` |
| 22 | /// |
| 23 | /// This creates a wrapper named `$name` around the foreign type |
| 24 | /// `$foreign` of `$kind` — one of [`Boxed`][#boxed], |
| 25 | /// [`Shared`][#shared], or [`Object`][#object]. |
| 26 | /// |
| 27 | /// Inside the `match fn` block there are closure-like expressions to |
| 28 | /// provide ways of copying/freeing, or referencing/unreferencing the |
| 29 | /// value that you are wrapping. These expressions will be evaluated |
| 30 | /// in an `unsafe` context, since they frequently invoke `extern` |
| 31 | /// functions from an FFI crate. |
| 32 | /// |
| 33 | /// What follows is a description of each of the possible `$kind`: |
| 34 | /// [`Boxed`][#boxed], [`Shared`][#shared], and [`Object`][#object]; |
| 35 | /// note that each supports different sets of `$fn_name` inside the |
| 36 | /// `match fn` block. Also, `Object` may require you to specify |
| 37 | /// things like the class struct to wrap, plus any interfaces that the |
| 38 | /// class implements. |
| 39 | /// |
| 40 | /// ### Boxed (heap allocated) |
| 41 | /// |
| 42 | /// Boxed records with single ownership allocated on the heap. |
| 43 | /// |
| 44 | /// With no registered `glib_ffi::GType`: |
| 45 | /// |
| 46 | /// ```ignore |
| 47 | /// wrapper! { |
| 48 | /// /// Text buffer iterator |
| 49 | /// pub struct TextIter(Boxed<ffi::GtkTextIter>); |
| 50 | /// |
| 51 | /// match fn { |
| 52 | /// copy => |ptr| ffi::gtk_text_iter_copy(ptr), |
| 53 | /// free => |ptr| ffi::gtk_text_iter_free(ptr), |
| 54 | /// } |
| 55 | /// } |
| 56 | /// ``` |
| 57 | /// |
| 58 | /// `copy`: `|*const $foreign| -> *mut $foreign` creates a copy of the value. |
| 59 | /// |
| 60 | /// `free`: `|*mut $foreign|` frees the value. |
| 61 | /// |
| 62 | /// With a registered `glib_ffi::GType`: |
| 63 | /// |
| 64 | /// ```ignore |
| 65 | /// wrapper! { |
| 66 | /// /// Text buffer iterator |
| 67 | /// pub struct TextIter(Boxed<ffi::GtkTextIter>); |
| 68 | /// |
| 69 | /// match fn { |
| 70 | /// copy => |ptr| ffi::gtk_text_iter_copy(ptr), |
| 71 | /// free => |ptr| ffi::gtk_text_iter_free(ptr), |
| 72 | /// type_ => || ffi::gtk_text_iter_get_type(), |
| 73 | /// } |
| 74 | /// } |
| 75 | /// ``` |
| 76 | /// |
| 77 | /// `type_`: `|| -> glib_ffi::GType` (optional) returns the |
| 78 | /// `glib_ffi::GType` that corresponds to the foreign struct. |
| 79 | /// |
| 80 | /// ### BoxedInline (inline, stack allocated) |
| 81 | /// |
| 82 | /// Boxed records with single ownership allocated on the stack or otherwise inline. |
| 83 | /// With no registered `glib_ffi::GType`: |
| 84 | /// |
| 85 | /// ```ignore |
| 86 | /// wrapper! { |
| 87 | /// /// Text buffer iterator |
| 88 | /// pub struct TextIter(BoxedInline<ffi::GtkTextIter>); |
| 89 | /// |
| 90 | /// match fn { |
| 91 | /// copy => |ptr| ffi::gtk_text_iter_copy(ptr), |
| 92 | /// free => |ptr| ffi::gtk_text_iter_free(ptr), |
| 93 | /// } |
| 94 | /// } |
| 95 | /// ``` |
| 96 | /// |
| 97 | /// `copy`: `|*const $foreign| -> *mut $foreign` (optional) creates a heap allocated copy of the value. |
| 98 | /// |
| 99 | /// `free`: `|*mut $foreign|` (optional) frees the value. |
| 100 | /// |
| 101 | /// With a registered `glib_ffi::GType`: |
| 102 | /// |
| 103 | /// ```ignore |
| 104 | /// wrapper! { |
| 105 | /// /// Text buffer iterator |
| 106 | /// pub struct TextIter(BoxedInline<ffi::GtkTextIter>); |
| 107 | /// |
| 108 | /// match fn { |
| 109 | /// copy => |ptr| ffi::gtk_text_iter_copy(ptr), |
| 110 | /// free => |ptr| ffi::gtk_text_iter_free(ptr), |
| 111 | /// type_ => || ffi::gtk_text_iter_get_type(), |
| 112 | /// } |
| 113 | /// } |
| 114 | /// ``` |
| 115 | /// |
| 116 | /// `type_`: `|| -> glib_ffi::GType` (optional) returns the |
| 117 | /// `glib_ffi::GType` that corresponds to the foreign struct. |
| 118 | /// |
| 119 | /// ### Shared |
| 120 | /// |
| 121 | /// Records with reference-counted, shared ownership. |
| 122 | /// |
| 123 | /// With no registered `glib_ffi::GType`: |
| 124 | /// |
| 125 | /// ```ignore |
| 126 | /// wrapper! { |
| 127 | /// /// Object holding timing information for a single frame. |
| 128 | /// pub struct FrameTimings(Shared<ffi::GdkFrameTimings>); |
| 129 | /// |
| 130 | /// match fn { |
| 131 | /// ref => |ptr| ffi::gdk_frame_timings_ref(ptr), |
| 132 | /// unref => |ptr| ffi::gdk_frame_timings_unref(ptr), |
| 133 | /// } |
| 134 | /// } |
| 135 | /// ``` |
| 136 | /// |
| 137 | /// `ref`: `|*mut $foreign|` increases the refcount. |
| 138 | /// |
| 139 | /// `unref`: `|*mut $foreign|` decreases the refcount. |
| 140 | /// |
| 141 | /// With a registered `glib_ffi::GType`: |
| 142 | /// |
| 143 | /// ```ignore |
| 144 | /// wrapper! { |
| 145 | /// /// Object holding timing information for a single frame. |
| 146 | /// pub struct FrameTimings(Shared<ffi::GdkFrameTimings>); |
| 147 | /// |
| 148 | /// match fn { |
| 149 | /// ref => |ptr| ffi::gdk_frame_timings_ref(ptr), |
| 150 | /// unref => |ptr| ffi::gdk_frame_timings_unref(ptr), |
| 151 | /// type_ => || ffi::gdk_frame_timings_get_type(), |
| 152 | /// } |
| 153 | /// } |
| 154 | /// ``` |
| 155 | /// |
| 156 | /// `type_`: `|| -> glib_ffi::GType` (optional) returns the |
| 157 | /// `glib_ffi::GType` that corresponds to the foreign struct. |
| 158 | /// |
| 159 | /// ### Object |
| 160 | /// |
| 161 | /// Objects -- classes. Note that the class name, if available, must be specified after the |
| 162 | /// $foreign type; see below for [non-derivable classes][#non-derivable-classes]. |
| 163 | /// |
| 164 | /// The basic syntax is this: |
| 165 | /// |
| 166 | /// ```ignore |
| 167 | /// wrapper! { |
| 168 | /// /// Your documentation goes here |
| 169 | /// pub struct InstanceName(Object<ffi::InstanceStruct, ffi::ClassStruct>) |
| 170 | /// @extends ParentClass, GrandparentClass, ..., |
| 171 | /// @implements Interface1, Interface2, ...; |
| 172 | /// |
| 173 | /// match fn { |
| 174 | /// type_ => || ffi::instance_get_type(), |
| 175 | /// } |
| 176 | /// } |
| 177 | /// ``` |
| 178 | /// |
| 179 | /// `type_`: `|| -> glib_ffi::GType` returns the `glib_ffi::GType` |
| 180 | /// that corresponds to the foreign class. |
| 181 | /// |
| 182 | /// #### All parent classes must be specified |
| 183 | /// |
| 184 | /// In the example above, "`@extends ParentClass, GrandparentClass, ...,`" is where you must |
| 185 | /// specify all the parent classes of the one you are wrapping. The uppermost parent class, |
| 186 | /// `glib::Object`, must not be specified. |
| 187 | /// |
| 188 | /// For example, `ffi::GtkWindowGroup` derives directly from |
| 189 | /// `GObject`, so it can be simply wrapped as follows: |
| 190 | /// |
| 191 | /// ```ignore |
| 192 | /// wrapper! { |
| 193 | /// pub struct WindowGroup(Object<ffi::GtkWindowGroup, ffi::GtkWindowGroupClass>); |
| 194 | /// |
| 195 | /// match fn { |
| 196 | /// type_ => || ffi::gtk_window_group_get_type(), |
| 197 | /// } |
| 198 | /// } |
| 199 | /// ``` |
| 200 | /// |
| 201 | /// In contrast, `ffi::GtkButton` has a parent, grandparent, etc. classes, which must be specified: |
| 202 | /// |
| 203 | /// ```ignore |
| 204 | /// wrapper! { |
| 205 | /// pub struct Button(Object<ffi::GtkButton>) @extends Bin, Container, Widget; |
| 206 | /// // see note on interfaces in the example below |
| 207 | /// |
| 208 | /// match fn { |
| 209 | /// type_ => || ffi::gtk_button_get_type(), |
| 210 | /// } |
| 211 | /// } |
| 212 | /// ``` |
| 213 | /// |
| 214 | /// #### Objects which implement interfaces |
| 215 | /// |
| 216 | /// The example above is incomplete, since `ffi::GtkButton` actually implements two interfaces, |
| 217 | /// `Buildable` and `Actionable`. In this case, they must be specified after all the parent classes |
| 218 | /// behind the `@implements` keyword: |
| 219 | /// |
| 220 | /// ```ignore |
| 221 | /// wrapper! { |
| 222 | /// pub struct Button(Object<ffi::GtkButton>) |
| 223 | /// @extends Bin, Container, Widget, // parent classes |
| 224 | /// @implements Buildable, Actionable; // interfaces |
| 225 | /// |
| 226 | /// match fn { |
| 227 | /// type_ => || ffi::gtk_button_get_type(), |
| 228 | /// } |
| 229 | /// } |
| 230 | /// ``` |
| 231 | /// |
| 232 | /// #### Non-derivable classes |
| 233 | /// |
| 234 | /// By convention, GObject implements "final" classes, i.e. those who |
| 235 | /// cannot be subclassed, by *not* exposing a public Class struct. |
| 236 | /// This way it is not possible to override any methods, as there are |
| 237 | /// no `klass.method_name` fields to overwrite. In this case, don't |
| 238 | /// specify a FFI class name at all in the `Object<>` part: |
| 239 | /// |
| 240 | /// ```ignore |
| 241 | /// wrapper! { |
| 242 | /// pub struct Clipboard(Object<ffi::GtkClipboard>); |
| 243 | /// ... |
| 244 | /// } |
| 245 | /// ``` |
| 246 | /// |
| 247 | /// #### Interfaces |
| 248 | /// |
| 249 | /// Interfaces are passed in the same way to the macro but instead of specifying |
| 250 | /// `Object`, `Interface` has to be specified: |
| 251 | /// |
| 252 | /// ```ignore |
| 253 | /// wrapper! { |
| 254 | /// pub struct TreeModel(Interface<ffi::GtkTreeModel, ffi::GtkTreeModelIface>); |
| 255 | /// ... |
| 256 | /// } |
| 257 | /// ``` |
| 258 | /// |
| 259 | /// #### Interfaces with prerequisites |
| 260 | /// |
| 261 | /// Interfaces can declare prerequisites, i.e. the classes from which types that implement the |
| 262 | /// interface have to inherit or interfaces that have to be implemented: |
| 263 | /// |
| 264 | /// ```ignore |
| 265 | /// wrapper! { |
| 266 | /// pub struct TreeSortable(Interface<ffi::GtkTreeSortable, ffi::GtkTreeSortable>) @requires TreeModel; |
| 267 | /// ... |
| 268 | /// } |
| 269 | /// ``` |
| 270 | /// |
| 271 | /// [#boxed]: #boxed |
| 272 | /// [#shared]: #shared |
| 273 | /// [#object]: #object |
| 274 | /// [#non-derivable-classes]: #non-derivable-classes |
| 275 | #[macro_export ] |
| 276 | macro_rules! wrapper { |
| 277 | // Boxed |
| 278 | ( |
| 279 | $(#[$attr:meta])* |
| 280 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Boxed<$ffi_name:ty>); |
| 281 | |
| 282 | match fn { |
| 283 | copy => |$copy_arg:ident| $copy_expr:expr, |
| 284 | free => |$free_arg:ident| $free_expr:expr, |
| 285 | $( |
| 286 | type_ => || $get_type_expr:expr, |
| 287 | )? |
| 288 | } |
| 289 | ) => { |
| 290 | $crate::glib_boxed_wrapper!( |
| 291 | [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name, |
| 292 | @copy $copy_arg $copy_expr, @free $free_arg $free_expr |
| 293 | $(, @type_ $get_type_expr)? |
| 294 | ); |
| 295 | }; |
| 296 | |
| 297 | // BoxedInline |
| 298 | ( |
| 299 | $(#[$attr:meta])* |
| 300 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (BoxedInline<$ffi_name:ty>); |
| 301 | |
| 302 | match fn { |
| 303 | $( |
| 304 | copy => |$copy_arg:ident| $copy_expr:expr, |
| 305 | free => |$free_arg:ident| $free_expr:expr, |
| 306 | )? |
| 307 | $( |
| 308 | init => |$init_arg:ident| $init_expr:expr, |
| 309 | copy_into => |$copy_into_arg_dest:ident, $copy_into_arg_src:ident| $copy_into_expr:expr, |
| 310 | clear => |$clear_arg:ident| $clear_expr:expr, |
| 311 | )? |
| 312 | $( |
| 313 | type_ => || $get_type_expr:expr, |
| 314 | )? |
| 315 | } |
| 316 | ) => { |
| 317 | $crate::glib_boxed_inline_wrapper!( |
| 318 | [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name |
| 319 | $(, @copy $copy_arg $copy_expr, @free $free_arg $free_expr)? |
| 320 | $(, @init $init_arg $init_expr, @copy_into $copy_into_arg_dest $copy_into_arg_src $copy_into_expr, @clear $clear_arg $clear_expr)? |
| 321 | $(, @type_ $get_type_expr)? |
| 322 | ); |
| 323 | }; |
| 324 | |
| 325 | // BoxedInline |
| 326 | ( |
| 327 | $(#[$attr:meta])* |
| 328 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (BoxedInline<$ffi_name:ty>); |
| 329 | ) => { |
| 330 | $crate::glib_boxed_inline_wrapper!( |
| 331 | [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name |
| 332 | ); |
| 333 | }; |
| 334 | |
| 335 | // Shared |
| 336 | ( |
| 337 | $(#[$attr:meta])* |
| 338 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Shared<$ffi_name:ty>); |
| 339 | |
| 340 | match fn { |
| 341 | ref => |$ref_arg:ident| $ref_expr:expr, |
| 342 | unref => |$unref_arg:ident| $unref_expr:expr, |
| 343 | $( |
| 344 | type_ => || $get_type_expr:expr, |
| 345 | )? |
| 346 | } |
| 347 | ) => { |
| 348 | $crate::glib_shared_wrapper!( |
| 349 | [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name, |
| 350 | @ref $ref_arg $ref_expr, @unref $unref_arg $unref_expr |
| 351 | $(, @type_ $get_type_expr)? |
| 352 | ); |
| 353 | }; |
| 354 | |
| 355 | // Object, no parents |
| 356 | ( |
| 357 | $(#[$attr:meta])* |
| 358 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Object<$ffi_name:ty $(, $ffi_class_name:ty)?>) $(@implements $($implements:path),+)?; |
| 359 | |
| 360 | match fn { |
| 361 | type_ => || $get_type_expr:expr, |
| 362 | } |
| 363 | ) => { |
| 364 | $crate::glib_object_wrapper!( |
| 365 | @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, *mut std::os::raw::c_void, (), $ffi_name, |
| 366 | $( @ffi_class $ffi_class_name ,)? |
| 367 | @type_ $get_type_expr, |
| 368 | @extends [], |
| 369 | @implements [$($($implements),+)?] |
| 370 | ); |
| 371 | }; |
| 372 | |
| 373 | // Object, parents |
| 374 | ( |
| 375 | $(#[$attr:meta])* |
| 376 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Object<$ffi_name:ty $(, $ffi_class_name:ty)?>) @extends $($extends:path),+ $(, @implements $($implements:path),+)?; |
| 377 | |
| 378 | match fn { |
| 379 | type_ => || $get_type_expr:expr, |
| 380 | } |
| 381 | ) => { |
| 382 | $crate::glib_object_wrapper!( |
| 383 | @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, *mut std::os::raw::c_void, (), $ffi_name, |
| 384 | $( @ffi_class $ffi_class_name ,)? |
| 385 | @type_ $get_type_expr, |
| 386 | @extends [$($extends),+], |
| 387 | @implements [$($($implements),+)?] |
| 388 | ); |
| 389 | }; |
| 390 | |
| 391 | // ObjectSubclass, no parents |
| 392 | ( |
| 393 | $(#[$attr:meta])* |
| 394 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectSubclass<$subclass:ty>) $(@implements $($implements:path),+)?; |
| 395 | ) => { |
| 396 | $crate::glib_object_wrapper!( |
| 397 | @object_subclass [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $subclass, |
| 398 | @extends [], |
| 399 | @implements [$($($implements),+)?] |
| 400 | ); |
| 401 | }; |
| 402 | |
| 403 | // ObjectSubclass, parents |
| 404 | ( |
| 405 | $(#[$attr:meta])* |
| 406 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectSubclass<$subclass:ty>) @extends $($extends:path),+ $(, @implements $($implements:path),+)?; |
| 407 | ) => { |
| 408 | $crate::glib_object_wrapper!( |
| 409 | @object_subclass [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $subclass, |
| 410 | @extends [$($extends),+], |
| 411 | @implements [$($($implements),+)?] |
| 412 | ); |
| 413 | }; |
| 414 | |
| 415 | // Interface |
| 416 | ( |
| 417 | $(#[$attr:meta])* |
| 418 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Interface<$ffi_name:ty $(, $ffi_class_name:ty)?>) $(@requires $($requires:path),+)?; |
| 419 | |
| 420 | match fn { |
| 421 | type_ => || $get_type_expr:expr, |
| 422 | } |
| 423 | ) => { |
| 424 | $crate::glib_object_wrapper!( |
| 425 | @interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, *mut std::os::raw::c_void, $ffi_name, |
| 426 | $( @ffi_class $ffi_class_name ,)? |
| 427 | @type_ $get_type_expr, |
| 428 | @requires [$( $($requires),+ )?] |
| 429 | ); |
| 430 | }; |
| 431 | |
| 432 | // ObjectInterface |
| 433 | ( |
| 434 | $(#[$attr:meta])* |
| 435 | $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectInterface<$iface_name:ty>) $(@requires $($requires:path),+)?; |
| 436 | ) => { |
| 437 | $crate::glib_object_wrapper!( |
| 438 | @object_interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $iface_name, |
| 439 | @type_ $crate::translate::IntoGlib::into_glib(<$iface_name as $crate::subclass::interface::ObjectInterfaceType>::type_()), |
| 440 | @requires [$( $($requires),+ )?] |
| 441 | ); |
| 442 | }; |
| 443 | } |
| 444 | |