Introduction

This book aims to provide complete documentation on how to use gir to generate bindings for GObject based libraries based on the GObject introspection data.

gir consists of a library and at the same time a binary you can use to generate the bindings.

gir requires both a Gir.toml configuration file and a .gir file containing the GObject introspection data.

  • The *.gir you need will correspond to the project you want to generate bindings for. You can get them from here or directly on ubuntu website (for example: http://packages.ubuntu.com/zesty/amd64/libgtk-3-dev).

  • The *.toml is what is used to pass various settings and options to gir for use when generating the bindings - you will need to write one to suit your needs, for an example you can take a look to gtk-rs/sys/gir-gtk.toml.

It operates on 4 different modes:

  • sys: is what creates the low-level FFI bindings from the supplied *.gir file - these are essentially direct calls in to the related C library and are typically unsafe. The resulting crate is typically appended with -sys.

  • normal: generates another crate for a layer on top of these unsafe (sys) bindings which makes them safe for use in general Rust.

  • not_bound: allows you to see the detected types/methods that will not be generated for whatever reasons.

  • doc: used for documentation generation

Helpers

gir includes a wrapper script ./generator.py that detects Gir.toml configurations in the current directory (or the path(s) passed on the command-line) and generates "normal" or "sys" crates for it. Alternatively --embed-docs can be passed to prepare source-code for a documentation build by moving all documentation into it. For a complete overview of available options, pass --help.

GIR format reference

It can always be useful to look at the reference or schema.

Configuration

GIR uses two configurations files, one for generating the FFI part of the bindings and the other file for the Rust API. The configuration files must be named Gir.toml

  • The FFI configuration allows things such as ignoring objects, overriding the minimum required version for a specific type or renaming the generated crate name.

  • The Rust API configuration is a bit more complex as it allows configuring Objects, Enums, Bitfields, Functions, Properties, Signals and a few other things.

FFI Options

In FFI (-m sys) mode, gir generates as much as it can. So in this mode, the TOML file is mostly used to ignore some objects. To do so, you need to add its fullname to an ignore array. Example:

ignore = ["Gtk.Widget", "Gtk.Window"]

And that's all! Neither GtkWidget nor GtkWindow (alongside with their functions) will be generated.

You also need to add any needed external libraries in the "external_libraries" parameter. Example:

[options]
external_libraries = [
   "GLib",
   "GObject",
]

You can specify a few other options:

[options]
girs_directories = ["../gir-files"]
library = "GtkSource"
version = "3.0"
min_cfg_version = "3.0"
target_path = "."
# Path where lib.rs generated (defaults to <target_path>/src)
# auto_path = "src"
work_mode = "sys"
# If true then build.rs will be split into 2 parts:
# always generated build_version.rs,
# and build.rs that generated only if not exists.
# Defaults to false
split_build_rs = false
# Adds extra versions to features
extra_versions = [
   "3.15",
   "3.17",
]
# Change library version for version
[[lib_version_overrides]]
version = "3.16"
lib_version = "3.16.1"
# Add extra dependencies to feature
[[feature_dependencies]]
version = "3.16"
dependencies = [
  "glib-sys/v3_16"
]
# Add features to the "dox" feature declaration in `Cargo.toml`. So with the following
# config, it'll generate:
# dox = ["whatever"]
dox_feature_dependencies = ["whatever"]

You can mark some functions that has suffix _utf8 on Windows:

[[object]]
name = "GdkPixbuf.PixbufAnimation"
status = "generate"
    [[object.function]]
    name = "new_from_file"
    is_windows_utf8 = true

Also, you can add rust cfg conditions on objects, functions and constants, for example, when flagging for conditional compilation:

[[object]]
name = "GstGL.GLDisplayEGL"
status = "generate"
cfg_condition = "feature = \"egl\""
    [[object.function]]
    pattern = ".*"
    cfg_condition = "feature = \"egl\""

Generation in FFI mode

When you're ready, let's generate the FFI part. In the command we'll execute, ../gir-files is where the directory with your .gir files is. (But again, you can just clone the gir-files repository and add your file(s) in it). Then let's run the command:

> cargo run --release -- -c YourSysGirFile.toml -d ../gir-files -m sys -o the-output-directory-sys

The generated files will be placed in the-output-directory-sys. Just take care about the dependencies and the crate's name generated in the Cargo.toml file (update them if they don't work as expected).

You now have the sys part of your binding!

API Options

This mode requires you to write another TOML file. gtk/Gir.toml is a good example.

[options]
girs_directories = ["gir-files"]
library = "Gtk"
version = "3.0"
min_cfg_version = "3.4"
target_path = "."
# Path where objects generated (defaults to <target_path>/src/auto)
# auto_path = "src/auto"
work_mode = "normal"
generate_safety_asserts = true
deprecate_by_min_version = true
# With this option enabled, versions for gir and gir-files saved only to one file to minimize noise,
# can also take path to the directory for saving "versions.txt" or filename with extension.
# Relative to target_path
single_version_file = true
# Generation of Display trait enabled for all enums, classes, etc.,
# which do not have an override for `generate_display_trait`
# (defaults to "true")
generate_display_trait = true
# Trust the nullability information about return values. If this is disabled
# then any pointer return type is assumed to be nullable unless there is an
# explicit override for it.
# This has to be used carefully as many libraries are missing nullable
# annotations for return values, which then will cause a panic once an
# unexpected NULL is returned.
trust_return_value_nullability = false
# Disable running `cargo fmt` on generated files
# (defaults to false)
disable_format = true
# Always generate a Builder if possible. This is mostly a convenient setter as most of the
# time you might want the Builder to be generated. Ignoring none-desired ones can still be done with per object `generate_builder` configuration.
# (defaults to false)
generate_builder = true

This mode generates only the specified objects. You can either add the object's fullname to the generate array or add it to the manual array (but in this case, it won't be generated, just used in other functions/methods instead of generating an "ignored" argument). Example:

generate = ["Gtk.Widget", "Gtk.Window"]
manual = ["Gtk.Button"]

So in here, both GtkWidget and GtkWindow will be fully generated and functions/methods using GtkButton will be uncommented. To generate code for all global functions, add Gtk.* to the generate array.

To also generate a Builder struct for a widget, it needs to be set with the generate_builder flag in object configuration:

[[object]]
name = "Gtk.TreeView"
status = "generate"
generate_builder = true

If you want to remove warning messages about the not bound Builders during the generation you don't want to be generated, you can ignore them with the generate_builder flag in object configuration:

[[object]]
name = "Gtk.TreeView"
status = "generate"
generate_builder = false

If there is some work which has to be done post-construction before the builder's build method returns, you can set the builder_postprocess value in the object configuration:

[[object]]
name = "Gtk.Application"
status = "generate"
generate_builder = true
builder_postprocess = "Application::register_startup_hook(&ret);"

For the duration of the code in builder_postprocess the binding ret will be the value to be returned from the build method.

Sometimes Gir understands the object definition incorrectly or the .gir file contains an incomplete or wrong definition, to fix it, you can use the full object configuration:

[[object]]
# object's fullname
name = "Gtk.SomeClass"
# can be also "manual" and "ignore" but it's simpler to just put the object in the same array
status = "generate"
# replace the parameter name for the child in child properties (instead "child")
child_name = "item"
# mark object as final type, i.e. one without any further subclasses. this
# will not generate trait SomeClassExt for this object, but implement all
# functions in impl SomeClass
final_type = true
# allow rename result file
module_name = "soome_class"
# override starting version
version = "3.12"
# prefixed object in mod.rs with #[cfg(mycond)]
cfg_condition = "mycond"
# if you want to override default option Ex. for write your own Display implementation
generate_display_trait = false
# if you want to generate builder with name SomeClassBuilder
generate_builder = true
# trust return value nullability annotations for this specific type.
# See above for details and use with care
trust_return_value_nullability = false
    # define overrides for function
    [[object.function]]
    # filter functions from object
    name = "set_website_label"
    # alternative way to apply override for many functions. Will be used with '^' and '$' on both sides
    # can be used instead of `name` almost anywhere
    # pattern = "[gs]et_value"
    # don't generate function
    ignore = true
    # override starting version
    version = "3.12"
    # prefixed function with #[cfg(mycond)]
    cfg_condition = "mycond"
    # prefixed function with #[doc(hidden)]
    doc_hidden = true
    # define a list of function parameters to be ignored when the documentation is generated
    doc_ignore_parameters = ["some_user_data_param"]
    # disable length_of autodetection
    disable_length_detect = true
    # write function docs to trait other than default "xxxExt",
    # also works in [object.signal] and [object.property]
    doc_trait_name = "SocketListenerExtManual"
    # disable generation of future for async function
    no_future = true
    # to rename the generated function
    rename = "something_else"
    # to override the default safety assertions: "none", "skip",
    # "not-initialized", "in-main-thread"
    assertion = "in-main-thread"
        # override for parameter
        [[object.function.parameter]]
        # filter by name
        name = "website_label"
        # allow to remove/add Option<>
        nullable = true
        # allow to make parameter immutable
        const = true
        # parameter is calculated as length of string or array and removed from function declaration
        # (for length of return value use "return")
        length_of = "str"
        # change string type. Variants: "utf8", "filename", "os_string"
        string_type = "os_string"
        # make function unsafe to call (emits `fn unsafe`)
        unsafe = true

        # override for return value
        [object.function.return]
        # allow to remove/add Option<> to return value
        nullable = true
        # convert bool return types to Result<(), glib::BoolError> with
        # the given error message on failure
        bool_return_is_error = "Function failed doing what it is supposed to do"
        # convert Option return types to Result<T, glib::BoolError> with
        # the given error message on failure
        nullable_return_is_error = "Function failed doing what it is supposed to do"
        # always include the return value of throwing functions in the returned Result<...>,
        # without this option bool and guint return values are assumed to indicate success or error,
        # and are not included in the returned Result<...>
        use_return_for_result = true
        # change string type. Variants: "utf8", "filename", "os_string"
        string_type = "os_string"
        # overwrite type
        type = "Gtk.Widget"

            # Override callback's parameter
            [[object.function.parameter.callback_parameter]]
            name = "name_of_the_callback_parameter"
            nullable = true
    [[object.signal]]
    name = "activate-link"
    # replace trampoline bool return type with `Inhibit`
    inhibit = true
    ignore = true
    version = "3.10"
    doc_hidden = true
        [[object.signal.parameter]]
        name = "path_string"
        # allow to use different names in closure
        new_name = "path"
        # can be also "borrow" and "none": Add some transformation between ffi trampoline parameters and rust closure
        transformation = "treepath"
        nullable = true
        [object.signal.return]
        nullable = true
    # override for properties
    [[object.property]]
    name = "baseline-position"
    version = "3.10"
    ignore = true
    [[object.property]]
    name = "events"
    # generate only `connect_property_events_notify`, without `get_property_events` and `set_property_events`
    # supported values: "get", "set", "notify"
    generate = ["notify"]

Since there are no child properties in .gir files, it needs to be added for classes manually:

[[object]]
name = "Gtk.SomeClassWithChildProperties"
status = "generate"
# replace parameter name for child in child properties (instead of "child")
child_name = "item"
# define concrete child type (instead of "Widget")
child_type = "Gtk.MenuItem"
    [[object.child_prop]]
    name = "position"
    type = "gint"
    doc_hidden = true

For enumerations and bitflags, you can configure the members and mark the type as #[must_use]:

[[object]]
name = "Gdk.EventType"
status = "generate"
# generates #[must_use] attribute for the type
must_use = true
# override starting version
version = "3.12"
    [[object.member]]
    name = "2button_press"
    # allows to skip elements with bad names, other members with same value used instead
    alias = true
    # Allow to add a cfg condition
    cfg_condition = "target_os = \"linux\""
    [[object.member]]
    name = "touchpad_pinch"
    # define starting version when member added
    version = "3.18"

For enumerations and bitflags, you can also configure additional #[derive()] clauses optionally conditioned to a cfg.

[[object]]
name = "Gst.Format"
status = "generate"
    [[object.derive]]
    name = "Serialize, Deserialize"
    cfg_condition = "feature = \"ser_de\""

Gir auto-detects copy/free or ref/unref function pairs for memory management on records. It falls back to generic g_boxed_copy/g_boxed_free if these are not found, based on an existing implementation of get_type. Otherwise no record implementation can be generated.

Some boxed types are passed as out parameters to functions and the caller is required to allocate them. For this it is necessary to provide Rust expressions in the configuration for initializing newly allocated memory for them, and to free any resources that might be stored in values of that boxed types. By default the memory is zero-initialized and it is valid to provide an empty closure like below.

[[object]]
name = "Gtk.TreeIter"
status = "generate"
init_function_expression = "|_ptr| ()"
clear_function_expression = "|_ptr| ()"

For global functions, the members can be configured by configuring the Gtk.* object:

[[object]]
name = "Gtk.*"
status = "generate"
    [[object.function]]
    name = "stock_list_ids"
    # allows to ignore global functions
    ignore = true

Which will prevent gir from generating stock_list_ids. If you want to specify that a function will be manually implemented, you can use:

[[object]]
name = "Gtk.Entry"
status = "generate"
    [[object.function]]
    name = "get_invisible_char"
    manual = true

This will prevent gir from generating get_invisible_char and it won't generate get_property_invisible_char which would have been generated if we had used "ignore = true".

Note that you must not place Gtk.* into the generate array and additionally configure its members.

You can control the generation of constants in a similar fashion:

[[object]]
name = "Gtk.*"
status = "generate"
    [[object.constant]]
    pattern = "*"
    # No constants will be generated
    ignore = true

Constants also support version and cfg_condition fields.

In various cases, GObjects or boxed types can be used from multiple threads and have certain concurrency guarantees. This can be configured with the concurrency setting at the top-level options or per object. It will automatically implement the Send and Sync traits for the resulting object and set appropriate trait bounds for signal callbacks. The default is none, and apart from that send and send+sync are supported.

[[object]]
# object's fullname
name = "Gtk.SomeClass"
# can be also "manual" and "ignore" but it's simpler to just put the object in the same array
status = "generate"
# concurrency of the object, default is set in the top-level options or
# otherwise "none". Valid values are "none", "send" and "send+sync"
concurrency = "send+sync"

Note that send is only valid for types that are either not reference counted (i.e. clone() copies the object) or that are read-only (i.e. no API for mutating the object exists). send+sync is valid if the type can be sent to different threads and all API allows simultaneous calls from different threads due to internal locking via e.g. a mutex.

[[object]]
name = "Gtk.Something"
status = "manual"
# Can also be "ref-mut", "ref-immut"
ref_mode = "ref"

When manually generating bindings, it can happen that the reference mode detected by GIR is different than what was implemented and conversion to the C types are wrong in autogenerated functions that have such objects as argument. This can be overridden with the ref_mode configuration.

Getters are automatically renamed to comply with Rust codying style guidelines. However, this can cause name clashes with existing functions. If you want to bypass the automatic renaming mechanism, use bypass_auto_rename = true:

[[object]]
name = "Gtk.TextBuffer"
[...]
    [[object.function]]
    name = "get_insert"
    # Avoid clash with the `insert` operation.
    bypass_auto_rename = true

Some constructors are not annotated as constructor in the gir files. In order for the naming convention to be applied, you can force a function to be considered as a constructor:

[[object.function]]
name = "new_for_path"
# Not annotated as constructor in Gir => force it to apply naming convention
constructor = true

conversion_type "Option"

The conversion_type variant Option is available for types T implementing glib::TryFromGlib<Error=GlibNoneError>. As a reminder, this allows implementing FromGlib for Option<T> and usually goes alongside with ToGlib for both T and Option<T>. In this case, Option<T> will be used for return values (including ffi output arguments). For in-arguments, except if the parameter is declared mandatory, impl Into<Option<T>> so that either an Option<T> or T can be used.

Ex. from gstreamer-rs:


#![allow(unused)]
fn main() {
[[object]]
name = "Gst.ClockTime"
status = "manual"
conversion_type = "Option"
}

The type ClockTime implements glib::TryFromGlib<Error=GlibNoneError> (and OptionToGlib), which means that its Rust representation can take advantage of Option<ClockTime>.

Additionally, the user can instruct gir to expect Some or Ok results for specific arguments or return values. E.g.:


#![allow(unused)]
fn main() {
[[object]]
name = "Gst.Clock"
status = "generate"
manual_traits = ["ClockExtManual"]
    [[object.function]]
    name = "get_calibration"
        [[object.function.parameter]]
        name = "internal"
        mandatory = true
}

In the above example, the user instructs gir to consider the internal argument (which also happens to be an out argument) with type gir Gst.ClockTime can be represented as a ClockTime without the Option. This argument is actually part of a set of output arguments. With the above gir declaration, the generated signature is the following (the implementation takes care of expecting the value to be defined):


#![allow(unused)]
fn main() {
    fn get_calibration(
        &self,
    ) -> (
        ClockTime,
        Option<ClockTime>,
        Option<ClockTime>,
        Option<ClockTime>,
    );
}

For a return value, the mandatory declaration reads:


#![allow(unused)]
fn main() {
    [[object.function]]
    name = "util_get_timestamp"
    /.../
        [object.function.return]
        always returns a value
        mandatory = true
}

conversion_type "Result"

The conversion_type variant Result is available for types T implementing glib::TryFromGlib<Error=Err> where Err is neither GlibNoneError nor GlibNoneOrInvalidError. In this case, Result<T, ErrorType> will be used for return values (including ffi output arguments) and the type itself in argument position.

In gstreamer-rs, the C type GstStateChangeReturn can represent both a successful or an error return value. In Rust, the Result enum is the idiomatic way of returning an error. In gstreamer-rs, bindings to functions returning GstStateChangeReturn had to be manually implemented so as to return Result<StateChangeSuccess, StateChangeError>. Note that in this case, the type implementing TryFromGlib is StateChangeSuccess and not GstStateChangeReturn. These functions can be auto-generated using:


#![allow(unused)]
fn main() {
[[object]]
name = "Gst.StateChangeReturn"
status = "generate"
must_use = true
    [object.conversion_type]
    variant = "Result"
    ok_type = "gst::StateChangeSuccess"
    err_type = "gst::StateChangeError"
}

Generation in API mode

To generate the Rust-user API level, The command is very similar to the previous one. It's better to not put this output in the same directory as where the FFI files are. Just run:

> cargo run --release -- -c YourGirFile.toml -d ../gir-files -o the-output-directory

Now it should be done. Just go to the output directory (so the-output-directory/auto in our case) and try to build using cargo build. Don't forget to update your dependencies in both projects: nothing much to do in the FFI/sys one but the Rust-user API level will need to have a dependency over the FFI/sys one.

Now, at your crate entry point (generally lib.rs), add the following to include all generated files:


#![allow(unused)]
fn main() {
pub use auto::*;
}

Add manual bindings alongside generated code

Unfortunately, gir isn't perfect (yet) and will certainly not be able to generate all the code on its own. So here's what a gir generated folder looks like:

- your_folder
|
|- Cargo.toml
|- src
 |
 |- lib.rs
 |- auto
  |
  |- (all files generated by gir)

You can add your manual bindings directly inside the src folder (at the same level as lib.rs). Then don't forget to reexport them. Let's say you added a Color type in a color.rs file. You need to add in lib.rs:


#![allow(unused)]
fn main() {
// We make the type public for the API users.
pub use color::Color;

mod color;
}

Crate name overrides

gir uses simple rule to convert a namespace to a crate name and it sometimes goes wrong. For example, "WebKit2WebExtension" namespace will be converted to "web_kit2_web_extension", which looks bad.

To fix it, the crate_name_overrides option can be used.

It also replaces FFI crates' name.

[crate_name_overrides]
"web_kit2_web_extension" = "webkit2_webextension"

Tutorial

Let's see how to generate Rust bindings of a GNOME library using the gir crate.

Note that the .gir files often happens that they are invalid (missing or invalid annotations for example). We have a small script to fix the .gir files we're using (and only them!) available in the gir-files repository. You can run it like this (at the same level of the .gir files you want to patch):

> sh fix.sh

All gtk-rs generated crates come in two parts: the sys part which contains all the C functions and types definitions (direct mapping, everything is unsafe) and the "high-level" part which contains the nice, completely safe and idiomatic Rust API on top of the sys part.

As an example, we'll generate the sourceview library bindings. So first, let's generate the sys part!

Generating the FFI library

First, you'll need to download gir:

> git clone https://github.com/gtk-rs/gir
> cd gir
> cargo install --path . # so we can use gir binary directly

Then the .gir files (luckily for you, we have a repository which contains all the ones you need for sourceview!):

> git clone https://github.com/gtk-rs/gir-files

If you look into gir-files, you'll see a file named GtkSource-3.0.gir. That's the one for sourceview.

Now let's create a new project for our sourceview crate:

> cargo new sourceview --lib

Then let's create a folder inside the newly created sourceview folder for the sys part:

> cd sourceview
> cargo new sourceview-sys --lib

To indicate to gir what to generate, we'll need a Gir.toml file (inside the sourceview-sys folder) containing:

[options]
library = "GtkSource"
version = "3.0"
target_path = "."
min_cfg_version = "3.0"
  • library stands for the library we want to generate.
  • version stands for the version of the library to be used.
  • target_path stands for the location where the files will be generated.
  • min_cfg_version will be the minimum version supported by the generated bindings.

You should now have a folder looking like this:

sourceview/
  |
  |---- Cargo.toml
  |---- sourceview-sys/
  |       |
  |       |---- Cargo.toml
  |       |---- Gir.toml
  |       |---- src/
  |               |
  |               |---- lib.rs
  |---- src/
          |
          |---- lib.rs

Let's generate the sys crate now:

> cd sourceview-sys
> # Run gir in "sys" mode (the "-m" option) and we give the gir files path (the "-d" option)
> gir -m sys -d ../../gir-files/

(In case a failure happens at this point, and you can't figure out what's going on, don't hesitate to reach us so we can give you a hand!)

You should now see new files (and a new folder):

  • build.rs
  • Cargo.toml
  • src/lib.rs
  • tests/

Now let's try to build it:

> cargo build

Surprise! It doesn't build at all and you should see a loooooot of errors. Well, that was expected. We need to add some dependencies (you can find which ones in the .gir files) in order to make it work. Let's update our Gir.toml file to make it look like this:

[options]
library = "GtkSource"
version = "3.0"
target_path = "."
min_cfg_version = "3.0"

external_libraries = [
    "Cairo",
    "Gdk",
    "GdkPixbuf",
    "Gio",
    "GLib",
    "GObject",
    "Gtk",
]

Now we regenerate it then rebuild it:

> rm Cargo.* # we remove Cargo files
> gir -m sys -d ../../gir-files/
> cargo build

Should work just fine!

We can cleanup the command line a bit now. You can actually give the work mode ("-m" option) and the gir files repository through the Gir.toml file using "work_mode" and "girs_dir" options:

[options]
library = "GtkSource"
version = "3.0"
target_path = "."
min_cfg_version = "3.0"
work_mode = "sys"
girs_dir = "../../gir-files/"

external_libraries = [
    "Cairo",
    "Gdk",
    "GdkPixbuf",
    "Gio",
    "GLib",
    "GObject",
    "Gtk",
]

Now, if you want to regenerate, just run:

> gir

Now we have a working sys containing all functions and objects definition. Just to be sure everything was correctly generated, we can run some tests (graciously generated by gir as well):

> cargo test

Normally, all tests passed. If you get an error when running those tests, it's very likely that the sys generation is invalid and/or incomplete.

Time to generate the high-level Rust API!

Generating the Rust API

Time to go back to the "global" sourceview folder:

> cd ..

As you certainly guessed, we'll need a new Gir.toml file. Let's write it:

[options]
girs_dir = "../gir-files"
library = "GtkSource"
version = "3.0"
min_cfg_version = "3.0"
target_path = "."
work_mode = "normal"
generate_safety_asserts = true
deprecate_by_min_version = true
single_version_file = true

generate = []

A few new things in here. Let's take a look at them:

  • work_mode value is now set to normal, it means it'll generate the high-level Rust api instead of the sys-level.
  • generate_safety_asserts is used to generates checks to ensure that, or any other kind of initialization needed before being able to use the library.
  • deprecate_by_min_version is used to generate a Rust "#[deprecated]" attribute based on the deprecation information provided by the .gir file.
  • single_version_file is a very useful option when you have a lot of generated files (like we'll have). Instead of generating the gir hash commit used for the generation in the header of all generated files, it'll just write it inside one file, removing git diff noise a lot.
  • generate = []: this line currently does nothing. We say to gir to generate nothing. We'll fulfill it later on.

Let's make a first generation of our high-level Rust API!

> gir

Now if you take a look around, you'll see a new "auto" folder inside "src". Doesn't contain much though. Which makes sense since we're generating nothing. Time to introduce you to a whole new gir mode: not_bound. Let's give it a try:

> gir -m not_bound
[NOT GENERATED] GtkSource.Buffer
[NOT GENERATED PARENT] Gtk.TextBuffer
[NOT GENERATED] GtkSource.Language
[NOT GENERATED] GtkSource.Mark
[NOT GENERATED PARENT] Gtk.TextMark
[NOT GENERATED] GtkSource.StyleScheme
[NOT GENERATED] GtkSource.UndoManager
[NOT GENERATED] GtkSource.SortFlags
[NOT GENERATED] GtkSource.Completion
[NOT GENERATED PARENT] Gtk.Buildable
[NOT GENERATED] GtkSource.CompletionProvider
[NOT GENERATED] GtkSource.CompletionContext
[NOT GENERATED PARENT] GObject.InitiallyUnowned
[NOT GENERATED] GtkSource.CompletionInfo
[NOT GENERATED PARENT] Gtk.Window
[NOT GENERATED PARENT] Gtk.Bin
[NOT GENERATED PARENT] Gtk.Container
[NOT GENERATED PARENT] Gtk.Widget
[NOT GENERATED PARENT] Atk.ImplementorIface
[NOT GENERATED] GtkSource.View
[NOT GENERATED PARENT] Gtk.TextView
[NOT GENERATED PARENT] Gtk.Scrollable
[NOT GENERATED] GtkSource.CompletionActivation
[NOT GENERATED] GtkSource.CompletionProposal
[NOT GENERATED] GtkSource.CompletionError
[NOT GENERATED] GtkSource.CompletionItem
[NOT GENERATED PARENT] GtkSource.CompletionProposal
[NOT GENERATED] GtkSource.CompletionWords
[NOT GENERATED PARENT] GtkSource.CompletionProvider
[NOT GENERATED] GtkSource.DrawSpacesFlags (deprecated in 3.24)
[NOT GENERATED] GtkSource.Encoding
[NOT GENERATED] GtkSource.File
[NOT GENERATED] GtkSource.MountOperationFactory
[NOT GENERATED] GtkSource.FileLoader
[NOT GENERATED] GtkSource.FileLoaderError
[NOT GENERATED] GtkSource.FileSaver
[NOT GENERATED] GtkSource.FileSaverFlags
[NOT GENERATED] GtkSource.FileSaverError
[NOT GENERATED] GtkSource.Gutter
[NOT GENERATED] GtkSource.GutterRenderer
[NOT GENERATED] GtkSource.GutterRendererState
[NOT GENERATED] GtkSource.GutterRendererAlignmentMode
[NOT GENERATED] GtkSource.GutterRendererPixbuf
[NOT GENERATED PARENT] GtkSource.GutterRenderer
[NOT GENERATED] GtkSource.GutterRendererText
[NOT GENERATED] GtkSource.LanguageManager
[NOT GENERATED] GtkSource.Map
[NOT GENERATED PARENT] GtkSource.View
[NOT GENERATED] GtkSource.MarkAttributes
[NOT GENERATED] GtkSource.PrintCompositor
[NOT GENERATED] GtkSource.Region
[NOT GENERATED] GtkSource.RegionIter
[NOT GENERATED] GtkSource.SearchContext
[NOT GENERATED] GtkSource.SearchSettings
[NOT GENERATED] GtkSource.Style
[NOT GENERATED] GtkSource.SpaceDrawer
[NOT GENERATED] GtkSource.SpaceTypeFlags
[NOT GENERATED] GtkSource.SpaceLocationFlags
[NOT GENERATED] GtkSource.StyleSchemeChooser
[NOT GENERATED] GtkSource.StyleSchemeChooserButton
[NOT GENERATED PARENT] Gtk.Button
[NOT GENERATED PARENT] Gtk.Actionable
[NOT GENERATED PARENT] Gtk.Activatable
[NOT GENERATED PARENT] GtkSource.StyleSchemeChooser
[NOT GENERATED] GtkSource.StyleSchemeChooserInterface
[NOT GENERATED] GtkSource.StyleSchemeChooserWidget
[NOT GENERATED] GtkSource.StyleSchemeManager
[NOT GENERATED] GtkSource.Tag
[NOT GENERATED PARENT] Gtk.TextTag
[NOT GENERATED] GtkSource.ViewGutterPosition

We now have the list of all the non-yet generated items. Quite convenient! You can also see that we have two kinds of not generated items:

  • [NOT GENERATED]
  • [NOT GENERATED PARENT]

[NOT GENERATED PARENT] means that this object lives in a dependency of the current library. We'll come back on how to add them a bit later.

Let's start by generating one type. Let's update the "generate" array as follows:

generate = [
    "GtkSource.Language",
]

Another gir run:

> gir

(Again, if you do it on another library and it fails and you can't figure out why, don't hesitate to reach us!)

We now have a src/auto/language.rs file. We need to include all auto files in our library. To do so, let's update the src/lib.rs file as follows:


#![allow(unused)]
fn main() {
pub use auto::*;

mod auto;
}

Let's compile:

> cargo build

It completely failed with a lot of errors. Yeay!

You guessed it, we need to add a few dependencies to make it work. A lot of those errors were about the fact that the Language type didn't exist. Which is weird since we generated it, right? Well, if you take a look at the src/auto/language.rs file, you'll see this at the top:


#![allow(unused)]
fn main() {
glib_wrapper! {
    pub struct Language(Object<gtk_source_sys::GtkSourceLanguage, gtk_source_sys::GtkSourceLanguageClass, LanguageClass>);

    match fn {
        get_type => || gtk_source_sys::gtk_source_language_get_type(),
    }
}
}

This macro comes from the glib crate. We didn't import it, therefore the Rust compiler can't find it. We'll also need its sys part (the case of glib is a bit special).

Another issue that will arise is that we didn't import the sourceview-sys crate either. Alongside those two (three if we count glib-sys!), we'll need both libc and bitflags. Let's fix all of those issues at once! For that, we need to update the Cargo.toml:

[package]
name = "sourceview"
version = "0.1.0"
authors = ["Guillaume Gomez <guillaume1.gomez@gmail.com>"]

[dependencies]
libc = "0.2"
bitflags = "1.0"

[dependencies.gtk-source-sys]
path = "./sourceview-sys"

[dependencies.glib]
git = "https://github.com/gtk-rs/glib"

[dependencies.glib-sys]
git = "https://github.com/gtk-rs/sys" # all gtk-rs sys crates are in the sys repository

And to import those crates into src/lib.rs:


#![allow(unused)]
fn main() {
#[macro_use]
extern crate glib;
extern crate glib_sys;
extern crate gtk_source_sys;

extern crate libc;
#[macro_use]
extern crate bitflags;

pub use auto::*;

mod auto;
}

Let's try to rebuild:

> cargo build

It worked! We have generated the Language item! I'll let you take a look at the src/auto/language.rs file, then we can continue.

Again, if you encounter any issue at this stage (if the generated code is invalid for example), don't hesitate to reach us so we can give you a hand!

We'll now generate the GtkSource.Region type. Why this one? Well, I don't want to spoil the surprise so just wait for a bit!

First, we need to add it into the types to generate into our Gir.toml file:

generate = [
    "GtkSource.Language",
    "GtkSource.Region",
]

We regenerate:

> gir

We rebuild:

> cargo build

Everything works, yeay! Now if we take a look at our newly generated src/auto/region.rs, we'll see code like this:


#![allow(unused)]
fn main() {
//#[cfg(any(feature = "v3_22", feature = "dox"))]
//fn add_subregion(&self, _start: /*Ignored*/&gtk::TextIter, _end: /*Ignored*/&gtk::TextIter);

//#[cfg(any(feature = "v3_22", feature = "dox"))]
//fn get_buffer(&self) -> /*Ignored*/Option<gtk::TextBuffer>;
}

Some functions are commented. Why so? The reason is simple: we need to tell to gir that those types have been generated and that it can generate code using them. We can do it by adding the type into the "manual" list. To put it simply, when gir sees an item into this "manual" list, it means to it "this type has been generated somewhere else, you can use it just like the others".

Let's update our Gir.toml file once again:

generate = [
    "GtkSource.Language",
    "GtkSource.Region",
]

manual = [
    "Gtk.TextIter",
    "Gtk.TextBuffer",
]

We'll also need to import the gtk crate. Let's add it into our Cargo.toml file:

[dependencies.gtk]
git = "https://github.com/gtk-rs/gtk"

And import it into our src/lib.rs:


#![allow(unused)]
fn main() {
extern crate gtk;
}

We regenerate and rebuild:

> gir
> cargo build

Everything is working, yeay! If you take another look at src/auto/region.rs, you'll see a lot less commented functions. Amongst the remaining ones, you'll see this one:


#![allow(unused)]
fn main() {
//#[cfg(any(feature = "v3_22", feature = "dox"))]
//fn get_start_region_iter(&self, iter: /*Ignored*/RegionIter);
}

If a type name isn't prepend by [crate_name]::, then it means it comes from the current crate. To add it, just put it into the "generate" list of Gir.toml.

At this point, you should have almost everything you need. There is just one last case we need to talk about.

Handling generation errors

There are a few kinds of errors (not much luckily) which can happen with gir generation. Let's take a look at them.

Missing memory management functions

If gir generation fails (for whatever reason), it means you'll have to implement the type yourself. Just like types from other gtk-rs crates, you'll need to put it into the "manual" list. Then you need to put the type into the src folder (or inside a subfolder, you know how Rust works).

/!\ Don't forget to reexport the type inside your src/lib.rs file! For example, let's take a look at the requisition.rs file from the gtk crate.

Since it's a "simple" type (no pointer, therefore no memory management to do), gir doesn't know how to generate it. You'll need to implement some traits by hand like ToGlibPtr or ToGlibPtrMut (depending on your needs).

Bad function generation

In some cases, the generated code isn't correct (array parameters are often an issue). In such cases, it's better to just make the implementation yourself. As an example, let's say you want to implement Region::is_empty yourself. A few changes have to be made. Let's start with Gir.toml:

generate = [
    "GtkSource.Language",
]

[[object]]
name = "GtkSource.Region"
status = "generate"
    [[object.function]]
    name = "is_empty"
    ignore = true

So to sum up what I wrote above: we removed "GtkSource.Region" from the "generate" list and we created a new entry for it. Then we say to gir that it should generate (through status = "generate"). However, we also tell it that we don't want the "is_empty" to be generated.

Now that we've done that, we need to reimplement it. Let's create a src/region.rs file:


#![allow(unused)]
fn main() {
use glib::object::IsA;
use glib::translate::*;
use Region;

pub trait RegionExtManual: 'static {
    pub fn is_empty(&self) -> bool;
}

impl<O: IsA<Region>> RegionExtManual for O {
    pub fn is_empty(&self) -> bool {
        // blablabla
        true
    }
}
}

You might wonder: "why not just implementing it on the Region type directly?". Because like this, a subclass will also be able to use this trait easily as long as it implements IsA<Region>. For instance, in gtk, everything that implements IsA<Widget> (so almost every GTK types) can use those methods.

As usual, don't forget to reexport the trait. A little tip about reexporting manual traits: in gtk-rs, we create a src/prelude.rs file which reexports all traits (both manual and generated ones), making it simpler for users to use them through use [DEPENDENCY]::prelude::*. It looks like this:


#![allow(unused)]
fn main() {
pub use auto::traits::*;

pub use region::RegionExtManual;
}

Then it's reexported as follows from the src/lib.rs file:


#![allow(unused)]
fn main() {
pub mod prelude;

pub use prelude::*;
}

Generating documentation

And finally the last feature! Just run the following command (note the -m doc at the end):

> cargo run --release -- -c YourGirFile.toml -d ../gir-files --doc-target-path the-output-file-name -m doc

It'll generate a markdown file if everything went fine. That's where all this crate's documentation is. If you want to put it back into your crate's source code like "normal" doc comments, run:

> cargo install rustdoc-stripper
> rustdoc-stripper -g -o docs.md

And now your crate should be completely documented as expected!

If you defining traits manually you can add them to "Implements" section for classes and interfaces:

[[object]]
name = "Gtk.Assistant"
status = "generate"
#add link to trait from current crate
manual_traits = ["AssistantExtManual"]

[[object]]
name = "Gtk.Application"
status = "generate"
#add link to trait from other crate
manual_traits = ["gio::ApplicationExtManual"]