├── .gitignore ├── LICENSE ├── README.md ├── binding-overrides └── cairo1.zig ├── build.zig ├── build.zig.zon ├── doc └── binding-strategy.md ├── example ├── build.zig ├── build.zig.zon └── src │ ├── custom_class.zig │ ├── custom_drawing.zig │ ├── hello_world.zig │ ├── list_view.zig │ ├── main.zig │ └── pango_cairo.zig ├── extensions ├── glib2.zig ├── gobject2.zig └── gtk4.zig ├── flatpak-env.sh ├── gir-fixes ├── Adw-1.xslt ├── GLib-2.0.xslt ├── GObject-2.0.xslt ├── Gck-1.xslt ├── Gck-2.xslt ├── Gee-0.8.xslt ├── Gio-2.0.xslt ├── Gst-1.0.xslt ├── GstController-1.0.xslt ├── GstGLEGL-1.0.xslt ├── GstVideo-1.0.xslt ├── WebKit2-4.1.xslt ├── WebKit2WebExtension-4.1.xslt └── freetype2-2.0.xslt ├── src ├── build │ ├── build_base.zig │ └── build_gresources_xml.zig ├── compat.zig ├── gir.zig ├── main.zig ├── translate.zig └── zig_writer.zig └── test ├── build.zig └── build.zig.zon /.gitignore: -------------------------------------------------------------------------------- 1 | *.tar.zst 2 | test/abi 3 | .zig-cache 4 | zig-cache 5 | zig-out 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Ian Johnson 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 8 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 10 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 11 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 12 | THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zig-gobject 2 | 3 | Bindings for GObject-based libraries (such as GTK) generated using GObject 4 | introspection data. 5 | 6 | ## Usage 7 | 8 | To use the bindings, find the [latest release of this 9 | project](https://github.com/ianprime0509/zig-gobject/releases) and add the 10 | desired bindings artifact as a dependency in `build.zig.zon`. Then, the exposed 11 | bindings can be used as modules. For example: 12 | 13 | ```zig 14 | const gobject = b.dependency("gobject", .{}); 15 | exe.root_module.addImport("gtk", gobject.module("gtk4")); 16 | exe.root_module.addImport("adw", gobject.module("adw1")); 17 | ``` 18 | 19 | The binding generator and generated bindings are tested on Zig 0.14.0 and 20 | master, though support for the latest master may temporarily regress when 21 | breaking changes are made upstream. 22 | 23 | ## Companion projects 24 | 25 | - [zig-libintl](https://github.com/ianprime0509/zig-libintl) - libintl bindings 26 | for Zig, which provide `gettext` functions for internationalization. 27 | - [Nonograms](https://github.com/ianprime0509/nonograms) - a full application 28 | written using these bindings. 29 | 30 | ## Examples 31 | 32 | There are several examples in the `example` directory, which is itself a 33 | runnable project (depending on the `bindings` directory as a dependency). After 34 | generating the bindings, the examples can be run using `zig build run` in the 35 | `example` directory. 36 | 37 | ## Development environment 38 | 39 | The bindings generated by this project cover a wide variety of libraries, and it 40 | can be annoying and inconvenient to install these libraries on a host system for 41 | testing purposes. The best way to get a consistent environment for testing is to 42 | use [Flatpak](https://flatpak.org/): 43 | 44 | 1. Install `flatpak`. 45 | 2. Install the GNOME SDK: `flatpak install org.gnome.Sdk//48` 46 | 47 | The steps above only need to be done once per GNOME SDK version. To enter a 48 | development environment: 49 | 50 | 1. Run `flatpak run --filesystem=home --share=network --share=ipc --socket=fallback-x11 --socket=wayland --device=dri --socket=session-bus org.gnome.Sdk//48` 51 | - `--filesystem=home` - makes the user's home directory available within the 52 | container 53 | - `--share=network` - allows network access (needed to fetch `build.zig.zon` 54 | dependencies) 55 | - `--share=ipc --socket=fallback-x11 --socket=wayland --device=dri` - allows 56 | graphical display through X11 or Wayland 57 | - `--socket=session-bus` - allows access to the session bus 58 | - For convenience, this command is available as a script in this repository: 59 | `flatpak-env.sh`. 60 | 2. Within the spawned shell, you can use the latest master version of Zig 61 | downloaded from ziglang.org. Since the downloaded Zig is statically linked, 62 | it is usable within the Flatpak environment with no additional setup. 63 | 64 | ## Running the binding generator 65 | 66 | The binding generator can be invoked using `zig build codegen`, which accepts 67 | several useful options and is described further below, or by building the 68 | `translate-gir` binary using `zig build` and invoking it directly. 69 | 70 | `zig build codegen` requires a set of modules to be used as input. The input 71 | modules can be specified using `-Dmodules` to provide an explicit list of root 72 | modules for codegen (the codegen process will also discover any necessary 73 | dependencies): for example, `zig build codegen -Dmodules=Gtk-4.0` will generate 74 | bindings for GTK 4 and anything else it depends on (Gio, GObject, GLib, and many 75 | others). 76 | 77 | Alternatively, if a Flatpak development environment is set up (see the section 78 | above), a predefined GIR profile can be selected using `-Dgir-profile`, which 79 | includes all the modules available in a specific GNOME SDK. The predefined 80 | profiles track the latest two GNOME releases. 81 | 82 | GIR files are assumed to be located in `/usr/share/gir-1.0` unless this is 83 | overridden via `-Dgir-files-path`. 84 | 85 | The bindings are generated to the `bindings` directory under the build prefix 86 | (by default, `zig-out`). 87 | 88 | ### Fixing broken GIR files 89 | 90 | Sometimes, there are errors in GIR files which result in incorrect or incomplete 91 | bindings. The codegen process can handle this via XSLT stylesheets, which are 92 | named after the modules whose GIR files they correct. This project maintains 93 | stylesheets fixing known GIR issues in `gir-fixes`. 94 | 95 | The XSLT stylesheets are applied using the `xsltproc` program, which is part of 96 | the libxslt project. At this time, this is a system command dependency; there is 97 | no support yet for building xsltproc from source due to 98 | https://github.com/ianprime0509/zig-libxml2/issues/1 99 | 100 | ### Writing bindings by hand 101 | 102 | While the binding generator is capable of generating good bindings from GIR 103 | input, it is sometimes necessary or desirable to bypass GIR for everything 104 | except build-related metadata (library dependencies, etc.) and write Zig 105 | bindings by hand. This is the strategy taken for Cairo, using the Cairo bindings 106 | in `binding-overrides`. 107 | 108 | Any manual binding files present in `binding-overrides` will cause codegen of 109 | bindings to be skipped for the corresponding modules, using the manual bindings 110 | instead. 111 | 112 | ## Running tests 113 | 114 | `zig build test` will run the binding generator's tests. If bindings have been 115 | generated, the `test` project directory contains a package depending on them 116 | which runs tests on the generated bindings. The `zig build test` command in the 117 | `test` project accepts the same `modules` and `gir-profile` options as the 118 | codegen command to specify which modules to test (unlike codegen, the `modules` 119 | option here specifies a complete list of modules to test: there is no discovery 120 | and testing of dependency modules). 121 | 122 | ## Further reading 123 | 124 | - [Binding strategy](./doc/binding-strategy.md) 125 | 126 | ## License 127 | 128 | This project is released under the [Zero-Clause BSD 129 | License](https://spdx.org/licenses/0BSD.html). The libraries exposed by the 130 | generated bindings are subject to their own licenses. 131 | -------------------------------------------------------------------------------- /binding-overrides/cairo1.zig: -------------------------------------------------------------------------------- 1 | extern fn cairo_version() c_int; 2 | pub const version = cairo_version; 3 | 4 | pub const DestroyFunc = *const fn (data: ?*anyopaque) callconv(.c) void; 5 | pub const WriteFunc = *const fn (closure: ?*anyopaque, data: [*]const u8, length: c_uint) callconv(.c) Status; 6 | pub const ReadFunc = *const fn (closure: ?*anyopaque, data: [*]u8, length: c_uint) callconv(.c) Status; 7 | 8 | pub const Status = enum(c_int) { 9 | success, 10 | 11 | no_memory, 12 | invalid_restore, 13 | invalid_pop_group, 14 | no_current_point, 15 | invalid_matrix, 16 | invalid_status, 17 | null_pointer, 18 | invalid_string, 19 | invalid_path_data, 20 | read_error, 21 | write_error, 22 | surface_finished, 23 | surface_type_mismatch, 24 | pattern_type_mismatch, 25 | invalid_content, 26 | invalid_format, 27 | invalid_visual, 28 | file_not_found, 29 | invalid_dash, 30 | invalid_dsc_comment, 31 | invalid_index, 32 | clip_not_representable, 33 | temp_file_error, 34 | invalid_stride, 35 | font_type_mismatch, 36 | user_font_immutable, 37 | user_font_error, 38 | negative_count, 39 | invalid_clusters, 40 | invalid_slant, 41 | invalid_weight, 42 | invalid_size, 43 | user_font_not_implemented, 44 | device_type_mismatch, 45 | device_error, 46 | invalid_mesh_construction, 47 | device_finished, 48 | jbig2_global_missing, 49 | png_error, 50 | freetype_error, 51 | win32_gdi_error, 52 | tag_error, 53 | dwrite_error, 54 | svg_font_error, 55 | _, 56 | 57 | extern fn cairo_status_to_string(status: Status) [*:0]const u8; 58 | pub const toString = cairo_status_to_string; 59 | }; 60 | 61 | pub const UserDataKey = extern struct { 62 | unused: c_int, 63 | }; 64 | 65 | pub const RectangleInt = extern struct { 66 | x: c_int, 67 | y: c_int, 68 | width: c_int, 69 | height: c_int, 70 | }; 71 | 72 | pub const Rectangle = extern struct { 73 | x: f64, 74 | y: f64, 75 | width: f64, 76 | height: f64, 77 | }; 78 | 79 | pub const RectangleList = extern struct { 80 | status: Status, 81 | rectangles: [*]Rectangle, 82 | num_rectangles: c_int, 83 | 84 | extern fn cairo_rectangle_list_destroy(rectangle_list: *RectangleList) void; 85 | pub const destroy = cairo_rectangle_list_destroy; 86 | }; 87 | 88 | pub const Matrix = extern struct { 89 | xx: f64, 90 | yx: f64, 91 | xy: f64, 92 | yy: f64, 93 | x0: f64, 94 | y0: f64, 95 | 96 | extern fn cairo_matrix_init(matrix: *Matrix, xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) void; 97 | pub const init = cairo_matrix_init; 98 | 99 | extern fn cairo_matrix_init_identity(matrix: *Matrix) void; 100 | pub const initIdentity = cairo_matrix_init_identity; 101 | 102 | extern fn cairo_matrix_init_translate(matrix: *Matrix, tx: f64, ty: f64) void; 103 | pub const initTranslate = cairo_matrix_init_translate; 104 | 105 | extern fn cairo_matrix_init_scale(matrix: *Matrix, sx: f64, sy: f64) void; 106 | pub const initScale = cairo_matrix_init_scale; 107 | 108 | extern fn cairo_matrix_init_rotate(matrix: *Matrix, radians: f64) void; 109 | pub const initRotate = cairo_matrix_init_rotate; 110 | 111 | extern fn cairo_matrix_translate(matrix: *Matrix, tx: f64, ty: f64) void; 112 | pub const translate = cairo_matrix_translate; 113 | 114 | extern fn cairo_matrix_scale(matrix: *Matrix, sx: f64, sy: f64) void; 115 | pub const scale = cairo_matrix_scale; 116 | 117 | extern fn cairo_matrix_rotate(matrix: *Matrix, radians: f64) void; 118 | pub const rotate = cairo_matrix_rotate; 119 | 120 | extern fn cairo_matrix_invert(matrix: *Matrix) Status; 121 | pub const invert = cairo_matrix_invert; 122 | 123 | extern fn cairo_matrix_multiply(result: *Matrix, a: *const Matrix, b: *const Matrix) void; 124 | pub const multiply = cairo_matrix_multiply; 125 | 126 | extern fn cairo_matrix_transform_distance(matrix: *const Matrix, dx: *f64, dy: *f64) void; 127 | pub const transformDistance = cairo_matrix_transform_distance; 128 | 129 | extern fn cairo_matrix_transform_point(matrix: *const Matrix, x: *f64, y: *f64) void; 130 | pub const transformPoint = cairo_matrix_transform_point; 131 | }; 132 | 133 | pub const Context = opaque { 134 | extern fn cairo_create(target: *Surface) *Context; 135 | pub const create = cairo_create; 136 | 137 | extern fn cairo_reference(cr: *Context) *Context; 138 | pub const reference = cairo_reference; 139 | 140 | extern fn cairo_destroy(cr: *Context) void; 141 | pub const destroy = cairo_destroy; 142 | 143 | extern fn cairo_get_reference_count(cr: *Context) c_uint; 144 | pub const getReferenceCount = cairo_get_reference_count; 145 | 146 | extern fn cairo_get_user_data(cr: *Context, key: *const UserDataKey) ?*anyopaque; 147 | pub const getUserData = cairo_get_user_data; 148 | 149 | extern fn cairo_set_user_data(cr: *Context, key: *const UserDataKey, user_data: ?*anyopaque, destroy: DestroyFunc) Status; 150 | pub const setUserData = cairo_set_user_data; 151 | 152 | extern fn cairo_save(cr: *Context) void; 153 | pub const save = cairo_save; 154 | 155 | extern fn cairo_restore(cr: *Context) void; 156 | pub const restore = cairo_restore; 157 | 158 | extern fn cairo_push_group(cr: *Context) void; 159 | pub const pushGroup = cairo_push_group; 160 | 161 | extern fn cairo_push_group_with_content(cr: *Context, content: Content) void; 162 | pub const pushGroupWithContent = cairo_push_group_with_content; 163 | 164 | extern fn cairo_pop_group(cr: *Context) *Pattern; 165 | pub const popGroup = cairo_pop_group; 166 | 167 | extern fn cairo_pop_group_to_source(cr: *Context) void; 168 | pub const popGroupToSource = cairo_pop_group_to_source; 169 | 170 | extern fn cairo_set_operator(cr: *Context, op: Operator) void; 171 | pub const setOperator = cairo_set_operator; 172 | 173 | extern fn cairo_set_source(cr: *Context, source: *Pattern) void; 174 | pub const setSource = cairo_set_source; 175 | 176 | extern fn cairo_set_source_rgb(cr: *Context, red: f64, green: f64, blue: f64) void; 177 | pub const setSourceRgb = cairo_set_source_rgb; 178 | 179 | extern fn cairo_set_source_rgba(cr: *Context, red: f64, green: f64, blue: f64, alpha: f64) void; 180 | pub const setSourceRgba = cairo_set_source_rgba; 181 | 182 | extern fn cairo_set_source_surface(cr: *Context, surface: *Surface, x: f64, y: f64) void; 183 | pub const setSourceSurface = cairo_set_source_surface; 184 | 185 | extern fn cairo_set_tolerance(cr: *Context, tolerance: f64) void; 186 | pub const setTolerance = cairo_set_tolerance; 187 | 188 | extern fn cairo_set_antialias(cr: *Context, antialias: Antialias) void; 189 | pub const setAntialias = cairo_set_antialias; 190 | 191 | extern fn cairo_set_fill_rule(cr: *Context, fill_rule: FillRule) void; 192 | pub const setFillRule = cairo_set_fill_rule; 193 | 194 | extern fn cairo_set_line_width(cr: *Context, width: f64) void; 195 | pub const setLineWidth = cairo_set_line_width; 196 | 197 | extern fn cairo_set_hairline(cr: *Context, set_hairline: c_int) void; 198 | pub const setHairline = cairo_set_hairline; 199 | 200 | extern fn cairo_set_line_cap(cr: *Context, line_cap: LineCap) void; 201 | pub const setLineCap = cairo_set_line_cap; 202 | 203 | extern fn cairo_set_line_join(cr: *Context, line_join: LineJoin) void; 204 | pub const setLineJoin = cairo_set_line_join; 205 | 206 | extern fn cairo_set_dash(cr: *Context, dashes: [*]const f64, num_dashes: c_int, offset: f64) void; 207 | pub const setDash = cairo_set_dash; 208 | 209 | extern fn cairo_set_miter_limit(cr: *Context, limit: f64) void; 210 | pub const setMiterLimit = cairo_set_miter_limit; 211 | 212 | extern fn cairo_translate(cr: *Context, tx: f64, ty: f64) void; 213 | pub const translate = cairo_translate; 214 | 215 | extern fn cairo_scale(cr: *Context, sx: f64, sy: f64) void; 216 | pub const scale = cairo_scale; 217 | 218 | extern fn cairo_rotate(cr: *Context, angle: f64) void; 219 | pub const rotate = cairo_rotate; 220 | 221 | extern fn cairo_transform(cr: *Context, matrix: *const Matrix) void; 222 | pub const transform = cairo_transform; 223 | 224 | extern fn cairo_set_matrix(cr: *Context, matrix: *const Matrix) void; 225 | pub const setMatrix = cairo_set_matrix; 226 | 227 | extern fn cairo_identity_matrix(cr: *Context) void; 228 | pub const identityMatrix = cairo_identity_matrix; 229 | 230 | extern fn cairo_user_to_device(cr: *Context, x: *f64, y: *f64) void; 231 | pub const userToDevice = cairo_user_to_device; 232 | 233 | extern fn cairo_user_to_device_distance(cr: *Context, dx: *f64, dy: *f64) void; 234 | pub const userToDeviceDistance = cairo_user_to_device_distance; 235 | 236 | extern fn cairo_device_to_user(cr: *Context, x: *f64, y: *f64) void; 237 | pub const deviceToUser = cairo_device_to_user; 238 | 239 | extern fn cairo_device_to_user_distance(cr: *Context, dx: *f64, dy: *f64) void; 240 | pub const deviceToUserDistance = cairo_device_to_user_distance; 241 | 242 | extern fn cairo_new_path(cr: *Context) void; 243 | pub const newPath = cairo_new_path; 244 | 245 | extern fn cairo_move_to(cr: *Context, x: f64, y: f64) void; 246 | pub const moveTo = cairo_move_to; 247 | 248 | extern fn cairo_new_sub_path(cr: *Context) void; 249 | pub const newSubPath = cairo_new_sub_path; 250 | 251 | extern fn cairo_line_to(cr: *Context, x: f64, y: f64) void; 252 | pub const lineTo = cairo_line_to; 253 | 254 | extern fn cairo_curve_to(cr: *Context, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) void; 255 | pub const curveTo = cairo_curve_to; 256 | 257 | extern fn cairo_arc(cr: *Context, xc: f64, yc: f64, radius: f64, angle1: f64, angle2: f64) void; 258 | pub const arc = cairo_arc; 259 | 260 | extern fn cairo_arc_negative(cr: *Context, xc: f64, yc: f64, radius: f64, angle1: f64, angle2: f64) void; 261 | pub const arcNegative = cairo_arc_negative; 262 | 263 | extern fn cairo_rel_move_to(cr: *Context, dx: f64, dy: f64) void; 264 | pub const relMoveTo = cairo_rel_move_to; 265 | 266 | extern fn cairo_rel_line_to(cr: *Context, dx: f64, dy: f64) void; 267 | pub const relLineTo = cairo_rel_line_to; 268 | 269 | extern fn cairo_rel_curve_to(cr: *Context, dx1: f64, dy1: f64, dx2: f64, dy2: f64, dx3: f64, dy3: f64) void; 270 | pub const relCurveTo = cairo_rel_curve_to; 271 | 272 | extern fn cairo_rectangle(cr: *Context, x: f64, y: f64, width: f64, height: f64) void; 273 | pub const rectangle = cairo_rectangle; 274 | 275 | extern fn cairo_close_path(cr: *Context) void; 276 | pub const closePath = cairo_close_path; 277 | 278 | extern fn cairo_path_extents(cr: *Context, x1: *f64, y1: *f64, x2: *f64, y2: *f64) void; 279 | pub const pathExtents = cairo_path_extents; 280 | 281 | extern fn cairo_paint(cr: *Context) void; 282 | pub const paint = cairo_paint; 283 | 284 | extern fn cairo_paint_with_alpha(cr: *Context, alpha: f64) void; 285 | pub const paintWithAlpha = cairo_paint_with_alpha; 286 | 287 | extern fn cairo_mask(cr: *Context, pattern: *Pattern) void; 288 | pub const mask = cairo_mask; 289 | 290 | extern fn cairo_mask_surface(cr: *Context, surface: *Surface, surface_x: f64, surface_y: f64) void; 291 | pub const maskSurface = cairo_mask_surface; 292 | 293 | extern fn cairo_stroke(cr: *Context) void; 294 | pub const stroke = cairo_stroke; 295 | 296 | extern fn cairo_stroke_preserve(cr: *Context) void; 297 | pub const strokePreserve = cairo_stroke_preserve; 298 | 299 | extern fn cairo_fill(cr: *Context) void; 300 | pub const fill = cairo_fill; 301 | 302 | extern fn cairo_fill_preserve(cr: *Context) void; 303 | pub const fillPreserve = cairo_fill_preserve; 304 | 305 | extern fn cairo_copy_page(cr: *Context) void; 306 | pub const copyPage = cairo_copy_page; 307 | 308 | extern fn cairo_show_page(cr: *Context) void; 309 | pub const showPage = cairo_show_page; 310 | 311 | extern fn cairo_in_stroke(cr: *Context, x: f64, y: f64) c_int; 312 | pub const inStroke = cairo_in_stroke; 313 | 314 | extern fn cairo_in_fill(cr: *Context, x: f64, y: f64) c_int; 315 | pub const inFill = cairo_in_fill; 316 | 317 | extern fn cairo_in_clip(cr: *Context, x: f64, y: f64) c_int; 318 | pub const inClip = cairo_in_clip; 319 | 320 | extern fn cairo_stroke_extents(cr: *Context, x1: *f64, y1: *f64, x2: *f64, y2: *f64) void; 321 | pub const strokeExtents = cairo_stroke_extents; 322 | 323 | extern fn cairo_fill_extents(cr: *Context, x1: *f64, y1: *f64, x2: *f64, y2: *f64) void; 324 | pub const fillExtents = cairo_fill_extents; 325 | 326 | extern fn cairo_reset_clip(cr: *Context) void; 327 | pub const resetClip = cairo_reset_clip; 328 | 329 | extern fn cairo_clip(cr: *Context) void; 330 | pub const clip = cairo_clip; 331 | 332 | extern fn cairo_clip_preserve(cr: *Context) void; 333 | pub const clipPreserve = cairo_clip_preserve; 334 | 335 | extern fn cairo_clip_extents(cr: *Context, x1: *f64, y1: *f64, x2: *f64, y2: *f64) void; 336 | pub const clipExtents = cairo_clip_extents; 337 | 338 | extern fn cairo_copy_clip_rectangle_list(cr: *Context) *RectangleList; 339 | pub const copyClipRectangleList = cairo_copy_clip_rectangle_list; 340 | 341 | extern fn cairo_tag_begin(cr: *Context, tag_name: [*:0]const u8, attributes: [*:0]const u8) void; 342 | pub const tagBegin = cairo_tag_begin; 343 | 344 | extern fn cairo_tag_end(cr: *Context, tag_name: [*:0]const u8) void; 345 | pub const tagEnd = cairo_tag_end; 346 | 347 | extern fn cairo_select_font_face(cr: *Context, family: [*:0]const u8, slant: FontSlant, weight: FontWeight) void; 348 | pub const selectFontFace = cairo_select_font_face; 349 | 350 | extern fn cairo_set_font_size(cr: *Context, size: f64) void; 351 | pub const setFontSize = cairo_set_font_size; 352 | 353 | extern fn cairo_set_font_matrix(cr: *Context, matrix: *const Matrix) void; 354 | pub const setFontMatrix = cairo_set_font_matrix; 355 | 356 | extern fn cairo_get_font_matrix(cr: *Context, matrix: *Matrix) void; 357 | pub const getFontMatrix = cairo_get_font_matrix; 358 | 359 | extern fn cairo_set_font_options(cr: *Context, options: *const FontOptions) void; 360 | pub const setFontOptions = cairo_set_font_options; 361 | 362 | extern fn cairo_get_font_options(cr: *Context, options: *FontOptions) void; 363 | pub const getFontOptions = cairo_get_font_options; 364 | 365 | extern fn cairo_set_font_face(cr: *Context, font_face: *FontFace) void; 366 | pub const setFontFace = cairo_set_font_face; 367 | 368 | extern fn cairo_get_font_face(cr: *Context) *FontFace; 369 | pub const getFontFace = cairo_get_font_face; 370 | 371 | extern fn cairo_set_scaled_font(cr: *Context, scaled_font: *const ScaledFont) void; 372 | pub const setScaledFont = cairo_set_scaled_font; 373 | 374 | extern fn cairo_get_scaled_font(cr: *Context) *ScaledFont; 375 | pub const getScaledFont = cairo_get_scaled_font; 376 | 377 | extern fn cairo_show_text(cr: *Context, utf8: [*:0]const u8) void; 378 | pub const showText = cairo_show_text; 379 | 380 | extern fn cairo_show_glyphs(cr: *Context, glyphs: [*]const Glyph, num_glyphs: c_int) void; 381 | pub const showGlyphs = cairo_show_glyphs; 382 | 383 | extern fn cairo_show_text_glyphs(cr: *Context, utf8: [*]const u8, utf8_len: c_int, glyphs: [*]const Glyph, num_glyphs: c_int, clusters: [*]const TextCluster, num_clusters: c_int, cluster_flags: TextClusterFlags) void; 384 | pub const showTextGlyphs = cairo_show_text_glyphs; 385 | 386 | extern fn cairo_text_path(cr: *Context, utf8: [*:0]const u8) void; 387 | pub const textPath = cairo_text_path; 388 | 389 | extern fn cairo_glyph_path(cr: *Context, glyphs: [*]const Glyph, num_glyphs: c_int) void; 390 | pub const glyphPath = cairo_glyph_path; 391 | 392 | extern fn cairo_text_extents(cr: *Context, utf8: [*:0]const u8, extents: *TextExtents) void; 393 | pub const textExtents = cairo_text_extents; 394 | 395 | extern fn cairo_glyph_extents(cr: *Context, glyphs: [*]const Glyph, num_glyphs: c_int, extents: *TextExtents) void; 396 | pub const glyphExtents = cairo_glyph_extents; 397 | 398 | extern fn cairo_font_extents(cr: *Context, extents: *FontExtents) void; 399 | pub const fontExtents = cairo_font_extents; 400 | 401 | extern fn cairo_get_operator(cr: *Context) Operator; 402 | pub const getOperator = cairo_get_operator; 403 | 404 | extern fn cairo_get_source(cr: *Context) *Pattern; 405 | pub const getSource = cairo_get_source; 406 | 407 | extern fn cairo_get_tolerance(cr: *Context) f64; 408 | pub const getTolerance = cairo_get_tolerance; 409 | 410 | extern fn cairo_get_antialias(cr: *Context) Antialias; 411 | pub const getAntialias = cairo_get_antialias; 412 | 413 | extern fn cairo_get_current_point(cr: *Context, x: *f64, y: *f64) void; 414 | pub const getCurrentPoint = cairo_get_current_point; 415 | 416 | extern fn cairo_get_fill_rule(cr: *Context) FillRule; 417 | pub const getFillRule = cairo_get_fill_rule; 418 | 419 | extern fn cairo_get_line_width(cr: *Context) f64; 420 | pub const getLineWidth = cairo_get_line_width; 421 | 422 | extern fn cairo_get_hairline(cr: *Context) c_int; 423 | pub const getHairline = cairo_get_hairline; 424 | 425 | extern fn cairo_get_line_cap(cr: *Context) LineCap; 426 | pub const getLineCap = cairo_get_line_cap; 427 | 428 | extern fn cairo_get_line_join(cr: *Context) LineJoin; 429 | pub const getLineJoin = cairo_get_line_join; 430 | 431 | extern fn cairo_get_miter_limit(cr: *Context) f64; 432 | pub const getMiterLimit = cairo_get_miter_limit; 433 | 434 | extern fn cairo_get_dash_count(cr: *Context) c_int; 435 | pub const getDashCount = cairo_get_dash_count; 436 | 437 | extern fn cairo_get_dash(cr: *Context, dashes: ?[*]f64, offset: ?*f64) void; 438 | pub const getDash = cairo_get_dash; 439 | 440 | extern fn cairo_get_matrix(cr: *Context, matrix: *Matrix) void; 441 | pub const getMatrix = cairo_get_matrix; 442 | 443 | extern fn cairo_get_target(cr: *Context) *Surface; 444 | pub const getTarget = cairo_get_target; 445 | 446 | extern fn cairo_get_group_target(cr: *Context) *Surface; 447 | pub const getGroupTarget = cairo_get_group_target; 448 | 449 | extern fn cairo_copy_path(cr: *Context) *Path; 450 | pub const copyPath = cairo_copy_path; 451 | 452 | extern fn cairo_copy_path_flat(cr: *Context) *Path; 453 | pub const copyPathFlat = cairo_copy_path_flat; 454 | 455 | extern fn cairo_status(cr: *Context) Status; 456 | pub const status = cairo_status; 457 | }; 458 | 459 | pub const Surface = opaque { 460 | extern fn cairo_surface_create_similar(other: *Surface, content: Content, width: c_int, height: c_int) *Surface; 461 | pub const createSimilar = cairo_surface_create_similar; 462 | 463 | extern fn cairo_surface_create_similar_image(other: *Surface, format: Format, width: c_int, height: c_int) *Surface; 464 | pub const createSimilarImage = cairo_surface_create_similar_image; 465 | 466 | extern fn cairo_surface_map_to_image(surface: *Surface, extents: ?*const RectangleInt) *Surface; 467 | pub const mapToImage = cairo_surface_map_to_image; 468 | 469 | extern fn cairo_surface_unmap_image(surface: *Surface, image: *Surface) void; 470 | pub const unmapImage = cairo_surface_unmap_image; 471 | 472 | extern fn cairo_surface_create_for_rectangle(target: *Surface, x: f64, y: f64, width: f64, height: f64) *Surface; 473 | pub const createForRectangle = cairo_surface_create_for_rectangle; 474 | 475 | extern fn cairo_surface_create_observer(target: *Surface, mode: SurfaceObserverMode) *Surface; 476 | pub const createObserver = cairo_surface_create_observer; 477 | 478 | extern fn cairo_surface_observer_add_paint_callback(abstract_surface: *Surface, func: SurfaceObserverCallback, data: ?*anyopaque) Status; 479 | pub const observerAddPaintCallback = cairo_surface_observer_add_paint_callback; 480 | 481 | extern fn cairo_surface_observer_add_mask_callback(abstract_surface: *Surface, func: SurfaceObserverCallback, data: ?*anyopaque) Status; 482 | pub const observerAddMaskCallback = cairo_surface_observer_add_mask_callback; 483 | 484 | extern fn cairo_surface_observer_add_fill_callback(abstract_surface: *Surface, func: SurfaceObserverCallback, data: ?*anyopaque) Status; 485 | pub const observerAddFillCallback = cairo_surface_observer_add_fill_callback; 486 | 487 | extern fn cairo_surface_observer_add_stroke_callback(abstract_surface: *Surface, func: SurfaceObserverCallback, data: ?*anyopaque) Status; 488 | pub const observerAddStrokeCallback = cairo_surface_observer_add_stroke_callback; 489 | 490 | extern fn cairo_surface_observer_add_glyphs_callback(abstract_surface: *Surface, func: SurfaceObserverCallback, data: ?*anyopaque) Status; 491 | pub const observerAddGlyphsCallback = cairo_surface_observer_add_glyphs_callback; 492 | 493 | extern fn cairo_surface_observer_add_flush_callback(abstract_surface: *Surface, func: SurfaceObserverCallback, data: ?*anyopaque) Status; 494 | pub const observerAddFlushCallback = cairo_surface_observer_add_glyphs_callback; 495 | 496 | extern fn cairo_surface_observer_add_finish_callback(abstract_surface: *Surface, func: SurfaceObserverCallback, data: ?*anyopaque) Status; 497 | pub const observerAddFinishCallback = cairo_surface_observer_add_glyphs_callback; 498 | 499 | extern fn cairo_surface_observer_print(abstract_surface: *Surface, write_func: WriteFunc, closure: ?*anyopaque) Status; 500 | pub const observerPrint = cairo_surface_observer_print; 501 | 502 | extern fn cairo_surface_observer_elapsed(abstract_surface: *Surface) f64; 503 | pub const observerElapsed = cairo_surface_observer_elapsed; 504 | 505 | extern fn cairo_surface_reference(surface: *Surface) *Surface; 506 | pub const reference = cairo_surface_reference; 507 | 508 | extern fn cairo_surface_finish(surface: *Surface) void; 509 | pub const finish = cairo_surface_finish; 510 | 511 | extern fn cairo_surface_destroy(surface: *Surface) void; 512 | pub const destroy = cairo_surface_destroy; 513 | 514 | extern fn cairo_surface_get_device(surface: *Surface) ?*Device; 515 | pub const getDevice = cairo_surface_get_device; 516 | 517 | extern fn cairo_surface_get_reference_count(surface: *Surface) c_uint; 518 | pub const getReferenceCount = cairo_surface_get_reference_count; 519 | 520 | extern fn cairo_surface_status(surface: *Surface) Status; 521 | pub const status = cairo_surface_status; 522 | 523 | extern fn cairo_surface_get_type(surface: *Surface) SurfaceType; 524 | pub const getType = cairo_surface_get_type; 525 | 526 | extern fn cairo_surface_get_content(surface: *Surface) Content; 527 | pub const getContent = cairo_surface_get_content; 528 | 529 | extern fn cairo_surface_write_to_png(surface: *Surface, filename: [*:0]const u8) Status; 530 | pub const writeToPng = cairo_surface_write_to_png; 531 | 532 | extern fn cairo_surface_write_to_png_stream(surface: *Surface, write_func: WriteFunc, closure: ?*anyopaque) Status; 533 | pub const writeToPngStream = cairo_surface_write_to_png_stream; 534 | 535 | extern fn cairo_surface_get_user_data(surface: *Surface, key: *const UserDataKey) ?*anyopaque; 536 | pub const getUserData = cairo_surface_get_user_data; 537 | 538 | extern fn cairo_surface_set_user_data(surface: *Surface, key: *const UserDataKey, user_data: ?*anyopaque, destroy: DestroyFunc) Status; 539 | pub const setUserData = cairo_surface_set_user_data; 540 | 541 | extern fn cairo_surface_get_mime_data(surface: *Surface, mime_type: [*:0]const u8, data: *?[*]const u8, length: *c_ulong) void; 542 | pub const getMimeData = cairo_surface_get_mime_data; 543 | 544 | extern fn cairo_surface_set_mime_data(surface: *Surface, mime_type: [*:0]const u8, data: ?[*]const u8, length: c_ulong, destroy: DestroyFunc, closure: ?*anyopaque) Status; 545 | pub const setMimeData = cairo_surface_set_mime_data; 546 | 547 | extern fn cairo_surface_supports_mime_type(surface: *Surface, mime_type: [*:0]const u8) c_int; 548 | pub const supportsMimeType = cairo_surface_supports_mime_type; 549 | 550 | extern fn cairo_surface_get_font_options(surface: *Surface, options: *FontOptions) void; 551 | pub const getFontOptions = cairo_surface_get_font_options; 552 | 553 | extern fn cairo_surface_flush(surface: *Surface) void; 554 | pub const flush = cairo_surface_flush; 555 | 556 | extern fn cairo_surface_mark_dirty(surface: *Surface) void; 557 | pub const markDirty = cairo_surface_mark_dirty; 558 | 559 | extern fn cairo_surface_mark_dirty_rectangle(surface: *Surface, x: c_int, y: c_int, width: c_int, height: c_int) void; 560 | pub const markDirtyRectangle = cairo_surface_mark_dirty_rectangle; 561 | 562 | extern fn cairo_surface_set_device_scale(surface: *Surface, x_scale: f64, y_scale: f64) void; 563 | pub const setDeviceScale = cairo_surface_set_device_scale; 564 | 565 | extern fn cairo_surface_get_device_scale(surface: *Surface, x_scale: *f64, y_scale: *f64) void; 566 | pub const getDeviceScale = cairo_surface_get_device_scale; 567 | 568 | extern fn cairo_surface_set_device_offset(surface: *Surface, x_offset: f64, y_offset: f64) void; 569 | pub const setDeviceOffset = cairo_surface_set_device_offset; 570 | 571 | extern fn cairo_surface_get_device_offset(surface: *Surface, x_offset: *f64, y_offset: *f64) void; 572 | pub const getDeviceOffset = cairo_surface_get_device_offset; 573 | 574 | extern fn cairo_surface_set_fallback_resolution(surface: *Surface, x_pixels_per_inch: f64, y_pixels_per_inch: f64) void; 575 | pub const setFallbackResolution = cairo_surface_set_fallback_resolution; 576 | 577 | extern fn cairo_surface_get_fallback_resolution(surface: *Surface, x_pixels_per_inch: *f64, y_pixels_per_inch: *f64) void; 578 | pub const getFallbackResolution = cairo_surface_get_fallback_resolution; 579 | 580 | extern fn cairo_surface_copy_page(surface: *Surface) void; 581 | pub const copyPage = cairo_surface_copy_page; 582 | 583 | extern fn cairo_surface_show_page(surface: *Surface) void; 584 | pub const showPage = cairo_surface_show_page; 585 | 586 | extern fn cairo_surface_has_show_text_glyphs(surface: *Surface) c_int; 587 | pub const hasShowTextGlyphs = cairo_surface_has_show_text_glyphs; 588 | 589 | extern fn cairo_image_surface_create(format: Format, width: c_int, height: c_int) *Surface; 590 | pub const imageCreate = cairo_image_surface_create; 591 | 592 | extern fn cairo_image_surface_create_for_data(data: [*]u8, format: Format, width: c_int, height: c_int, stride: c_int) *Surface; 593 | pub const imageCreateForData = cairo_image_surface_create_for_data; 594 | 595 | extern fn cairo_image_surface_get_data(surface: *Surface) ?[*]u8; 596 | pub const imageGetData = cairo_image_surface_get_data; 597 | 598 | extern fn cairo_image_surface_get_format(surface: *Surface) Format; 599 | pub const imageGetFormat = cairo_image_surface_get_format; 600 | 601 | extern fn cairo_image_surface_get_width(surface: *Surface) c_int; 602 | pub const imageGetWidth = cairo_image_surface_get_width; 603 | 604 | extern fn cairo_image_surface_get_height(surface: *Surface) c_int; 605 | pub const imageGetHeight = cairo_image_surface_get_height; 606 | 607 | extern fn cairo_image_surface_get_stride(surface: *Surface) c_int; 608 | pub const imageGetStride = cairo_image_surface_get_stride; 609 | 610 | extern fn cairo_image_surface_create_from_png(filename: [*:0]const u8) *Surface; 611 | pub const imageCreateFromPng = cairo_image_surface_create_from_png; 612 | 613 | extern fn cairo_image_surface_create_from_png_stream(read_func: ReadFunc, closure: ?*anyopaque) *Surface; 614 | pub const imageCreateFromPngStream = cairo_image_surface_create_from_png_stream; 615 | 616 | extern fn cairo_recording_surface_create(content: Content, extents: ?*const Rectangle) *Surface; 617 | pub const recordingCreate = cairo_recording_surface_create; 618 | 619 | extern fn cairo_recording_surface_ink_extents(surface: *Surface, x0: *f64, y0: *f64, width: *f64, height: *f64) void; 620 | pub const recordingInkExtents = cairo_recording_surface_ink_extents; 621 | 622 | extern fn cairo_recording_surface_get_extents(surface: *Surface, extents: *Rectangle) c_int; 623 | pub const recordingGetExtents = cairo_recording_surface_get_extents; 624 | }; 625 | 626 | pub const mime_type_jpeg = "image/jpeg"; 627 | pub const mime_type_png = "image/png"; 628 | pub const mime_type_jp2 = "image/jp2"; 629 | pub const mime_type_uri = "text/x-uri"; 630 | pub const mime_type_unique_id = "application/x-cairo.uuid"; 631 | pub const mime_type_jbig2 = "application/x-cairo.jbig2"; 632 | pub const mime_type_jbig2_global = "application/x-cairo.jbig2-global"; 633 | pub const mime_type_jbig2_global_id = "application/x-cairo.jbig2-global-id"; 634 | pub const mime_type_ccitt_fax = "image/g3fax"; 635 | pub const mime_type_ccitt_fax_params = "image/x-cairo.ccitt.params"; 636 | pub const mime_type_eps = "application/postscript"; 637 | pub const mime_type_eps_params = "application/x-cairo.eps.params"; 638 | 639 | pub const SurfaceType = enum(c_int) { 640 | image, 641 | pdf, 642 | ps, 643 | xlib, 644 | xcb, 645 | glitz, 646 | quartz, 647 | win32, 648 | beos, 649 | directfb, 650 | svg, 651 | os2, 652 | win32_printing, 653 | quartz_image, 654 | script, 655 | qt, 656 | recording, 657 | vg, 658 | gl, 659 | drm, 660 | tee, 661 | xml, 662 | skia, 663 | subsurface, 664 | cogl, 665 | }; 666 | 667 | pub const SurfaceObserverMode = enum(c_int) { 668 | normal = 0, 669 | record_operations = 0x1, 670 | }; 671 | 672 | pub const SurfaceObserverCallback = *const fn (observer: *Surface, target: *Surface, data: ?*anyopaque) callconv(.c) void; 673 | 674 | pub const Device = opaque { 675 | extern fn cairo_device_reference(device: *Device) *Device; 676 | pub const reference = cairo_device_reference; 677 | 678 | extern fn cairo_device_get_type(device: *Device) DeviceType; 679 | pub const getType = cairo_device_get_type; 680 | 681 | extern fn cairo_device_status(device: *Device) Status; 682 | pub const status = cairo_device_status; 683 | 684 | extern fn cairo_device_acquire(device: *Device) Status; 685 | pub const acquire = cairo_device_acquire; 686 | 687 | extern fn cairo_device_release(device: *Device) void; 688 | pub const release = cairo_device_release; 689 | 690 | extern fn cairo_device_flush(device: *Device) void; 691 | pub const flush = cairo_device_flush; 692 | 693 | extern fn cairo_device_finish(device: *Device) void; 694 | pub const finish = cairo_device_finish; 695 | 696 | extern fn cairo_device_destroy(device: *Device) void; 697 | pub const destroy = cairo_device_destroy; 698 | 699 | extern fn cairo_device_get_reference_count(device: *Device) c_uint; 700 | pub const getReferenceCount = cairo_device_get_reference_count; 701 | 702 | extern fn cairo_device_get_user_data(device: *Device, key: *const UserDataKey) ?*anyopaque; 703 | pub const getUserData = cairo_device_get_user_data; 704 | 705 | extern fn cairo_device_set_user_data(device: *Device, key: *const UserDataKey, user_data: ?*anyopaque, destroy: DestroyFunc) Status; 706 | pub const setUserData = cairo_device_set_user_data; 707 | 708 | extern fn cairo_device_observer_print(abstract_device: *Device, write_func: WriteFunc, closure: ?*anyopaque) Status; 709 | pub const observerPrint = cairo_device_observer_print; 710 | 711 | extern fn cairo_device_observer_elapsed(abstract_device: *Device) f64; 712 | pub const observerElapsed = cairo_device_observer_elapsed; 713 | 714 | extern fn cairo_device_observer_paint_elapsed(abstract_device: *Device) f64; 715 | pub const observerPaintElapsed = cairo_device_observer_paint_elapsed; 716 | 717 | extern fn cairo_device_observer_mask_elapsed(abstract_device: *Device) f64; 718 | pub const observerMaskElapsed = cairo_device_observer_mask_elapsed; 719 | 720 | extern fn cairo_device_observer_fill_elapsed(abstract_device: *Device) f64; 721 | pub const observerFillElapsed = cairo_device_observer_fill_elapsed; 722 | 723 | extern fn cairo_device_observer_stroke_elapsed(abstract_device: *Device) f64; 724 | pub const observerStrokeElapsed = cairo_device_observer_stroke_elapsed; 725 | 726 | extern fn cairo_device_observer_glyphs_elapsed(abstract_device: *Device) f64; 727 | pub const observerGlyphsElapsed = cairo_device_observer_glyphs_elapsed; 728 | }; 729 | 730 | pub const DeviceType = enum(c_int) { 731 | drm, 732 | gl, 733 | script, 734 | xcb, 735 | xlib, 736 | xml, 737 | cogl, 738 | win32, 739 | 740 | invalid = -1, 741 | }; 742 | 743 | pub const Pattern = opaque { 744 | extern fn cairo_pattern_set_dither(pattern: *Pattern, dither: Dither) void; 745 | pub const setDither = cairo_pattern_set_dither; 746 | 747 | extern fn cairo_pattern_get_dither(pattern: *Pattern) Dither; 748 | pub const getDither = cairo_pattern_get_dither; 749 | 750 | extern fn cairo_pattern_create_raster_source(user_data: ?*anyopaque, content: Content, width: c_int, height: c_int) *Pattern; 751 | pub const rasterSourceCreate = cairo_pattern_create_raster_source; 752 | 753 | extern fn cairo_raster_source_pattern_set_callback_data(pattern: *Pattern, data: ?*anyopaque) void; 754 | pub const rasterSourceSetCallbackData = cairo_raster_source_pattern_set_callback_data; 755 | 756 | extern fn cairo_raster_source_pattern_get_callback_data(pattern: *Pattern) ?*anyopaque; 757 | pub const rasterSourceGetCallbackData = cairo_raster_source_pattern_get_callback_data; 758 | 759 | extern fn cairo_raster_source_pattern_set_acquire(pattern: *Pattern, acquire: RasterSourceAcquireFunc, release: RasterSourceReleaseFunc) void; 760 | pub const rasterSourceSetAcquire = cairo_raster_source_pattern_set_acquire; 761 | 762 | extern fn cairo_raster_source_pattern_get_acquire(pattern: *Pattern, acquire: *RasterSourceAcquireFunc, release: *RasterSourceReleaseFunc) void; 763 | pub const rasterSourceGetAcquire = cairo_raster_source_pattern_get_acquire; 764 | 765 | extern fn cairo_raster_source_pattern_set_snapshot(pattern: *Pattern, snapshot: RasterSourceSnapshotFunc) void; 766 | pub const rasterSourceSetSnapshot = cairo_raster_source_pattern_set_snapshot; 767 | 768 | extern fn cairo_raster_source_pattern_get_snapshot(pattern: *Pattern) RasterSourceSnapshotFunc; 769 | pub const rasterSourceGetSnapshot = cairo_raster_source_pattern_get_snapshot; 770 | 771 | extern fn cairo_raster_source_pattern_set_copy(pattern: *Pattern, copy: RasterSourceCopyFunc) void; 772 | pub const rasterSourceSetCopy = cairo_raster_source_pattern_set_copy; 773 | 774 | extern fn cairo_raster_source_pattern_get_copy(pattern: *Pattern) RasterSourceCopyFunc; 775 | pub const rasterSourceGetCopy = cairo_raster_source_pattern_get_copy; 776 | 777 | extern fn cairo_raster_source_pattern_set_finish(pattern: *Pattern, finish: RasterSourceFinishFunc) void; 778 | pub const rasterSourceSetFinish = cairo_raster_source_pattern_set_finish; 779 | 780 | extern fn cairo_raster_source_pattern_get_finish(pattern: *Pattern) RasterSourceFinishFunc; 781 | pub const rasterSourceGetFinish = cairo_raster_source_pattern_get_finish; 782 | 783 | extern fn cairo_pattern_create_rgb(red: f64, green: f64, blue: f64) *Pattern; 784 | pub const solidCreateRgb = cairo_pattern_create_rgb; 785 | 786 | extern fn cairo_pattern_create_rgba(red: f64, green: f64, blue: f64, alpha: f64) *Pattern; 787 | pub const solidCreateRgba = cairo_pattern_create_rgba; 788 | 789 | extern fn cairo_pattern_get_rgba(pattern: *Pattern, red: ?*f64, green: ?*f64, blue: ?*f64, alpha: ?*f64) Status; 790 | pub const solidGetRgba = cairo_pattern_get_rgba; 791 | 792 | extern fn cairo_pattern_create_for_surface(surface: *Surface) *Pattern; 793 | pub const surfaceCreate = cairo_pattern_create_for_surface; 794 | 795 | extern fn cairo_pattern_get_surface(pattern: *Pattern, surface: ?**Surface) Status; 796 | pub const surfaceGet = cairo_pattern_get_surface; 797 | 798 | extern fn cairo_pattern_create_linear(x0: f64, y0: f64, x1: f64, y1: f64) *Pattern; 799 | pub const linearCreate = cairo_pattern_create_linear; 800 | 801 | extern fn cairo_pattern_get_linear_points(pattern: *Pattern, x0: ?*f64, y0: ?*f64, x1: ?*f64, y1: ?*f64) Status; 802 | pub const linearGetPoints = cairo_pattern_get_linear_points; 803 | 804 | extern fn cairo_pattern_create_radial(cx0: f64, cy0: f64, radius0: f64, cx1: f64, cy1: f64, radius1: f64) *Pattern; 805 | pub const radialCreate = cairo_pattern_create_radial; 806 | 807 | extern fn cairo_pattern_get_radial_circles(pattern: *Pattern, x0: ?*f64, y0: ?*f64, r0: ?*f64, x1: ?*f64, y1: ?*f64, r1: ?*f64) Status; 808 | pub const radialGetCircles = cairo_pattern_get_radial_circles; 809 | 810 | extern fn cairo_pattern_get_color_stop_count(pattern: *Pattern, count: ?*c_int) Status; 811 | pub const gradientGetColorStopCount = cairo_pattern_get_color_stop_count; 812 | 813 | extern fn cairo_pattern_add_color_stop_rgb(pattern: *Pattern, offset: f64, red: f64, green: f64, blue: f64) void; 814 | pub const gradientAddColorStopRgb = cairo_pattern_add_color_stop_rgb; 815 | 816 | extern fn cairo_pattern_add_color_stop_rgba(pattern: *Pattern, offset: f64, red: f64, green: f64, blue: f64, alpha: f64) void; 817 | pub const gradientAddColorStopRgba = cairo_pattern_add_color_stop_rgba; 818 | 819 | extern fn cairo_pattern_get_color_stop_rgba(pattern: *Pattern, index: c_int, offset: ?*f64, red: ?*f64, green: ?*f64, blue: ?*f64, alpha: ?*f64) Status; 820 | pub const gradientGetColorStopRgba = cairo_pattern_get_color_stop_rgba; 821 | 822 | extern fn cairo_pattern_create_mesh() *Pattern; 823 | pub const meshCreate = cairo_pattern_create_mesh; 824 | 825 | extern fn cairo_mesh_pattern_begin_patch(pattern: *Pattern) void; 826 | pub const meshBeginPatch = cairo_mesh_pattern_begin_patch; 827 | 828 | extern fn cairo_mesh_pattern_end_patch(pattern: *Pattern) void; 829 | pub const meshEndPatch = cairo_mesh_pattern_end_patch; 830 | 831 | extern fn cairo_mesh_pattern_curve_to(pattern: *Pattern, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) void; 832 | pub const meshCurveTo = cairo_mesh_pattern_curve_to; 833 | 834 | extern fn cairo_mesh_pattern_line_to(pattern: *Pattern, x: f64, y: f64) void; 835 | pub const meshLineTo = cairo_mesh_pattern_line_to; 836 | 837 | extern fn cairo_mesh_pattern_move_to(pattern: *Pattern, x: f64, y: f64) void; 838 | pub const meshMoveTo = cairo_mesh_pattern_move_to; 839 | 840 | extern fn cairo_mesh_pattern_set_control_point(pattern: *Pattern, point_num: c_uint, x: f64, y: f64) void; 841 | pub const meshSetControlPoint = cairo_mesh_pattern_set_control_point; 842 | 843 | extern fn cairo_mesh_pattern_set_corner_color_rgb(pattern: *Pattern, corner_num: c_uint, red: f64, green: f64, blue: f64) void; 844 | pub const meshSetCornerColorRgb = cairo_mesh_pattern_set_corner_color_rgb; 845 | 846 | extern fn cairo_mesh_pattern_set_corner_color_rgba(pattern: *Pattern, corner_num: c_uint, red: f64, green: f64, blue: f64, alpha: f64) void; 847 | pub const meshSetCornerColorRgba = cairo_mesh_pattern_set_corner_color_rgba; 848 | 849 | extern fn cairo_mesh_pattern_get_patch_count(pattern: *Pattern, count: ?*c_uint) Status; 850 | pub const meshGetPatchCount = cairo_mesh_pattern_get_patch_count; 851 | 852 | extern fn cairo_mesh_pattern_get_path(pattern: *Pattern, patch_num: c_uint) *Path; 853 | pub const meshGetPath = cairo_mesh_pattern_get_path; 854 | 855 | extern fn cairo_mesh_pattern_get_corner_color_rgba(pattern: *Pattern, patch_num: c_uint, corner_num: c_uint, red: ?*f64, green: ?*f64, blue: ?*f64, alpha: ?*f64) Status; 856 | pub const meshGetCornerColorRgba = cairo_mesh_pattern_get_corner_color_rgba; 857 | 858 | extern fn cairo_mesh_pattern_get_control_point(pattern: *Pattern, patch_num: c_uint, point_num: c_int, x: ?*f64, y: ?*f64) Status; 859 | pub const meshGetControlPoint = cairo_mesh_pattern_get_control_point; 860 | 861 | extern fn cairo_pattern_reference(pattern: *Pattern) *Pattern; 862 | pub const reference = cairo_pattern_reference; 863 | 864 | extern fn cairo_pattern_destroy(pattern: *Pattern) void; 865 | pub const destroy = cairo_pattern_destroy; 866 | 867 | extern fn cairo_pattern_get_reference_count(pattern: *Pattern) c_uint; 868 | pub const getReferenceCount = cairo_pattern_get_reference_count; 869 | 870 | extern fn cairo_pattern_status(pattern: *Pattern) Status; 871 | pub const status = cairo_pattern_status; 872 | 873 | extern fn cairo_pattern_get_user_data(pattern: *Pattern, key: *const UserDataKey) ?*anyopaque; 874 | pub const getUserData = cairo_pattern_get_user_data; 875 | 876 | extern fn cairo_pattern_set_user_data(pattern: *Pattern, key: *const UserDataKey, user_data: ?*anyopaque, destroy: DestroyFunc) Status; 877 | pub const setUserData = cairo_pattern_set_user_data; 878 | 879 | extern fn cairo_pattern_get_type(pattern: *Pattern) PatternType; 880 | pub const getType = cairo_pattern_get_type; 881 | 882 | extern fn cairo_pattern_set_extend(pattern: *Pattern, extend: Extend) void; 883 | pub const setExtend = cairo_pattern_set_extend; 884 | 885 | extern fn cairo_pattern_get_extend(pattern: *Pattern) Extend; 886 | pub const getExtend = cairo_pattern_get_extend; 887 | 888 | extern fn cairo_pattern_set_filter(pattern: *Pattern, filter: Filter) void; 889 | pub const setFilter = cairo_pattern_set_filter; 890 | 891 | extern fn cairo_pattern_get_filter(pattern: *Pattern) Filter; 892 | pub const getFilter = cairo_pattern_get_filter; 893 | }; 894 | 895 | pub const PatternType = enum(c_int) { 896 | solid, 897 | surface, 898 | linear, 899 | radial, 900 | mesh, 901 | raster_source, 902 | }; 903 | 904 | pub const Extend = enum(c_int) { 905 | none, 906 | repeat, 907 | reflect, 908 | pad, 909 | }; 910 | 911 | pub const Filter = enum(c_int) { 912 | fast, 913 | good, 914 | best, 915 | nearest, 916 | bilinear, 917 | gaussian, 918 | }; 919 | 920 | pub const RasterSourceAcquireFunc = *const fn (pattern: *Pattern, callback_data: ?*anyopaque, target: *Surface, extents: *const RectangleInt) callconv(.c) *Surface; 921 | pub const RasterSourceReleaseFunc = *const fn (pattern: *Pattern, callback_data: ?*anyopaque, surface: *Surface) callconv(.c) void; 922 | pub const RasterSourceSnapshotFunc = *const fn (pattern: *Pattern, callback_data: ?*anyopaque) callconv(.c) Status; 923 | pub const RasterSourceCopyFunc = *const fn (pattern: *Pattern, callback_data: ?*anyopaque, other: *const Pattern) callconv(.c) Status; 924 | pub const RasterSourceFinishFunc = *const fn (pattern: *Pattern, callback_data: ?*anyopaque) callconv(.c) void; 925 | 926 | pub const Content = enum(c_int) { 927 | color = 0x1000, 928 | alpha = 0x2000, 929 | color_alpha = 0x3000, 930 | }; 931 | 932 | pub const Format = enum(c_int) { 933 | invalid = -1, 934 | argb32 = 0, 935 | rgb24 = 1, 936 | a8 = 2, 937 | a1 = 3, 938 | rgb16_565 = 4, 939 | rgb30 = 5, 940 | rgb96f = 6, 941 | rgba128f = 7, 942 | 943 | extern fn cairo_format_stride_for_width(format: Format, width: c_int) c_int; 944 | pub const strideForWidth = cairo_format_stride_for_width; 945 | }; 946 | 947 | pub const Dither = enum(c_int) { 948 | none, 949 | default, 950 | fast, 951 | good, 952 | best, 953 | }; 954 | 955 | pub const Operator = enum(c_int) { 956 | clear, 957 | 958 | source, 959 | over, 960 | in, 961 | out, 962 | atop, 963 | 964 | dest, 965 | dest_over, 966 | dest_in, 967 | dest_out, 968 | dest_atop, 969 | 970 | xor, 971 | add, 972 | saturate, 973 | 974 | multiply, 975 | screen, 976 | overlay, 977 | darken, 978 | lighten, 979 | color_dodge, 980 | color_burn, 981 | hard_light, 982 | soft_light, 983 | difference, 984 | exclusion, 985 | hsl_hue, 986 | hsl_saturation, 987 | hsl_color, 988 | hsl_luminosity, 989 | }; 990 | 991 | pub const Antialias = enum(c_int) { 992 | default, 993 | 994 | none, 995 | gray, 996 | subpixel, 997 | 998 | fast, 999 | good, 1000 | best, 1001 | }; 1002 | 1003 | pub const FillRule = enum(c_int) { 1004 | winding, 1005 | even_odd, 1006 | }; 1007 | 1008 | pub const LineCap = enum(c_int) { 1009 | butt, 1010 | round, 1011 | square, 1012 | }; 1013 | 1014 | pub const LineJoin = enum(c_int) { 1015 | miter, 1016 | round, 1017 | bevel, 1018 | }; 1019 | 1020 | pub const tag_dest = "cairo.dest"; 1021 | pub const tag_link = "Link"; 1022 | pub const tag_content = "cairo.content"; 1023 | pub const tag_content_ref = "cairo.content_ref"; 1024 | 1025 | pub const ScaledFont = opaque { 1026 | extern fn cairo_scaled_font_create(font_face: *FontFace, matrix: *const Matrix, ctm: *const Matrix, options: *FontOptions) *ScaledFont; 1027 | pub const create = cairo_scaled_font_create; 1028 | 1029 | extern fn cairo_scaled_font_reference(scaled_font: *ScaledFont) *ScaledFont; 1030 | pub const reference = cairo_scaled_font_reference; 1031 | 1032 | extern fn cairo_scaled_font_destroy(scaled_font: *ScaledFont) void; 1033 | pub const destroy = cairo_scaled_font_destroy; 1034 | 1035 | extern fn cairo_scaled_font_get_reference_count(scaled_font: *ScaledFont) c_uint; 1036 | pub const getReferenceCount = cairo_scaled_font_get_reference_count; 1037 | 1038 | extern fn cairo_scaled_font_status(scaled_font: *ScaledFont) Status; 1039 | pub const status = cairo_scaled_font_status; 1040 | 1041 | extern fn cairo_scaled_font_get_type(scaled_font: *ScaledFont) FontType; 1042 | pub const getType = cairo_scaled_font_get_type; 1043 | 1044 | extern fn cairo_scaled_font_get_user_data(scaled_font: *ScaledFont, key: *const UserDataKey) ?*anyopaque; 1045 | pub const getUserData = cairo_scaled_font_get_user_data; 1046 | 1047 | extern fn cairo_scaled_font_set_user_data(scaled_font: *ScaledFont, key: *const UserDataKey, user_data: ?*anyopaque, destroy: DestroyFunc) Status; 1048 | pub const setUserData = cairo_scaled_font_set_user_data; 1049 | 1050 | extern fn cairo_scaled_font_extents(scaled_font: *ScaledFont, extents: *FontExtents) void; 1051 | pub const extents = cairo_scaled_font_extents; 1052 | 1053 | extern fn cairo_scaled_font_text_extents(scaled_font: *ScaledFont, utf8: [*:0]const u8, extents: *TextExtents) void; 1054 | pub const textExtents = cairo_scaled_font_text_extents; 1055 | 1056 | extern fn cairo_scaled_font_glyph_extents(scaled_font: *ScaledFont, glyphs: [*]const Glyph, num_glyphs: c_int, extents: *TextExtents) void; 1057 | pub const glyphExtents = cairo_scaled_font_glyph_extents; 1058 | 1059 | extern fn cairo_scaled_font_text_to_glyphs(scaled_font: *ScaledFont, x: f64, y: f64, utf8: [*]const u8, utf8_len: c_int, glyphs: *?[*]Glyph, num_glyphs: c_int, clusters: ?*?[*]TextCluster, num_clusters: c_int, cluster_flags: ?*TextClusterFlags) Status; 1060 | pub const textToGlyphs = cairo_scaled_font_text_to_glyphs; 1061 | 1062 | extern fn cairo_scaled_font_get_font_face(scaled_font: *ScaledFont) *FontFace; 1063 | pub const getFontFace = cairo_scaled_font_get_font_face; 1064 | 1065 | extern fn cairo_scaled_font_get_font_matrix(scaled_font: *ScaledFont, font_matrix: *Matrix) void; 1066 | pub const getFontMatrix = cairo_scaled_font_get_font_matrix; 1067 | 1068 | extern fn cairo_scaled_font_get_ctm(scaled_font: *ScaledFont, ctm: *Matrix) void; 1069 | pub const getCtm = cairo_scaled_font_get_ctm; 1070 | 1071 | extern fn cairo_scaled_font_get_scale_matrix(scaled_font: *ScaledFont, scale_matrix: *Matrix) void; 1072 | pub const getScaleMatrix = cairo_scaled_font_get_scale_matrix; 1073 | 1074 | extern fn cairo_scaled_font_get_font_options(scaled_font: *ScaledFont, options: *FontOptions) void; 1075 | pub const getFontOptions = cairo_scaled_font_get_font_options; 1076 | 1077 | extern fn cairo_user_scaled_font_get_foreground_marker(scaled_font: *ScaledFont) *Pattern; 1078 | pub const userGetForegroundMarker = cairo_user_scaled_font_get_foreground_marker; 1079 | 1080 | extern fn cairo_user_scaled_font_get_foreground_source(scaled_font: *ScaledFont) *Pattern; 1081 | pub const userGetForegroundSource = cairo_user_scaled_font_get_foreground_source; 1082 | }; 1083 | 1084 | pub const FontFace = opaque { 1085 | extern fn cairo_font_face_reference(font_face: *FontFace) *FontFace; 1086 | pub const reference = cairo_font_face_reference; 1087 | 1088 | extern fn cairo_font_face_destroy(font_face: *FontFace) void; 1089 | pub const destroy = cairo_font_face_destroy; 1090 | 1091 | extern fn cairo_font_face_get_reference_count(font_face: *FontFace) c_uint; 1092 | pub const getReferenceCount = cairo_font_face_get_reference_count; 1093 | 1094 | extern fn cairo_font_face_status(font_face: *FontFace) Status; 1095 | pub const status = cairo_font_face_status; 1096 | 1097 | extern fn cairo_font_face_get_type(font_face: *FontFace) FontType; 1098 | pub const getType = cairo_font_face_get_type; 1099 | 1100 | extern fn cairo_font_face_get_user_data(font_face: *FontFace, key: *const UserDataKey) ?*anyopaque; 1101 | pub const getUserData = cairo_font_face_get_user_data; 1102 | 1103 | extern fn cairo_font_face_set_user_data(font_face: *FontFace, key: *const UserDataKey, user_data: ?*anyopaque, destroy: DestroyFunc) Status; 1104 | pub const setUserData = cairo_font_face_set_user_data; 1105 | 1106 | extern fn cairo_toy_font_face_create(family: [*:0]const u8, slant: FontSlant, weight: FontWeight) *FontFace; 1107 | pub const toyCreate = cairo_toy_font_face_create; 1108 | 1109 | extern fn cairo_toy_font_face_get_family(font_face: *FontFace) [*:0]const u8; 1110 | pub const toyGetFamily = cairo_toy_font_face_get_family; 1111 | 1112 | extern fn cairo_toy_font_face_get_slant(font_face: *FontFace) FontSlant; 1113 | pub const toyGetSlant = cairo_toy_font_face_get_slant; 1114 | 1115 | extern fn cairo_toy_font_face_get_weight(font_face: *FontFace) FontWeight; 1116 | pub const toyGetWeight = cairo_toy_font_face_get_weight; 1117 | 1118 | extern fn cairo_user_font_face_create() *FontFace; 1119 | pub const userCreate = cairo_user_font_face_create; 1120 | 1121 | extern fn cairo_user_font_face_set_init_func(font_face: *FontFace, init_func: UserScaledFontInitFunc) void; 1122 | pub const userSetInitFunc = cairo_user_font_face_set_init_func; 1123 | 1124 | extern fn cairo_user_font_face_set_render_glyph_func(font_face: *FontFace, render_glyph_func: UserScaledFontRenderGlyphFunc) void; 1125 | pub const userSetRenderGlyphFunc = cairo_user_font_face_set_render_glyph_func; 1126 | 1127 | extern fn cairo_user_font_face_set_render_color_glyph_func(font_face: *FontFace, render_glyph_func: UserScaledFontRenderGlyphFunc) void; 1128 | pub const userSetRenderColorGlyphFunc = cairo_user_font_face_set_render_color_glyph_func; 1129 | 1130 | extern fn cairo_user_font_face_set_text_to_glyphs_func(font_face: *FontFace, text_to_glyphs_func: UserScaledFontTextToGlyphsFunc) void; 1131 | pub const userSetTextToGlyphsFunc = cairo_user_font_face_set_text_to_glyphs_func; 1132 | 1133 | extern fn cairo_user_font_face_set_unicode_to_glyph_func(font_face: *FontFace, unicode_to_glyph_func: UserScaledFontUnicodeToGlyphFunc) void; 1134 | pub const userSetUnicodeToGlyphFunc = cairo_user_font_face_set_unicode_to_glyph_func; 1135 | 1136 | extern fn cairo_user_font_face_get_init_func(font_face: *FontFace) UserScaledFontInitFunc; 1137 | pub const userGetInitFunc = cairo_user_font_face_get_init_func; 1138 | 1139 | extern fn cairo_user_font_face_get_render_glyph_func(font_face: *FontFace) UserScaledFontRenderGlyphFunc; 1140 | pub const userGetRenderGlyphFunc = cairo_user_font_face_get_render_glyph_func; 1141 | 1142 | extern fn cairo_user_font_face_get_render_color_glyph_func(font_face: *FontFace) UserScaledFontRenderGlyphFunc; 1143 | pub const userGetRenderColorGlyphFunc = cairo_user_font_face_get_render_color_glyph_func; 1144 | 1145 | extern fn cairo_user_font_face_get_text_to_glyphs_func(font_face: *FontFace) UserScaledFontTextToGlyphsFunc; 1146 | pub const userGetTextToGlyphsFunc = cairo_user_font_face_get_text_to_glyphs_func; 1147 | 1148 | extern fn cairo_user_font_face_get_unicode_to_glyph_func(font_face: *FontFace) UserScaledFontUnicodeToGlyphFunc; 1149 | pub const userGetUnicodeToGlyphFunc = cairo_user_font_face_get_unicode_to_glyph_func; 1150 | }; 1151 | 1152 | pub const UserScaledFontInitFunc = *const fn (scaled_font: *ScaledFont, cr: *Context, extents: *FontExtents) callconv(.c) Status; 1153 | pub const UserScaledFontRenderGlyphFunc = *const fn (scaled_font: *ScaledFont, glyph: c_ulong, cr: *Context, extents: *FontExtents) callconv(.c) Status; 1154 | pub const UserScaledFontTextToGlyphsFunc = *const fn (scaled_font: *ScaledFont, utf8: [*]const u8, utf8_len: c_int, glyphs: *?[*]Glyph, num_glyphs: c_int, clusters: ?*?[*]TextCluster, num_clusters: c_int, cluster_flags: ?*TextClusterFlags) callconv(.c) Status; 1155 | pub const UserScaledFontUnicodeToGlyphFunc = *const fn (scaled_font: *ScaledFont, unicode: c_ulong, glyph_index: *c_ulong) callconv(.c) Status; 1156 | 1157 | pub const Glyph = extern struct { 1158 | index: c_ulong, 1159 | x: f64, 1160 | y: f64, 1161 | 1162 | extern fn cairo_glyph_allocate(num_glyphs: c_int) ?[*]Glyph; 1163 | pub const allocate = cairo_glyph_allocate; 1164 | 1165 | extern fn cairo_glyph_free(glyphs: ?[*]Glyph) void; 1166 | pub const free = cairo_glyph_free; 1167 | }; 1168 | 1169 | pub const TextCluster = extern struct { 1170 | num_bytes: c_int, 1171 | num_glyphs: c_int, 1172 | 1173 | extern fn cairo_text_cluster_allocate(num_clusters: c_int) ?[*]TextCluster; 1174 | pub const allocate = cairo_text_cluster_allocate; 1175 | 1176 | extern fn cairo_text_cluster_free(clusters: ?[*]TextCluster) void; 1177 | pub const free = cairo_text_cluster_free; 1178 | }; 1179 | 1180 | pub const TextClusterFlags = packed struct(c_int) { 1181 | backward: bool, 1182 | _: @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(c_int) - 1 } }) = 0, 1183 | }; 1184 | 1185 | pub const TextExtents = extern struct { 1186 | x_bearing: f64, 1187 | y_bearing: f64, 1188 | width: f64, 1189 | height: f64, 1190 | x_advance: f64, 1191 | y_advance: f64, 1192 | }; 1193 | 1194 | pub const FontExtents = extern struct { 1195 | ascent: f64, 1196 | descent: f64, 1197 | height: f64, 1198 | max_x_advance: f64, 1199 | max_y_advance: f64, 1200 | }; 1201 | 1202 | pub const FontSlant = enum(c_int) { 1203 | normal, 1204 | italic, 1205 | oblique, 1206 | }; 1207 | 1208 | pub const FontWeight = enum(c_int) { 1209 | normal, 1210 | bold, 1211 | }; 1212 | 1213 | pub const SubpixelOrder = enum(c_int) { 1214 | default, 1215 | rgb, 1216 | bgr, 1217 | vrgb, 1218 | vbgr, 1219 | }; 1220 | 1221 | pub const HintStyle = enum(c_int) { 1222 | default, 1223 | none, 1224 | slight, 1225 | medium, 1226 | full, 1227 | }; 1228 | 1229 | pub const HintMetrics = enum(c_int) { 1230 | default, 1231 | off, 1232 | on, 1233 | }; 1234 | 1235 | pub const ColorMode = enum(c_int) { 1236 | default, 1237 | no_color, 1238 | color, 1239 | }; 1240 | 1241 | pub const FontOptions = opaque { 1242 | extern fn cairo_font_options_create() *FontOptions; 1243 | pub const create = cairo_font_options_create; 1244 | 1245 | extern fn cairo_font_options_copy(options: *FontOptions) *FontOptions; 1246 | pub const copy = cairo_font_options_copy; 1247 | 1248 | extern fn cairo_font_options_destroy(options: *FontOptions) void; 1249 | pub const destroy = cairo_font_options_destroy; 1250 | 1251 | extern fn cairo_font_options_status(options: *FontOptions) Status; 1252 | pub const status = cairo_font_options_status; 1253 | 1254 | extern fn cairo_font_options_merge(options: *FontOptions, other: *const FontOptions) void; 1255 | pub const merge = cairo_font_options_merge; 1256 | 1257 | extern fn cairo_font_options_equal(options: *const FontOptions, other: *const FontOptions) c_int; 1258 | pub const equal = cairo_font_options_equal; 1259 | 1260 | extern fn cairo_font_options_hash(options: *const FontOptions) c_ulong; 1261 | pub const hash = cairo_font_options_hash; 1262 | 1263 | extern fn cairo_font_options_set_antialias(options: *FontOptions, antialias: Antialias) void; 1264 | pub const setAntialias = cairo_font_options_set_antialias; 1265 | 1266 | extern fn cairo_font_options_get_antialias(options: *const FontOptions) Antialias; 1267 | pub const getAntialias = cairo_font_options_get_antialias; 1268 | 1269 | extern fn cairo_font_options_set_subpixel_order(options: *FontOptions, subpixel_order: SubpixelOrder) void; 1270 | pub const setSubpixelOrder = cairo_font_options_set_subpixel_order; 1271 | 1272 | extern fn cairo_font_options_get_subpixel_order(options: *const FontOptions) SubpixelOrder; 1273 | pub const getSubpixelOrder = cairo_font_options_get_subpixel_order; 1274 | 1275 | extern fn cairo_font_options_set_hint_style(options: *FontOptions, hint_style: HintStyle) void; 1276 | pub const setHintStyle = cairo_font_options_set_hint_style; 1277 | 1278 | extern fn cairo_font_options_get_hint_style(options: *const FontOptions) HintStyle; 1279 | pub const getHintStyle = cairo_font_options_get_hint_style; 1280 | 1281 | extern fn cairo_font_options_set_hint_metrics(options: *FontOptions, hint_metrics: HintMetrics) void; 1282 | pub const setHintMetrics = cairo_font_options_set_hint_metrics; 1283 | 1284 | extern fn cairo_font_options_get_hint_metrics(options: *const FontOptions) HintMetrics; 1285 | pub const getHintMetrics = cairo_font_options_get_hint_metrics; 1286 | 1287 | extern fn cairo_font_options_get_variations(options: *FontOptions) [*:0]const u8; 1288 | pub const getVariations = cairo_font_options_get_variations; 1289 | 1290 | extern fn cairo_font_options_set_variations(options: *FontOptions, variations: [*:0]const u8) void; 1291 | pub const setVariations = cairo_font_options_set_variations; 1292 | 1293 | extern fn cairo_font_options_set_color_mode(options: *FontOptions, color_mode: ColorMode) void; 1294 | pub const setColorMode = cairo_font_options_set_color_mode; 1295 | 1296 | extern fn cairo_font_options_get_color_mode(options: *const FontOptions) ColorMode; 1297 | pub const getColorMode = cairo_font_options_get_color_mode; 1298 | 1299 | extern fn cairo_font_options_get_color_palette(options: *const FontOptions) c_uint; 1300 | pub const getColorPalette = cairo_font_options_get_color_palette; 1301 | 1302 | extern fn cairo_font_options_set_color_palette(options: *FontOptions, palette_index: c_uint) void; 1303 | pub const setColorPalette = cairo_font_options_set_color_palette; 1304 | 1305 | extern fn cairo_font_options_set_custom_palette_color(options: *FontOptions, index: c_uint, red: f64, green: f64, blue: f64, alpha: f64) void; 1306 | pub const setCustomPaletteColor = cairo_font_options_set_custom_palette_color; 1307 | 1308 | extern fn cairo_font_options_get_custom_palette_color(options: *FontOptions, index: c_uint, red: *f64, green: *f64, blue: *f64, alpha: *f64) Status; 1309 | pub const getCustomPaletteColor = cairo_font_options_get_custom_palette_color; 1310 | }; 1311 | 1312 | pub const color_palette_default = 0; 1313 | 1314 | pub const FontType = enum(c_int) { 1315 | toy, 1316 | ft, 1317 | win32, 1318 | quartz, 1319 | user, 1320 | dwrite, 1321 | _, 1322 | }; 1323 | 1324 | pub const Path = extern struct { 1325 | status: Status, 1326 | data: *PathData, 1327 | num_data: c_int, 1328 | 1329 | extern fn cairo_path_destroy(path: *Path) void; 1330 | pub const destroy = cairo_path_destroy; 1331 | }; 1332 | 1333 | pub const PathData = extern union { 1334 | header: extern struct { 1335 | type: PathDataType, 1336 | length: c_int, 1337 | }, 1338 | point: extern struct { 1339 | x: f64, 1340 | y: f64, 1341 | }, 1342 | }; 1343 | 1344 | pub const PathDataType = enum(c_int) { 1345 | move_to, 1346 | line_to, 1347 | curve_to, 1348 | close_path, 1349 | }; 1350 | 1351 | pub const Region = opaque { 1352 | extern fn cairo_region_create() *Region; 1353 | pub const create = cairo_region_create; 1354 | 1355 | extern fn cairo_region_create_rectangle(rectangle: *const RectangleInt) *Region; 1356 | pub const createRectangle = cairo_region_create_rectangle; 1357 | 1358 | extern fn cairo_region_create_rectangles(rects: [*]const RectangleInt, count: c_int) *Region; 1359 | pub const createRectangles = cairo_region_create_rectangles; 1360 | 1361 | extern fn cairo_region_copy(original: *const Region) *Region; 1362 | pub const copy = cairo_region_copy; 1363 | 1364 | extern fn cairo_region_reference(region: *Region) *Region; 1365 | pub const reference = cairo_region_reference; 1366 | 1367 | extern fn cairo_region_destroy(region: *Region) void; 1368 | pub const destroy = cairo_region_destroy; 1369 | 1370 | extern fn cairo_region_equal(a: *const Region, b: *const Region) c_int; 1371 | pub const equal = cairo_region_equal; 1372 | 1373 | extern fn cairo_region_status(region: *const Region) void; 1374 | pub const status = cairo_region_status; 1375 | 1376 | extern fn cairo_region_get_extents(region: *const Region, extents: *RectangleInt) void; 1377 | pub const getExtents = cairo_region_get_extents; 1378 | 1379 | extern fn cairo_region_num_rectangles(region: *const Region) c_int; 1380 | pub const numRectangles = cairo_region_num_rectangles; 1381 | 1382 | extern fn cairo_region_get_rectangle(region: *const Region, nth: c_int, rectangle: *RectangleInt) void; 1383 | pub const getRectangle = cairo_region_get_rectangle; 1384 | 1385 | extern fn cairo_region_is_empty(region: *const Region) c_int; 1386 | pub const isEmpty = cairo_region_is_empty; 1387 | 1388 | extern fn cairo_region_contains_rectangle(region: *const Region, rectangle: *const RectangleInt) RegionOverlap; 1389 | pub const containsRectangle = cairo_region_contains_rectangle; 1390 | 1391 | extern fn cairo_region_contains_point(region: *const Region, x: c_int, y: c_int) c_int; 1392 | pub const containsPoint = cairo_region_contains_point; 1393 | 1394 | extern fn cairo_region_translate(region: *Region, dx: c_int, dy: c_int) void; 1395 | pub const translate = cairo_region_translate; 1396 | 1397 | extern fn cairo_region_subtract(dst: *Region, other: *const Region) Status; 1398 | pub const subtract = cairo_region_subtract; 1399 | 1400 | extern fn cairo_region_subtract_rectangle(dst: *Region, rectangle: *const RectangleInt) Status; 1401 | pub const subtractRectangle = cairo_region_subtract_rectangle; 1402 | 1403 | extern fn cairo_region_union(dst: *Region, other: *const Region) Status; 1404 | pub const @"union" = cairo_region_union; 1405 | 1406 | extern fn cairo_region_union_rectangle(dst: *Region, rectangle: *const RectangleInt) Status; 1407 | pub const unionRectangle = cairo_region_union_rectangle; 1408 | 1409 | extern fn cairo_region_xor(dst: *Region, other: *const Region) Status; 1410 | pub const xor = cairo_region_xor; 1411 | 1412 | extern fn cairo_region_xor_rectangle(dst: *Region, rectangle: *const RectangleInt) Status; 1413 | pub const xorRectangle = cairo_region_xor_rectangle; 1414 | }; 1415 | 1416 | pub const RegionOverlap = enum(c_int) { 1417 | in, 1418 | out, 1419 | part, 1420 | }; 1421 | 1422 | extern fn cairo_debug_reset_static_data() void; 1423 | pub const debugResetStaticData = cairo_debug_reset_static_data; 1424 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const ArrayListUnmanaged = std.ArrayListUnmanaged; 3 | 4 | pub fn build(b: *std.Build) void { 5 | const target = b.standardTargetOptions(.{}); 6 | const optimize = b.standardOptimizeOption(.{}); 7 | 8 | const xml = b.dependency("xml", .{}).module("xml"); 9 | 10 | const exe = b.addExecutable(.{ 11 | .name = "translate-gir", 12 | .root_source_file = b.path("src/main.zig"), 13 | .target = target, 14 | .optimize = optimize, 15 | }); 16 | exe.linkLibC(); 17 | exe.root_module.addImport("xml", xml); 18 | b.installArtifact(exe); 19 | 20 | const run_cmd = b.addRunArtifact(exe); 21 | run_cmd.step.dependOn(b.getInstallStep()); 22 | if (b.args) |args| { 23 | run_cmd.addArgs(args); 24 | } 25 | 26 | const run_step = b.step("run", "Run the binding generator"); 27 | run_step.dependOn(&run_cmd.step); 28 | 29 | // Tests 30 | const test_step = b.step("test", "Run all tests"); 31 | 32 | const exe_tests = b.addTest(.{ 33 | .root_source_file = b.path("src/main.zig"), 34 | .target = target, 35 | .optimize = optimize, 36 | }); 37 | exe_tests.linkLibC(); 38 | exe_tests.root_module.addImport("xml", xml); 39 | 40 | const test_exe_step = b.step("test-exe", "Run tests for the binding generator"); 41 | test_exe_step.dependOn(&b.addRunArtifact(exe_tests).step); 42 | test_step.dependOn(test_exe_step); 43 | 44 | const GirProfile = enum { gnome47, gnome48 }; 45 | const gir_profile = b.option(GirProfile, "gir-profile", "Predefined GIR profile for codegen"); 46 | const codegen_modules: []const []const u8 = b.option([]const []const u8, "modules", "Modules to codegen") orelse if (gir_profile) |profile| switch (profile) { 47 | .gnome47 => &.{ 48 | "Adw-1", 49 | "AppStream-1.0", 50 | "AppStreamCompose-1.0", 51 | "Atk-1.0", 52 | "Atspi-2.0", 53 | "cairo-1.0", 54 | "CudaGst-1.0", 55 | "DBus-1.0", 56 | "Dex-1", 57 | "fontconfig-2.0", 58 | "freetype2-2.0", 59 | "GCab-1.0", 60 | "Gck-1", 61 | "Gck-2", 62 | "Gcr-3", 63 | "Gcr-4", 64 | "GcrUi-3", 65 | "GDesktopEnums-3.0", 66 | "Gdk-3.0", 67 | "Gdk-4.0", 68 | "GdkPixbuf-2.0", 69 | "GdkPixdata-2.0", 70 | "GdkWayland-4.0", 71 | "GdkX11-3.0", 72 | "GdkX11-4.0", 73 | "Gee-0.8", 74 | "Geoclue-2.0", 75 | "Gio-2.0", 76 | "GioUnix-2.0", 77 | "GIRepository-2.0", 78 | "GIRepository-3.0", 79 | "GL-1.0", 80 | "GLib-2.0", 81 | "GLibUnix-2.0", 82 | "GModule-2.0", 83 | "GObject-2.0", 84 | "Graphene-1.0", 85 | "Gsk-4.0", 86 | "Gst-1.0", 87 | "GstAllocators-1.0", 88 | "GstAnalytics-1.0", 89 | "GstApp-1.0", 90 | "GstAudio-1.0", 91 | "GstBadAudio-1.0", 92 | "GstBase-1.0", 93 | "GstCheck-1.0", 94 | "GstController-1.0", 95 | "GstCuda-1.0", 96 | // "GstDxva-1.0", // Not usable on Linux 97 | "GstGL-1.0", 98 | "GstGLEGL-1.0", 99 | "GstGLWayland-1.0", 100 | "GstGLX11-1.0", 101 | "GstInsertBin-1.0", 102 | "GstMpegts-1.0", 103 | "GstMse-1.0", 104 | "GstNet-1.0", 105 | "GstPbutils-1.0", 106 | "GstPlay-1.0", 107 | "GstPlayer-1.0", 108 | "GstRtp-1.0", 109 | "GstRtsp-1.0", 110 | "GstSdp-1.0", 111 | "GstTag-1.0", 112 | "GstTranscoder-1.0", 113 | "GstVa-1.0", 114 | "GstVideo-1.0", 115 | // "GstVulkan-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 116 | // "GstVulkanWayland-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 117 | // "GstVulkanXCB-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 118 | "GstWebRTC-1.0", 119 | "Gtk-3.0", 120 | "Gtk-4.0", 121 | "GtkSource-5", 122 | "GUdev-1.0", 123 | "Handy-1", 124 | "HarfBuzz-0.0", 125 | "IBus-1.0", 126 | "JavaScriptCore-4.1", 127 | "JavaScriptCore-6.0", 128 | "Json-1.0", 129 | "Libproxy-1.0", 130 | "libxml2-2.0", 131 | "Manette-0.2", 132 | "Nice-0.1", 133 | "Notify-0.7", 134 | "Pango-1.0", 135 | "PangoCairo-1.0", 136 | "PangoFc-1.0", 137 | "PangoFT2-1.0", 138 | "PangoOT-1.0", 139 | "Polkit-1.0", 140 | "Rsvg-2.0", 141 | "Secret-1", 142 | "Soup-3.0", 143 | "Tracker-3.0", 144 | "Tsparql-3.0", 145 | // "Vulkan-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 146 | "WebKit2-4.1", 147 | "WebKit2WebExtension-4.1", 148 | "WebKit-6.0", 149 | "WebKitWebProcessExtension-6.0", 150 | "win32-1.0", 151 | "xfixes-4.0", 152 | "xft-2.0", 153 | "xlib-2.0", 154 | "Xmlb-2.0", 155 | "xrandr-1.3", 156 | }, 157 | .gnome48 => &.{ 158 | "Adw-1", 159 | "AppStream-1.0", 160 | "AppStreamCompose-1.0", 161 | "Atk-1.0", 162 | "Atspi-2.0", 163 | "cairo-1.0", 164 | "CudaGst-1.0", 165 | "DBus-1.0", 166 | "Dex-1", 167 | "fontconfig-2.0", 168 | "freetype2-2.0", 169 | "GCab-1.0", 170 | "Gck-1", 171 | "Gck-2", 172 | "Gcr-3", 173 | "Gcr-4", 174 | "GcrUi-3", 175 | "GDesktopEnums-3.0", 176 | "Gdk-3.0", 177 | "Gdk-4.0", 178 | "GdkPixbuf-2.0", 179 | "GdkPixdata-2.0", 180 | "GdkWayland-4.0", 181 | "GdkX11-3.0", 182 | "GdkX11-4.0", 183 | "Gee-0.8", 184 | "Geoclue-2.0", 185 | "Gio-2.0", 186 | "GioUnix-2.0", 187 | "GIRepository-2.0", 188 | "GIRepository-3.0", 189 | "GL-1.0", 190 | "GLib-2.0", 191 | "GLibUnix-2.0", 192 | "GModule-2.0", 193 | "GObject-2.0", 194 | "Graphene-1.0", 195 | "Gsk-4.0", 196 | "Gst-1.0", 197 | "GstAllocators-1.0", 198 | "GstAnalytics-1.0", 199 | "GstApp-1.0", 200 | "GstAudio-1.0", 201 | "GstBadAudio-1.0", 202 | "GstBase-1.0", 203 | "GstCheck-1.0", 204 | "GstController-1.0", 205 | "GstCuda-1.0", 206 | // "GstDxva-1.0", // Not usable on Linux 207 | "GstGL-1.0", 208 | "GstGLEGL-1.0", 209 | "GstGLWayland-1.0", 210 | "GstGLX11-1.0", 211 | "GstInsertBin-1.0", 212 | "GstMpegts-1.0", 213 | "GstMse-1.0", 214 | "GstNet-1.0", 215 | "GstPbutils-1.0", 216 | "GstPlay-1.0", 217 | "GstPlayer-1.0", 218 | "GstRtp-1.0", 219 | "GstRtsp-1.0", 220 | "GstSdp-1.0", 221 | "GstTag-1.0", 222 | "GstTranscoder-1.0", 223 | "GstVa-1.0", 224 | "GstVideo-1.0", 225 | // "GstVulkan-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 226 | // "GstVulkanWayland-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 227 | // "GstVulkanXCB-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 228 | "GstWebRTC-1.0", 229 | "Gtk-3.0", 230 | "Gtk-4.0", 231 | "GtkSource-5", 232 | "GUdev-1.0", 233 | "Handy-1", 234 | "HarfBuzz-0.0", 235 | "IBus-1.0", 236 | "JavaScriptCore-4.1", 237 | "JavaScriptCore-6.0", 238 | "Json-1.0", 239 | "Libproxy-1.0", 240 | "libxml2-2.0", 241 | "Manette-0.2", 242 | "Nice-0.1", 243 | "Notify-0.7", 244 | "Pango-1.0", 245 | "PangoCairo-1.0", 246 | "PangoFc-1.0", 247 | "PangoFT2-1.0", 248 | "PangoOT-1.0", 249 | "Polkit-1.0", 250 | "Rsvg-2.0", 251 | "Secret-1", 252 | "Soup-3.0", 253 | "Tracker-3.0", 254 | "Tsparql-3.0", 255 | // "Vulkan-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 256 | "WebKit2-4.1", 257 | "WebKit2WebExtension-4.1", 258 | "WebKit-6.0", 259 | "WebKitWebProcessExtension-6.0", 260 | "win32-1.0", 261 | "xfixes-4.0", 262 | "xft-2.0", 263 | "xlib-2.0", 264 | "Xmlb-2.0", 265 | "xrandr-1.3", 266 | }, 267 | } else &.{}; 268 | 269 | const gir_files_path = b.option([]const u8, "gir-files-path", "Path to GIR files") orelse "/usr/share/gir-1.0"; 270 | 271 | const codegen_cmd = b.addRunArtifact(exe); 272 | 273 | codegen_cmd.addPrefixedDirectoryArg("--gir-dir=", .{ .cwd_relative = gir_files_path }); 274 | codegen_cmd.addPrefixedDirectoryArg("--gir-fixes-dir=", b.path("gir-fixes")); 275 | codegen_cmd.addPrefixedDirectoryArg("--bindings-dir=", b.path("binding-overrides")); 276 | codegen_cmd.addPrefixedDirectoryArg("--extensions-dir=", b.path("extensions")); 277 | const bindings_dir = codegen_cmd.addPrefixedOutputDirectoryArg("--output-dir=", "bindings"); 278 | codegen_cmd.addPrefixedDirectoryArg("--abi-test-output-dir=", b.path("test/abi")); 279 | _ = codegen_cmd.addPrefixedDepFileOutputArg("--dependency-file=", "codegen-deps"); 280 | codegen_cmd.addArgs(codegen_modules); 281 | // This is needed to tell Zig that the command run can be cached despite 282 | // having output files. 283 | codegen_cmd.expectExitCode(0); 284 | 285 | const install_bindings = b.addInstallDirectory(.{ 286 | .source_dir = bindings_dir, 287 | .install_dir = .prefix, 288 | .install_subdir = "bindings", 289 | }); 290 | 291 | const codegen_step = b.step("codegen", "Generate all bindings"); 292 | codegen_step.dependOn(&install_bindings.step); 293 | } 294 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .gobject_codegen, 3 | .version = "0.3.0", 4 | .fingerprint = 0x9edb5c54cdea7d07, // Changing this has security and trust implications. 5 | .minimum_zig_version = "0.14.0", 6 | .paths = .{ 7 | "binding-overrides", 8 | "extensions", 9 | "gir-fixes", 10 | "src", 11 | "LICENSE", 12 | "README.md", 13 | "build.zig", 14 | "build.zig.zon", 15 | }, 16 | .dependencies = .{ 17 | .xml = .{ 18 | .url = "git+https://github.com/ianprime0509/zig-xml?ref=main#7c1697f35065ab54088d268ef52abf4c53dc7d62", 19 | .hash = "xml-0.1.0-ZTbP3_47AgClPn_55oc3J5FaewBcphmzZifp-vLd5WpG", 20 | }, 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /doc/binding-strategy.md: -------------------------------------------------------------------------------- 1 | # Binding strategy 2 | 3 | Most of the bindings generated by zig-gobject are direct translations of the 4 | source GIR (GObject introspection metadata), adjusted to better fit Zig 5 | conventions (such as `camelCase` function names). For example, the function 6 | `gtk_application_new` is translated to `gtk.Application.new`. However, there are 7 | some additional elements added by the translation process to expose metadata 8 | (such as type inheritance) and make common tasks simpler and safer. 9 | 10 | The generated bindings are designed to provide direct transparency to the 11 | underlying library functions. For example, consider the source of 12 | `gtk.Application.new`: 13 | 14 | ```zig 15 | extern fn gtk_application_new(p_application_id: ?[*:0]const u8, p_flags: gio.ApplicationFlags) *_Self; 16 | pub const new = gtk_application_new; 17 | ``` 18 | 19 | The generated binding here is really a _binding_ rather than a _wrapper_: the 20 | function `gtk.Application.new` is exactly the same as `gtk_application_new` at 21 | the binary level. This is similar to the philosophy behind `zig translate-c`, 22 | but the GIR data offers much greater organization and precision in the result, 23 | such as the use of the correct pointer type `?[*:0]const u8` for the application 24 | ID, rather than the limited `[*c]const u8` which `zig translate-c` would 25 | produce. 26 | 27 | ## Naming 28 | 29 | Zig and GObject-based libraries have different naming conventions in some 30 | aspects, and Zig also enforces more stringent naming practices in several ways: 31 | for example, fields and decls may not have the same name, names may not shadow 32 | others in their enclosing scope. The generated bindings rename the original 33 | library symbols as follows: 34 | 35 | - Namespace names: lowercased (e.g. `Gtk` becomes `gtk`). 36 | - Type names: remain the same, as GObject and Zig both use `PascalCase`. 37 | - Exception: type names which would conflict with one of the built-in 38 | metadata fields are "mangled" by adding a trailing `_`. For example, a type 39 | in a library originally named `Class` will be translated as `Class_`. 40 | - Function names: translated from `snake_case` into `camelCase` (e.g. 41 | `signal_connect_data` becomes `signalConnectData`). 42 | - Constant names: remain the same. Although Zig prefers lowercase constant 43 | names, in contrast to GObject's `SCREAMING_SNAKE_CASE`, uniformly lowercasing 44 | constant names would lead to some conflicts (e.g. GDK's `KEY_A` and `KEY_a` 45 | would become ambiguous). 46 | - Enum and bit field (flags) members: remain the same (`snake_case`). 47 | - Field names: remain the same, but prefixed with `f_` to avoid potentially 48 | conflicting with other declarations on the types. 49 | - Parameter names: remain the same, but prefixed with `p_` to avoid potentially 50 | shadowing other names in the enclosing scope. 51 | 52 | ## Usage conventions 53 | 54 | While it is possible for functions in Zig to be called using method call syntax 55 | `obj.method()` if `method` has the type of `obj` as its first parameter, most 56 | methods in this library are conventionally called with the more verbose syntax 57 | `Obj.method(obj)`. The primary reason for this is visual consistency between 58 | normal methods, type-safe generated helpers, and extensions: 59 | 60 | - `gtk.Widget.show(win.as(gtk.Widget))` 61 | - `gtk.Button.signals.clicked.connect(button, Data, &handleButtonClicked, data, .{})` 62 | - `gtk.ext.WidgetClass.setTemplateFromSlice(class.as(gtk.Widget.Class), template)` 63 | 64 | It is also hoped that [the `@Result` builtin 65 | proposal](https://github.com/ziglang/zig/issues/16313) will be accepted, which 66 | would allow the elimination of redundant information from the `as` calls. 67 | 68 | It is up to the user to decide when to use this more verbose method call syntax 69 | or the shorter `obj.method()` syntax. There are cases where the above reasoning 70 | doesn't apply, and using the shorter syntax is desirable: for example, when 71 | working with Cairo types, which don't have signal handlers or other helpers, it 72 | is much nicer to write `cr.moveTo(0, 0)` than `cairo.Context.moveTo(cr, 0, 0)`. 73 | 74 | ## Extensions 75 | 76 | Most additional functionality provided by zig-gobject on top of the libraries 77 | being bound is added through _extensions_. These extensions are not added 78 | directly to the generated bindings; rather, the extensions file for a namespace 79 | is exposed as `ext` from the bindings for the namespace. For example, the 80 | extensions for GObject can be accessed through `gobject.ext`. 81 | 82 | It is conventional for the extensions of a namespace to mirror the structure of 83 | the namespace being extended. For example, the function 84 | `glib.ext.Bytes.newFromSlice` is a helper function which creates a `glib.Bytes` 85 | from a slice of bytes. 86 | 87 | ## Type system metadata 88 | 89 | GObject is built around an [object-oriented type 90 | system](https://docs.gtk.org/gobject/concepts.html). zig-gobject exposes 91 | metadata about relationships in the type system through a few special members: 92 | 93 | - `fn getGObjectType() gobject.Type` - this is the GObject "get-type" function 94 | for a type, returning the registered `gobject.Type` for the type. For example, 95 | the C macro `GTK_TYPE_APPLICATION` can be expressed as 96 | `gtk.Application.getGObjectType()` in Zig. 97 | - `const Class: type` - for a class type, this is the associated class struct. 98 | For example, `GObjectClass` in C is equivalent to `gobject.Object.Class` in 99 | Zig. 100 | - `const Iface: type` - for an interface type, this is the associated interface 101 | struct. 102 | - `const Parent: type` - for a class type, this is the parent type. For example, 103 | `gtk.ApplicationWindow.Parent` is the same as `gtk.Window`. 104 | - `const Implements: [_]type` - for a class type, this is an array of all the 105 | interface types implemented by the class. For example, `gtk.Window.Implements` 106 | contains several types, including `gtk.Buildable`. 107 | - `const Prerequisites: [_]type` - for an interface type, this is an array of all 108 | the prerequisite types of the interface. 109 | - `virtual_methods` - a namespace containing virtual method metadata 110 | - `properties` - a namespace containing property metadata 111 | - `signals` - a namespace containing signal metadata 112 | 113 | As an example of how these additional members are useful, the function 114 | `gobject.ext.as` casts an object instance to another type, failing to compile if 115 | the correctness of the cast cannot be guaranteed. For example, if `win` is a 116 | `gtk.Window`, then the call `gobject.ext.as(gobject.Object, win)` works, but 117 | `gobject.ext.as(gtk.ApplicationWindow, win)` will fail to compile, because `win` 118 | might not be an instance of `gtk.ApplicationWindow`. 119 | 120 | ## Virtual methods 121 | 122 | Each element in the `virtual_methods` namespace has the following structure: 123 | 124 | ```zig 125 | pub fn call( 126 | /// The type struct instance on which to call the method. 127 | class: anytype, 128 | ...method parameters... 129 | ) ...method return type... 130 | 131 | pub fn implement( 132 | /// The type struct instance on which to implement the method. 133 | class: anytype, 134 | /// The implementation of the method. 135 | impl: *const fn(*@typeInfo(@TypeOf(class)).pointer.child.Instance, ...method parameters...) ...method return type..., 136 | ) 137 | ``` 138 | 139 | For example, the virtual method `finalize` can be implemented for an object type 140 | using `gobject.Object.virtual_methods.finalize.implement`. This offers greater 141 | type safety than casting the type struct instance to an ancestor type and 142 | setting the method field directly. 143 | 144 | ## Properties 145 | 146 | Each element in the `properties` namespace has the following structure: 147 | 148 | ```zig 149 | pub const name = "property"; 150 | 151 | pub const Type = T; 152 | ``` 153 | 154 | ## Signals 155 | 156 | Each element in the `signals` namespace has the following structure: 157 | 158 | ```zig 159 | pub const name = "signal"; 160 | 161 | pub fn connect( 162 | /// The object to which to connect the signal handler. 163 | obj: anytype, 164 | /// The type of the user data to pass to the handler. 165 | comptime T: type, 166 | /// The signal handler function. 167 | callback: *const fn (@TypeOf(obj), ...signal parameters..., T), 168 | /// User data to pass to the handler. 169 | data: T, 170 | /// Signal connection options. 171 | options: struct { after: bool = false }, 172 | ) 173 | ``` 174 | 175 | Using these generated signal connection functions offers greater type safety 176 | than calling `gobject.signalConnectData` directly. 177 | 178 | ## Utility functions 179 | 180 | Some utility functions are added to the generated types to improve the safety 181 | and ease of use of the bindings. These utility functions are chosen judiciously: 182 | most additional utility functions are available through extensions rather than 183 | translated directly into the bindings, to avoid confusion and collision with the 184 | rest of the translated bindings. 185 | 186 | - `fn as(self: *Self, comptime T: type) *T` - for a class, interface, or type 187 | struct type, this is a shortcut for `gobject.ext.as`, due to its extremely 188 | frequent use. 189 | - `fn ref(self: *Self) void` - for a type with a reference function defined in 190 | its GIR, or for a type known to extend from `gobject.Object`, this is a 191 | function used to increment the object's reference count. This is generated 192 | only if the containing type does not already have a member translated as 193 | `ref`. 194 | - `fn unref(self: *Self) void` - like `ref`, but decrements the object's 195 | reference count. 196 | -------------------------------------------------------------------------------- /example/build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build) !void { 4 | const target = b.standardTargetOptions(.{}); 5 | const optimize = b.standardOptimizeOption(.{}); 6 | 7 | const gobject = b.dependency("gobject", .{ 8 | .target = target, 9 | .optimize = optimize, 10 | }); 11 | 12 | const exe = b.addExecutable(.{ 13 | .name = "zig-gobject-examples", 14 | .root_source_file = b.path("src/main.zig"), 15 | .target = target, 16 | .optimize = optimize, 17 | }); 18 | exe.root_module.addImport("glib", gobject.module("glib2")); 19 | exe.root_module.addImport("gobject", gobject.module("gobject2")); 20 | exe.root_module.addImport("gio", gobject.module("gio2")); 21 | exe.root_module.addImport("cairo", gobject.module("cairo1")); 22 | exe.root_module.addImport("pango", gobject.module("pango1")); 23 | exe.root_module.addImport("pangocairo", gobject.module("pangocairo1")); 24 | exe.root_module.addImport("gdk", gobject.module("gdk4")); 25 | exe.root_module.addImport("gtk", gobject.module("gtk4")); 26 | b.installArtifact(exe); 27 | 28 | const run_cmd = b.addRunArtifact(exe); 29 | run_cmd.step.dependOn(b.getInstallStep()); 30 | if (b.args) |args| { 31 | run_cmd.addArgs(args); 32 | } 33 | 34 | const run_step = b.step("run", "Run the example launcher"); 35 | run_step.dependOn(&run_cmd.step); 36 | } 37 | -------------------------------------------------------------------------------- /example/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .gobject_example, 3 | .version = "0.0.0", 4 | .fingerprint = 0xb1f52be6e638a6a8, // Changing this has security and trust implications. 5 | .minimum_zig_version = "0.14.0", 6 | .paths = .{""}, 7 | .dependencies = .{ 8 | .gobject = .{ 9 | .path = "../zig-out/bindings", 10 | }, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /example/src/custom_class.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const gtk = @import("gtk"); 3 | const gio = @import("gio"); 4 | const gobject = @import("gobject"); 5 | const glib = @import("glib"); 6 | 7 | const ExampleApplication = extern struct { 8 | parent_instance: Parent, 9 | 10 | pub const Parent = gtk.Application; 11 | 12 | pub const getGObjectType = gobject.ext.defineClass(ExampleApplication, .{ 13 | .classInit = &Class.init, 14 | }); 15 | 16 | pub fn new() *ExampleApplication { 17 | return gobject.ext.newInstance(ExampleApplication, .{ 18 | .application_id = "org.gtk.exampleapp", 19 | .flags = gio.ApplicationFlags{ .handles_open = true }, 20 | }); 21 | } 22 | 23 | pub fn as(app: *ExampleApplication, comptime T: type) *T { 24 | return gobject.ext.as(T, app); 25 | } 26 | 27 | fn activateImpl(app: *ExampleApplication) callconv(.c) void { 28 | const win = ExampleApplicationWindow.new(app); 29 | gtk.Window.present(win.as(gtk.Window)); 30 | } 31 | 32 | pub const Class = extern struct { 33 | parent_class: Parent.Class, 34 | 35 | pub const Instance = ExampleApplication; 36 | 37 | pub fn as(class: *Class, comptime T: type) *T { 38 | return gobject.ext.as(T, class); 39 | } 40 | 41 | fn init(class: *Class) callconv(.c) void { 42 | gio.Application.virtual_methods.activate.implement(class, &activateImpl); 43 | } 44 | }; 45 | }; 46 | 47 | const ExampleApplicationWindow = extern struct { 48 | parent_instance: Parent, 49 | 50 | const template = 51 | \\ 52 | \\ 53 | \\ 81 | \\ 82 | ; 83 | 84 | pub const Parent = gtk.ApplicationWindow; 85 | 86 | const Private = struct { 87 | button: *ExampleButton, 88 | 89 | var offset: c_int = 0; 90 | }; 91 | 92 | pub const getGObjectType = gobject.ext.defineClass(ExampleApplicationWindow, .{ 93 | .instanceInit = &init, 94 | .classInit = &Class.init, 95 | .parent_class = &Class.parent, 96 | .private = .{ .Type = Private, .offset = &Private.offset }, 97 | }); 98 | 99 | pub fn new(app: *ExampleApplication) *ExampleApplicationWindow { 100 | return gobject.ext.newInstance(ExampleApplicationWindow, .{ 101 | .application = app, 102 | }); 103 | } 104 | 105 | pub fn as(win: *ExampleApplicationWindow, comptime T: type) *T { 106 | return gobject.ext.as(T, win); 107 | } 108 | 109 | fn init(win: *ExampleApplicationWindow, _: *Class) callconv(.c) void { 110 | gtk.Widget.initTemplate(win.as(gtk.Widget)); 111 | 112 | _ = ExampleButton.signals.counter_incremented.connect(win.private().button, ?*anyopaque, &handleIncremented, null, .{}); 113 | } 114 | 115 | fn dispose(win: *ExampleApplicationWindow) callconv(.c) void { 116 | gtk.Widget.disposeTemplate(win.as(gtk.Widget), getGObjectType()); 117 | gobject.Object.virtual_methods.dispose.call(Class.parent, win.as(Parent)); 118 | } 119 | 120 | fn handleIncremented(_: *ExampleButton, new_value: c_uint, _: ?*anyopaque) callconv(.c) void { 121 | std.debug.print("New button value: {}\n", .{new_value}); 122 | } 123 | 124 | fn private(win: *ExampleApplicationWindow) *Private { 125 | return gobject.ext.impl_helpers.getPrivate(win, Private, Private.offset); 126 | } 127 | 128 | pub const Class = extern struct { 129 | parent_class: Parent.Class, 130 | 131 | var parent: *Parent.Class = undefined; 132 | 133 | pub const Instance = ExampleApplicationWindow; 134 | 135 | pub fn as(class: *Class, comptime T: type) *T { 136 | return gobject.ext.as(T, class); 137 | } 138 | 139 | fn init(class: *Class) callconv(.c) void { 140 | gobject.Object.virtual_methods.dispose.implement(class, &dispose); 141 | gtk.ext.WidgetClass.setTemplateFromSlice(class.as(gtk.WidgetClass), template); 142 | class.bindTemplateChildPrivate("button", .{}); 143 | } 144 | 145 | fn bindTemplateChildPrivate(class: *Class, comptime name: [:0]const u8, comptime options: gtk.ext.BindTemplateChildOptions) void { 146 | gtk.ext.impl_helpers.bindTemplateChildPrivate(class, name, Private, Private.offset, options); 147 | } 148 | }; 149 | }; 150 | 151 | const ExampleButton = extern struct { 152 | parent_instance: Parent, 153 | 154 | pub const Parent = gtk.Button; 155 | 156 | const Private = struct { 157 | counter: c_uint, 158 | 159 | var offset: c_int = 0; 160 | }; 161 | 162 | pub const getGObjectType = gobject.ext.defineClass(ExampleButton, .{ 163 | .instanceInit = &init, 164 | .classInit = &Class.init, 165 | .private = .{ .Type = Private, .offset = &Private.offset }, 166 | }); 167 | 168 | pub const signals = struct { 169 | pub const counter_incremented = struct { 170 | pub const name = "counter-incremented"; 171 | pub const connect = impl.connect; 172 | const impl = gobject.ext.defineSignal(name, ExampleButton, &.{c_uint}, void); 173 | }; 174 | }; 175 | 176 | pub const properties = struct { 177 | pub const counter = struct { 178 | pub const name = "counter"; 179 | const impl = gobject.ext.defineProperty(name, ExampleButton, c_uint, .{ 180 | .nick = "Counter", 181 | .blurb = "The value of the counter.", 182 | .minimum = 0, 183 | .maximum = std.math.maxInt(c_uint), 184 | .default = 0, 185 | .accessor = gobject.ext.privateFieldAccessor(ExampleButton, Private, &Private.offset, "counter"), 186 | }); 187 | }; 188 | }; 189 | 190 | pub fn as(button: *ExampleButton, comptime T: type) *T { 191 | return gobject.ext.as(T, button); 192 | } 193 | 194 | fn init(button: *ExampleButton, _: *Class) callconv(.c) void { 195 | // TODO: actually, the label should just be implemented using GtkExpression or something 196 | _ = gobject.Object.signals.notify.connect(button, ?*anyopaque, &handleNotifyCounter, null, .{ .detail = "counter" }); 197 | _ = gtk.Button.signals.clicked.connect(button, ?*anyopaque, &handleClicked, null, .{}); 198 | } 199 | 200 | fn handleNotifyCounter(button: *ExampleButton, _: *gobject.ParamSpec, _: ?*anyopaque) callconv(.c) void { 201 | button.updateLabel(); 202 | } 203 | 204 | fn handleClicked(button: *ExampleButton, _: ?*anyopaque) callconv(.c) void { 205 | var counter = gobject.ext.Value.new(c_uint); 206 | defer counter.unset(); 207 | gobject.Object.getProperty(button.as(gobject.Object), "counter", &counter); 208 | gobject.ext.Value.set(&counter, gobject.ext.Value.get(&counter, c_uint) +| 1); 209 | gobject.Object.setProperty(button.as(gobject.Object), "counter", &counter); 210 | signals.counter_incremented.impl.emit(button, null, .{gobject.ext.Value.get(&counter, c_uint)}, null); 211 | } 212 | 213 | fn updateLabel(button: *ExampleButton) void { 214 | var buf: [64]u8 = undefined; 215 | gtk.Button.setLabel(button.as(gtk.Button), std.fmt.bufPrintZ(&buf, "Clicked: {}", .{button.private().counter}) catch unreachable); 216 | } 217 | 218 | fn private(button: *ExampleButton) *Private { 219 | return gobject.ext.impl_helpers.getPrivate(button, Private, Private.offset); 220 | } 221 | 222 | pub const Class = extern struct { 223 | parent_class: Parent.Class, 224 | 225 | pub const Instance = ExampleButton; 226 | 227 | pub fn as(class: *Class, comptime T: type) *T { 228 | return gobject.ext.as(T, class); 229 | } 230 | 231 | fn init(class: *Class) callconv(.c) void { 232 | signals.counter_incremented.impl.register(.{}); 233 | gobject.ext.registerProperties(class, &.{ 234 | properties.counter.impl, 235 | }); 236 | } 237 | }; 238 | }; 239 | 240 | pub fn main() void { 241 | const status = gio.Application.run(ExampleApplication.new().as(gio.Application), @intCast(std.os.argv.len), std.os.argv.ptr); 242 | std.process.exit(@intCast(status)); 243 | } 244 | -------------------------------------------------------------------------------- /example/src/custom_drawing.zig: -------------------------------------------------------------------------------- 1 | // https://docs.gtk.org/gtk4/getting_started.html#custom-drawing 2 | 3 | const std = @import("std"); 4 | const gio = @import("gio"); 5 | const gtk = @import("gtk"); 6 | const gdk = @import("gdk"); 7 | const cairo = @import("cairo"); 8 | const gobject = @import("gobject"); 9 | 10 | pub fn main() void { 11 | const app = gtk.Application.new("org.gtk.example", .{}); 12 | _ = gio.Application.signals.activate.connect(app, ?*anyopaque, &activate, null, .{}); 13 | const status = gio.Application.run(app.as(gio.Application), @intCast(std.os.argv.len), std.os.argv.ptr); 14 | std.process.exit(@intCast(status)); 15 | } 16 | 17 | fn activate(app: *gtk.Application, _: ?*anyopaque) callconv(.c) void { 18 | const window = gtk.ApplicationWindow.new(app); 19 | gtk.Window.setTitle(window.as(gtk.Window), "Drawing Area"); 20 | 21 | _ = gtk.Widget.signals.destroy.connect(window, ?*anyopaque, &closeWindow, null, .{}); 22 | 23 | const frame = gtk.Frame.new(null); 24 | gtk.Window.setChild(window.as(gtk.Window), frame.as(gtk.Widget)); 25 | 26 | const drawing_area = gtk.DrawingArea.new(); 27 | gtk.Widget.setSizeRequest(drawing_area.as(gtk.Widget), 100, 100); 28 | 29 | gtk.Frame.setChild(frame, drawing_area.as(gtk.Widget)); 30 | 31 | gtk.DrawingArea.setDrawFunc(drawing_area, &drawCb, null, null); 32 | 33 | _ = gtk.DrawingArea.signals.resize.connect(drawing_area, ?*anyopaque, &resizeCb, null, .{ .after = true }); 34 | 35 | const drag = gtk.GestureDrag.new(); 36 | gtk.GestureSingle.setButton(drag.as(gtk.GestureSingle), gdk.BUTTON_PRIMARY); 37 | gtk.Widget.addController(drawing_area.as(gtk.Widget), drag.as(gtk.EventController)); 38 | _ = gtk.GestureDrag.signals.drag_begin.connect(drag, *gtk.DrawingArea, &dragBegin, drawing_area, .{}); 39 | _ = gtk.GestureDrag.signals.drag_update.connect(drag, *gtk.DrawingArea, &dragUpdate, drawing_area, .{}); 40 | _ = gtk.GestureDrag.signals.drag_end.connect(drag, *gtk.DrawingArea, &dragEnd, drawing_area, .{}); 41 | 42 | const press = gtk.GestureClick.new(); 43 | gtk.GestureSingle.setButton(press.as(gtk.GestureSingle), gdk.BUTTON_SECONDARY); 44 | gtk.Widget.addController(drawing_area.as(gtk.Widget), press.as(gtk.EventController)); 45 | _ = gtk.GestureClick.signals.pressed.connect(press, *gtk.DrawingArea, &pressed, drawing_area, .{}); 46 | 47 | gtk.Widget.show(window.as(gtk.Widget)); 48 | } 49 | 50 | var surface: ?*cairo.Surface = null; 51 | 52 | fn clearSurface() callconv(.c) void { 53 | const cr = cairo.Context.create(surface orelse return); 54 | defer cr.destroy(); 55 | 56 | cr.setSourceRgb(1, 1, 1); 57 | cr.paint(); 58 | } 59 | 60 | fn resizeCb(widget: *gtk.DrawingArea, _: c_int, _: c_int, _: ?*anyopaque) callconv(.c) void { 61 | if (surface) |s| { 62 | s.destroy(); 63 | surface = null; 64 | } 65 | 66 | const native = gtk.Widget.getNative(widget.as(gtk.Widget)) orelse return; 67 | const width = gtk.Widget.getWidth(widget.as(gtk.Widget)); 68 | const height = gtk.Widget.getHeight(widget.as(gtk.Widget)); 69 | const native_surface = gtk.Native.getSurface(native) orelse return; 70 | surface = native_surface.createSimilarSurface(cairo.Content.color, width, height); 71 | 72 | // Initialize the surface to white 73 | clearSurface(); 74 | } 75 | 76 | fn drawCb(_: *gtk.DrawingArea, cr: *cairo.Context, _: c_int, _: c_int, _: ?*anyopaque) callconv(.c) void { 77 | cr.setSourceSurface(surface orelse return, 0, 0); 78 | cr.paint(); 79 | } 80 | 81 | fn drawBrush(widget: *gtk.DrawingArea, x: f64, y: f64) callconv(.c) void { 82 | const cr = cairo.Context.create(surface orelse return); 83 | defer cr.destroy(); 84 | 85 | cr.rectangle(x - 3, y - 3, 6, 6); 86 | cr.fill(); 87 | 88 | gtk.Widget.queueDraw(widget.as(gtk.Widget)); 89 | } 90 | 91 | var start_x: f64 = 0; 92 | var start_y: f64 = 0; 93 | 94 | fn dragBegin(_: *gtk.GestureDrag, x: f64, y: f64, area: *gtk.DrawingArea) callconv(.c) void { 95 | start_x = x; 96 | start_y = y; 97 | 98 | drawBrush(area, x, y); 99 | } 100 | 101 | fn dragUpdate(_: *gtk.GestureDrag, x: f64, y: f64, area: *gtk.DrawingArea) callconv(.c) void { 102 | drawBrush(area, start_x + x, start_y + y); 103 | } 104 | 105 | fn dragEnd(_: *gtk.GestureDrag, x: f64, y: f64, area: *gtk.DrawingArea) callconv(.c) void { 106 | drawBrush(area, start_x + x, start_y + y); 107 | } 108 | 109 | fn pressed(_: *gtk.GestureClick, _: c_int, _: f64, _: f64, area: *gtk.DrawingArea) callconv(.c) void { 110 | clearSurface(); 111 | gtk.Widget.queueDraw(area.as(gtk.Widget)); 112 | } 113 | 114 | fn closeWindow(_: *gtk.ApplicationWindow, _: ?*anyopaque) callconv(.c) void { 115 | if (surface) |s| { 116 | s.destroy(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /example/src/hello_world.zig: -------------------------------------------------------------------------------- 1 | // https://docs.gtk.org/gtk4/getting_started.html#hello-world 2 | 3 | const std = @import("std"); 4 | const glib = @import("glib"); 5 | const gobject = @import("gobject"); 6 | const gio = @import("gio"); 7 | const gtk = @import("gtk"); 8 | 9 | pub fn main() void { 10 | var app = gtk.Application.new("org.gtk.example", .{}); 11 | defer app.unref(); 12 | _ = gio.Application.signals.activate.connect(app, ?*anyopaque, &activate, null, .{}); 13 | const status = gio.Application.run(app.as(gio.Application), @intCast(std.os.argv.len), std.os.argv.ptr); 14 | std.process.exit(@intCast(status)); 15 | } 16 | 17 | fn activate(app: *gtk.Application, _: ?*anyopaque) callconv(.c) void { 18 | var window = gtk.ApplicationWindow.new(app); 19 | gtk.Window.setTitle(window.as(gtk.Window), "Window"); 20 | gtk.Window.setDefaultSize(window.as(gtk.Window), 200, 200); 21 | 22 | var box = gtk.Box.new(gtk.Orientation.vertical, 0); 23 | gtk.Widget.setHalign(box.as(gtk.Widget), gtk.Align.center); 24 | gtk.Widget.setValign(box.as(gtk.Widget), gtk.Align.center); 25 | 26 | gtk.Window.setChild(window.as(gtk.Window), box.as(gtk.Widget)); 27 | 28 | var button = gtk.Button.newWithLabel("Hello World"); 29 | 30 | _ = gtk.Button.signals.clicked.connect(button, ?*anyopaque, &printHello, null, .{}); 31 | _ = gtk.Button.signals.clicked.connect(button, *gtk.ApplicationWindow, &closeWindow, window, .{}); 32 | 33 | gtk.Box.append(box, button.as(gtk.Widget)); 34 | 35 | gtk.Widget.show(window.as(gtk.Widget)); 36 | } 37 | 38 | fn printHello(_: *gtk.Button, _: ?*anyopaque) callconv(.c) void { 39 | std.debug.print("Hello World\n", .{}); 40 | } 41 | 42 | fn closeWindow(_: *gtk.Button, window: *gtk.ApplicationWindow) callconv(.c) void { 43 | gtk.Window.destroy(window.as(gtk.Window)); 44 | } 45 | -------------------------------------------------------------------------------- /example/src/list_view.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const gtk = @import("gtk"); 3 | const gio = @import("gio"); 4 | const gobject = @import("gobject"); 5 | 6 | pub fn main() void { 7 | const app = gtk.Application.new("org.gtk.example", .{}); 8 | _ = gio.Application.signals.activate.connect(app, ?*anyopaque, &activate, null, .{}); 9 | const status = gio.Application.run(app.as(gio.Application), @intCast(std.os.argv.len), std.os.argv.ptr); 10 | std.process.exit(@intCast(status)); 11 | } 12 | 13 | fn activate(app: *gtk.Application, _: ?*anyopaque) callconv(.c) void { 14 | const window = gtk.ApplicationWindow.new(app); 15 | gtk.Window.setTitle(window.as(gtk.Window), "Window"); 16 | gtk.Window.setDefaultSize(window.as(gtk.Window), 600, 600); 17 | 18 | const scrolled_window = gtk.ScrolledWindow.new(); 19 | gtk.Window.setChild(window.as(gtk.Window), scrolled_window.as(gtk.Widget)); 20 | 21 | const list_model = NumberList.new(1_000_000); 22 | const selection_model = gtk.SingleSelection.new(list_model.as(gio.ListModel)); 23 | const item_factory = gtk.SignalListItemFactory.new(); 24 | _ = gtk.SignalListItemFactory.signals.setup.connect(item_factory, ?*anyopaque, &setupListItem, null, .{}); 25 | _ = gtk.SignalListItemFactory.signals.bind.connect(item_factory, ?*anyopaque, &bindListItem, null, .{}); 26 | const list_view = gtk.ListView.new(selection_model.as(gtk.SelectionModel), item_factory.as(gtk.ListItemFactory)); 27 | gtk.ScrolledWindow.setChild(scrolled_window, list_view.as(gtk.Widget)); 28 | 29 | gtk.Widget.show(window.as(gtk.Widget)); 30 | } 31 | 32 | fn setupListItem(_: *gtk.SignalListItemFactory, list_item_obj: *gobject.Object, _: ?*anyopaque) callconv(.c) void { 33 | const list_item = gobject.ext.cast(gtk.ListItem, list_item_obj).?; 34 | const label = gtk.Label.new(null); 35 | list_item.setChild(label.as(gtk.Widget)); 36 | } 37 | 38 | fn bindListItem(_: *gtk.SignalListItemFactory, list_item_obj: *gobject.Object, _: ?*anyopaque) callconv(.c) void { 39 | const list_item = gobject.ext.cast(gtk.ListItem, list_item_obj).?; 40 | const number = gobject.ext.cast(Number, list_item.getItem().?).?; 41 | const label = gobject.ext.cast(gtk.Label, list_item.getChild().?).?; 42 | var buf: [64]u8 = undefined; 43 | const text = std.fmt.bufPrintZ(&buf, "Value: {}", .{number.value}) catch unreachable; 44 | label.setLabel(text); 45 | } 46 | 47 | const Number = extern struct { 48 | parent_instance: Parent, 49 | value: c_uint, 50 | 51 | pub const Parent = gobject.Object; 52 | 53 | pub const getGObjectType = gobject.ext.defineClass(Number, .{ 54 | .classInit = &Class.init, 55 | }); 56 | 57 | pub const properties = struct { 58 | pub const value = struct { 59 | pub const name = "value"; 60 | const impl = gobject.ext.defineProperty(name, Number, c_uint, .{ 61 | .nick = "Value", 62 | .blurb = "The value of the number.", 63 | .minimum = std.math.minInt(c_uint), 64 | .maximum = std.math.maxInt(c_uint), 65 | .default = 0, 66 | .accessor = gobject.ext.fieldAccessor(Number, "value"), 67 | }); 68 | }; 69 | }; 70 | 71 | pub fn new(value: c_uint) *Number { 72 | return gobject.ext.newInstance(Number, .{ .value = value }); 73 | } 74 | 75 | pub fn as(number: *Number, comptime T: type) *T { 76 | return gobject.ext.as(T, number); 77 | } 78 | 79 | pub const Class = extern struct { 80 | parent_class: Parent.Class, 81 | 82 | pub const Instance = Number; 83 | 84 | pub fn as(class: *Class, comptime T: type) *T { 85 | return gobject.ext.as(T, class); 86 | } 87 | 88 | fn init(class: *Class) callconv(.c) void { 89 | gobject.ext.registerProperties(class, &.{ 90 | properties.value.impl, 91 | }); 92 | } 93 | }; 94 | }; 95 | 96 | const NumberList = extern struct { 97 | parent_instance: Parent, 98 | len: c_uint, 99 | 100 | pub const Parent = gobject.Object; 101 | pub const Implements = [_]type{gio.ListModel}; 102 | 103 | pub const getGObjectType = gobject.ext.defineClass(NumberList, .{ 104 | .classInit = &Class.init, 105 | .implements = &.{ 106 | gobject.ext.implement(gio.ListModel, .{ .init = Class.initListModel }), 107 | }, 108 | }); 109 | 110 | pub const properties = struct { 111 | pub const len = struct { 112 | pub const name = "len"; 113 | const impl = gobject.ext.defineProperty(name, NumberList, c_uint, .{ 114 | .nick = "Length", 115 | .blurb = "The length of the list.", 116 | .minimum = 0, 117 | .maximum = std.math.maxInt(c_uint), 118 | .default = 0, 119 | .accessor = .{ 120 | .getter = &getLenInternal, 121 | .setter = &setLenInternal, 122 | }, 123 | }); 124 | }; 125 | }; 126 | 127 | pub fn new(len: c_uint) *NumberList { 128 | return gobject.ext.newInstance(NumberList, .{ .len = len }); 129 | } 130 | 131 | pub fn as(list: *NumberList, comptime T: type) *T { 132 | return gobject.ext.as(T, list); 133 | } 134 | 135 | pub fn getItem(list_model: *gio.ListModel, position: c_uint) callconv(.c) ?*gobject.Object { 136 | const list = gobject.ext.cast(NumberList, list_model).?; 137 | return if (position < list.len) Number.new(position).as(gobject.Object) else null; 138 | } 139 | 140 | pub fn getItemType(_: *gio.ListModel) callconv(.c) gobject.Type { 141 | return Number.getGObjectType(); 142 | } 143 | 144 | pub fn getNItems(list_model: *gio.ListModel) callconv(.c) c_uint { 145 | const list = gobject.ext.cast(NumberList, list_model).?; 146 | return list.len; 147 | } 148 | 149 | fn getLenInternal(list: *NumberList) c_uint { 150 | return list.len; 151 | } 152 | 153 | fn setLenInternal(list: *NumberList, len: c_uint) void { 154 | const old_len = list.len; 155 | list.len = len; 156 | if (len > old_len) { 157 | gio.ListModel.itemsChanged(list.as(gio.ListModel), old_len, 0, len - old_len); 158 | } else if (len < old_len) { 159 | gio.ListModel.itemsChanged(list.as(gio.ListModel), len, old_len - len, 0); 160 | } 161 | } 162 | 163 | pub const Class = extern struct { 164 | parent_class: Parent.Class, 165 | 166 | pub const Instance = NumberList; 167 | 168 | pub fn as(class: *Class, comptime T: type) *T { 169 | return gobject.ext.as(T, class); 170 | } 171 | 172 | fn init(class: *Class) callconv(.c) void { 173 | gobject.ext.registerProperties(class, &.{ 174 | properties.len.impl, 175 | }); 176 | } 177 | 178 | fn initListModel(iface: *gio.ListModel.Iface) callconv(.c) void { 179 | gio.ListModel.virtual_methods.get_item.implement(iface, getItem); 180 | gio.ListModel.virtual_methods.get_item_type.implement(iface, getItemType); 181 | gio.ListModel.virtual_methods.get_n_items.implement(iface, getNItems); 182 | } 183 | }; 184 | }; 185 | -------------------------------------------------------------------------------- /example/src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const Example = struct { 4 | name: []const u8, 5 | main: *const fn () void, 6 | }; 7 | 8 | const examples: []const Example = &.{ 9 | .{ .name = "Hello world", .main = &@import("hello_world.zig").main }, 10 | .{ .name = "Custom drawing", .main = &@import("custom_drawing.zig").main }, 11 | .{ .name = "Custom class", .main = &@import("custom_class.zig").main }, 12 | .{ .name = "PangoCairo text rendering", .main = &@import("pango_cairo.zig").main }, 13 | .{ .name = "List view", .main = &@import("list_view.zig").main }, 14 | }; 15 | 16 | pub fn main() !void { 17 | const stdin = std.io.getStdIn().reader(); 18 | const stdout = std.io.getStdOut().writer(); 19 | 20 | try stdout.writeAll("Available examples:\n"); 21 | var i: usize = 0; 22 | for (examples) |example| { 23 | try stdout.print("{} - {s}\n", .{ i, example.name }); 24 | i += 1; 25 | } 26 | try stdout.writeAll("Choose an example: "); 27 | 28 | var buf: [16]u8 = undefined; 29 | const input = try stdin.readUntilDelimiter(&buf, '\n'); 30 | const choice = try std.fmt.parseInt(usize, input, 10); 31 | if (choice >= examples.len) { 32 | return error.OutOfBounds; 33 | } 34 | examples[choice].main(); 35 | } 36 | -------------------------------------------------------------------------------- /example/src/pango_cairo.zig: -------------------------------------------------------------------------------- 1 | // Adapted from https://docs.gtk.org/PangoCairo/pango_cairo.html#using-pango-with-cairo 2 | 3 | const std = @import("std"); 4 | const math = std.math; 5 | const gio = @import("gio"); 6 | const gtk = @import("gtk"); 7 | const cairo = @import("cairo"); 8 | const pango = @import("pango"); 9 | const pangocairo = @import("pangocairo"); 10 | 11 | const n_words = 10; 12 | const font = "Sans Bold 27"; 13 | 14 | fn draw(_: *gtk.DrawingArea, cr: *cairo.Context, draw_width: c_int, draw_height: c_int, _: ?*anyopaque) callconv(.c) void { 15 | cr.translate(@as(f64, @floatFromInt(draw_width)) / 2, @as(f64, @floatFromInt(draw_height)) / 2); 16 | const radius = @as(f64, @floatFromInt(@min(draw_width, draw_height))) / 2; 17 | 18 | const layout = pangocairo.createLayout(cr); 19 | defer layout.unref(); 20 | layout.setText("Text", -1); 21 | const desc = pango.FontDescription.fromString(font); 22 | defer desc.free(); 23 | layout.setFontDescription(desc); 24 | 25 | for (0..n_words) |i| { 26 | cr.save(); 27 | defer cr.restore(); 28 | 29 | const angle = 360 * @as(f64, @floatFromInt(i)) / n_words; 30 | const red = (1 + math.cos((angle - 60) * math.pi / 180)) / 2; 31 | cr.setSourceRgb(red, 0, 1 - red); 32 | cr.rotate(angle * math.pi / 180); 33 | pangocairo.updateLayout(cr, layout); 34 | 35 | var width: c_int = undefined; 36 | layout.getSize(&width, null); 37 | cr.moveTo(-@as(f64, @floatFromInt(width)) / @as(f64, @floatFromInt(pango.SCALE)) / 2, -radius); 38 | pangocairo.showLayout(cr, layout); 39 | } 40 | } 41 | 42 | fn activate(app: *gtk.Application, _: ?*anyopaque) callconv(.c) void { 43 | const window = gtk.ApplicationWindow.new(app); 44 | gtk.Window.setTitle(window.as(gtk.Window), "PangoCairo text example"); 45 | gtk.Window.setDefaultSize(window.as(gtk.Window), 300, 300); 46 | 47 | const drawing_area = gtk.DrawingArea.new(); 48 | gtk.Widget.setHexpand(drawing_area.as(gtk.Widget), 1); 49 | gtk.Widget.setVexpand(drawing_area.as(gtk.Widget), 1); 50 | _ = gtk.DrawingArea.setDrawFunc(drawing_area, &draw, null, null); 51 | gtk.Window.setChild(window.as(gtk.Window), drawing_area.as(gtk.Widget)); 52 | 53 | gtk.Widget.show(window.as(gtk.Widget)); 54 | } 55 | 56 | pub fn main() void { 57 | const app = gtk.Application.new("org.gtk.example", .{}); 58 | _ = gio.Application.signals.activate.connect(app, ?*anyopaque, &activate, null, .{}); 59 | const status = gio.Application.run(app.as(gio.Application), @intCast(std.os.argv.len), std.os.argv.ptr); 60 | std.process.exit(@intCast(status)); 61 | } 62 | -------------------------------------------------------------------------------- /extensions/glib2.zig: -------------------------------------------------------------------------------- 1 | const glib = @import("glib2"); 2 | const std = @import("std"); 3 | 4 | /// Creates a heap-allocated value of type `T` using `glib.malloc`. `T` must not 5 | /// be zero-sized or aligned more than `std.c.max_align_t`. 6 | pub fn create(comptime T: type) *T { 7 | if (@sizeOf(T) == 0) @compileError("zero-sized types not supported"); 8 | if (@alignOf(T) > @alignOf(std.c.max_align_t)) @compileError("overaligned types not supported"); 9 | return @ptrCast(@alignCast(glib.malloc(@sizeOf(T)))); 10 | } 11 | 12 | test create { 13 | const T = struct { a: u32, b: u64 }; 14 | const value = glib.ext.create(T); 15 | defer glib.ext.destroy(value); 16 | value.a = 123; 17 | value.b = 456; 18 | try std.testing.expectEqual(123, value.a); 19 | try std.testing.expectEqual(456, value.b); 20 | } 21 | 22 | /// Creates a heap-allocated copy of `value` using `glib.malloc`. `T` must not 23 | /// be zero-sized or aligned more than `std.c.max_align_t`. 24 | pub inline fn new(comptime T: type, value: T) *T { 25 | const new_value = create(T); 26 | new_value.* = value; 27 | return new_value; 28 | } 29 | 30 | test new { 31 | const T = struct { a: u32, b: u64 }; 32 | const value = glib.ext.new(T, .{ .a = 123, .b = 456 }); 33 | defer glib.ext.destroy(value); 34 | try std.testing.expectEqual(123, value.a); 35 | try std.testing.expectEqual(456, value.b); 36 | } 37 | 38 | /// Destroys a value created using `create`. 39 | pub fn destroy(ptr: anytype) void { 40 | const type_info = @typeInfo(@TypeOf(ptr)); 41 | if (type_info != .pointer or type_info.pointer.size != .one) @compileError("must be a single-item pointer"); 42 | glib.freeSized(@ptrCast(ptr), @sizeOf(type_info.pointer.child)); 43 | } 44 | 45 | test destroy { 46 | const T = struct { a: u32, b: u64 }; 47 | const value = glib.ext.create(T); 48 | defer glib.ext.destroy(value); 49 | value.a = 123; 50 | value.b = 456; 51 | try std.testing.expectEqual(123, value.a); 52 | try std.testing.expectEqual(456, value.b); 53 | } 54 | 55 | /// Heap allocates a slice of `n` values of type `T` using `glib.mallocN`. `T` 56 | /// must not be zero-sized or aligned more than `std.c.max_align_t`. 57 | pub fn alloc(comptime T: type, n: usize) []T { 58 | if (@sizeOf(T) == 0) @compileError("zero-sized types not supported"); 59 | if (@alignOf(T) > @alignOf(std.c.max_align_t)) @compileError("overaligned types not supported"); 60 | const ptr: [*]T = @ptrCast(@alignCast(glib.mallocN(@sizeOf(T), n))); 61 | return ptr[0..n]; 62 | } 63 | 64 | test alloc { 65 | const slice = glib.ext.alloc(u32, 3); 66 | defer glib.ext.free(slice); 67 | slice[0] = 1; 68 | slice[1] = 2; 69 | slice[2] = 3; 70 | try std.testing.expectEqual(1, slice[0]); 71 | try std.testing.expectEqual(2, slice[1]); 72 | try std.testing.expectEqual(3, slice[2]); 73 | } 74 | 75 | /// Frees a slice created using `alloc`. 76 | pub fn free(ptr: anytype) void { 77 | const type_info = @typeInfo(@TypeOf(ptr)); 78 | if (type_info != .pointer or type_info.pointer.size != .slice) @compileError("must be a slice"); 79 | glib.freeSized(@ptrCast(ptr.ptr), @sizeOf(type_info.pointer.child) * ptr.len); 80 | } 81 | 82 | test free { 83 | const slice = glib.ext.alloc(u32, 3); 84 | defer glib.ext.free(slice); 85 | slice[0] = 1; 86 | slice[1] = 2; 87 | slice[2] = 3; 88 | try std.testing.expectEqual(1, slice[0]); 89 | try std.testing.expectEqual(2, slice[1]); 90 | try std.testing.expectEqual(3, slice[2]); 91 | } 92 | 93 | pub const Bytes = struct { 94 | /// Returns a new `Bytes` copying the given slice. 95 | pub fn newFromSlice(bytes: []const u8) *glib.Bytes { 96 | return glib.Bytes.new(bytes.ptr, bytes.len); 97 | } 98 | 99 | /// Returns the byte data in `bytes` as a slice. 100 | pub fn getDataSlice(bytes: *glib.Bytes) []const u8 { 101 | var size: usize = undefined; 102 | const maybe_ptr = bytes.getData(&size); 103 | return if (maybe_ptr) |ptr| ptr[0..size] else &.{}; 104 | } 105 | 106 | test getDataSlice { 107 | const null_ptr = glib.Bytes.new(null, 0); 108 | defer null_ptr.unref(); 109 | try std.testing.expectEqualStrings("", glib.ext.Bytes.getDataSlice(null_ptr)); 110 | 111 | const empty = glib.ext.Bytes.newFromSlice(""); 112 | defer empty.unref(); 113 | try std.testing.expectEqualStrings("", glib.ext.Bytes.getDataSlice(empty)); 114 | 115 | const non_empty = glib.ext.Bytes.newFromSlice("Hello"); 116 | defer non_empty.unref(); 117 | try std.testing.expectEqualStrings("Hello", glib.ext.Bytes.getDataSlice(non_empty)); 118 | } 119 | }; 120 | 121 | pub const Variant = struct { 122 | /// Returns a new `Variant` with the given contents. 123 | /// 124 | /// This does not take ownership of the value (if applicable). 125 | pub fn newFrom(contents: anytype) *glib.Variant { 126 | const T = @TypeOf(contents); 127 | const type_info = @typeInfo(T); 128 | if (T == bool) { 129 | return glib.Variant.newBoolean(@intFromBool(contents)); 130 | } else if (T == u8) { 131 | return glib.Variant.newByte(contents); 132 | } else if (T == i16) { 133 | return glib.Variant.newInt16(contents); 134 | } else if (T == i32) { 135 | return glib.Variant.newInt32(contents); 136 | } else if (T == i64) { 137 | return glib.Variant.newInt64(contents); 138 | } else if (T == u16) { 139 | return glib.Variant.newUint16(contents); 140 | } else if (T == u32) { 141 | return glib.Variant.newUint32(contents); 142 | } else if (T == u64) { 143 | return glib.Variant.newUint64(contents); 144 | } else if (T == f64) { 145 | return glib.Variant.newDouble(contents); 146 | } else if (comptime isCString(T)) { 147 | return glib.Variant.newString(contents); 148 | } else if (T == *glib.Variant) { 149 | return glib.Variant.newVariant(contents); 150 | } else if (type_info == .array) { 151 | const child_type = glib.ext.VariantType.newFor(type_info.array.child); 152 | defer child_type.free(); 153 | var children: [type_info.array.len]*glib.Variant = undefined; 154 | inline for (contents, &children) |item, *child| { 155 | child.* = newFrom(item); 156 | } 157 | return glib.Variant.newArray(child_type, &children, children.len); 158 | } else if (type_info == .pointer and type_info.pointer.size == .slice) { 159 | const child_type = glib.ext.VariantType.newFor(type_info.pointer.child); 160 | defer child_type.free(); 161 | const children = alloc(*glib.Variant, contents.len); 162 | defer free(children); 163 | for (contents, children) |item, *child| { 164 | child.* = newFrom(item); 165 | } 166 | return glib.Variant.newArray(child_type, children.ptr, children.len); 167 | } else if (type_info == .optional) { 168 | const child_type = glib.ext.VariantType.newFor(type_info.optional.child); 169 | defer child_type.free(); 170 | if (contents) |value| { 171 | const child = newFrom(value); 172 | return glib.Variant.newMaybe(child_type, child); 173 | } else { 174 | return glib.Variant.newMaybe(child_type, null); 175 | } 176 | } else if (type_info == .@"struct" and type_info.@"struct".is_tuple) { 177 | var children: [type_info.@"struct".fields.len]*glib.Variant = undefined; 178 | inline for (type_info.@"struct".fields, &children) |field, *child| { 179 | child.* = newFrom(@field(contents, field.name)); 180 | } 181 | return glib.Variant.newTuple(&children, children.len); 182 | } else { 183 | @compileError("cannot construct variant from " ++ @typeName(T)); 184 | } 185 | } 186 | 187 | test "newFrom(integer)" { 188 | try testVariantNewFrom(u8, 123, glib.Variant.getByte); 189 | try testVariantNewFrom(i16, -12345, glib.Variant.getInt16); 190 | try testVariantNewFrom(i32, -(1 << 24), glib.Variant.getInt32); 191 | try testVariantNewFrom(i64, -(1 << 48), glib.Variant.getInt64); 192 | try testVariantNewFrom(u16, 12345, glib.Variant.getUint16); 193 | try testVariantNewFrom(u32, 1 << 24, glib.Variant.getUint32); 194 | try testVariantNewFrom(u64, 1 << 48, glib.Variant.getUint64); 195 | try testVariantNewFrom(f64, 3.1415926, glib.Variant.getDouble); 196 | } 197 | 198 | test "newFrom(bool)" { 199 | const variant = glib.ext.Variant.newFrom(true); 200 | defer variant.unref(); 201 | try std.testing.expectEqual(1, variant.getBoolean()); 202 | } 203 | 204 | test "newFrom(string literal)" { 205 | const variant = glib.ext.Variant.newFrom("Hello, world!"); 206 | defer variant.unref(); 207 | var len: usize = undefined; 208 | const string = variant.getString(&len); 209 | try std.testing.expectEqualStrings("Hello, world!", string[0..len]); 210 | } 211 | 212 | test "newFrom([*:0]const u8)" { 213 | const str: [*:0]const u8 = "Hello, world!"; 214 | const variant = glib.ext.Variant.newFrom(str); 215 | defer variant.unref(); 216 | var len: usize = undefined; 217 | const string = variant.getString(&len); 218 | try std.testing.expectEqualStrings("Hello, world!", string[0..len]); 219 | } 220 | 221 | test "newFrom([:0]const u8)" { 222 | const str: [:0]const u8 = "Hello, world!"; 223 | const variant = glib.ext.Variant.newFrom(str); 224 | defer variant.unref(); 225 | var len: usize = undefined; 226 | const string = variant.getString(&len); 227 | try std.testing.expectEqualStrings("Hello, world!", string[0..len]); 228 | } 229 | 230 | test "newFrom([4]u16)" { 231 | const arr: [4]u16 = .{ 1, 2, 3, 4 }; 232 | const variant = glib.ext.Variant.newFrom(arr); 233 | defer variant.unref(); 234 | try std.testing.expectEqual(arr.len, variant.nChildren()); 235 | for (arr, 0..) |item, i| { 236 | const child = variant.getChildValue(i); 237 | defer child.unref(); 238 | try std.testing.expectEqual(item, child.getUint16()); 239 | } 240 | } 241 | 242 | test "newFrom([]const u32)" { 243 | const slice: []const u32 = &.{ 1, 2, 3, 4, 5 }; 244 | const variant = glib.ext.Variant.newFrom(slice); 245 | defer variant.unref(); 246 | try std.testing.expectEqual(slice.len, variant.nChildren()); 247 | for (slice, 0..) |item, i| { 248 | const child = variant.getChildValue(i); 249 | defer child.unref(); 250 | try std.testing.expectEqual(item, child.getUint32()); 251 | } 252 | } 253 | 254 | test "newFrom(?i16)" { 255 | { 256 | const opt: ?i16 = -12345; 257 | const variant = glib.ext.Variant.newFrom(opt); 258 | defer variant.unref(); 259 | const maybe_child = variant.getMaybe(); 260 | defer if (maybe_child) |child| child.unref(); 261 | try std.testing.expect(maybe_child != null); 262 | try std.testing.expectEqual(-12345, maybe_child.?.getInt16()); 263 | } 264 | 265 | { 266 | const opt: ?i16 = null; 267 | const variant = glib.ext.Variant.newFrom(opt); 268 | defer variant.unref(); 269 | try std.testing.expectEqual(null, variant.getMaybe()); 270 | } 271 | } 272 | 273 | test "newFrom(struct{u32, u64, i64})" { 274 | const tuple: struct { u32, u64, i64 } = .{ 1, 2, -3 }; 275 | const variant = glib.ext.Variant.newFrom(tuple); 276 | defer variant.unref(); 277 | try std.testing.expectEqual(tuple.len, variant.nChildren()); 278 | { 279 | const child = variant.getChildValue(0); 280 | defer child.unref(); 281 | try std.testing.expectEqual(tuple[0], child.getUint32()); 282 | } 283 | { 284 | const child = variant.getChildValue(1); 285 | defer child.unref(); 286 | try std.testing.expectEqual(tuple[1], child.getUint64()); 287 | } 288 | { 289 | const child = variant.getChildValue(2); 290 | defer child.unref(); 291 | try std.testing.expectEqual(tuple[2], child.getInt64()); 292 | } 293 | } 294 | 295 | fn testVariantNewFrom( 296 | comptime T: type, 297 | data: T, 298 | getter: fn (*glib.Variant) callconv(.c) T, 299 | ) !void { 300 | const variant = glib.ext.Variant.newFrom(data); 301 | defer variant.unref(); 302 | try std.testing.expectEqual(data, getter(variant)); 303 | } 304 | }; 305 | 306 | pub const VariantType = struct { 307 | /// Returns a new variant type corresponding to the given type. 308 | pub fn newFor(comptime T: type) *glib.VariantType { 309 | return glib.VariantType.new(stringFor(T)); 310 | } 311 | 312 | /// Returns the variant type string corresponding to the given type. 313 | pub fn stringFor(comptime T: type) [:0]const u8 { 314 | const type_info = @typeInfo(T); 315 | if (T == bool) { 316 | return "b"; 317 | } else if (T == u8) { 318 | return "y"; 319 | } else if (T == i16) { 320 | return "n"; 321 | } else if (T == u16) { 322 | return "q"; 323 | } else if (T == i32) { 324 | return "i"; 325 | } else if (T == u32) { 326 | return "u"; 327 | } else if (T == i64) { 328 | return "x"; 329 | } else if (T == u64) { 330 | return "t"; 331 | } else if (T == f64) { 332 | return "d"; 333 | } else if (comptime isCString(T)) { 334 | return "s"; 335 | } else if (T == *glib.Variant) { 336 | return "v"; 337 | } else if (type_info == .array) { 338 | return "a" ++ stringFor(type_info.array.child); 339 | } else if (type_info == .pointer and type_info.pointer.size == .slice) { 340 | return "a" ++ stringFor(type_info.pointer.child); 341 | } else if (type_info == .optional) { 342 | return "m" ++ stringFor(type_info.optional.child); 343 | } else if (type_info == .@"struct" and type_info.@"struct".is_tuple) { 344 | comptime var str: [:0]const u8 = "("; 345 | inline for (type_info.@"struct".fields) |field| { 346 | str = str ++ comptime stringFor(field.type); 347 | } 348 | return str ++ ")"; 349 | } else { 350 | @compileError("cannot determine variant type for " ++ @typeName(T)); 351 | } 352 | } 353 | }; 354 | 355 | fn isCString(comptime T: type) bool { 356 | return switch (@typeInfo(T)) { 357 | .pointer => |info| switch (info.size) { 358 | .one => switch (@typeInfo(info.child)) { 359 | .array => |child| child.child == u8 and std.meta.sentinel(info.child) == 0, 360 | else => false, 361 | }, 362 | .many, .slice => info.child == u8 and std.meta.sentinel(T) == 0, 363 | else => false, 364 | }, 365 | else => false, 366 | }; 367 | } 368 | -------------------------------------------------------------------------------- /extensions/gtk4.zig: -------------------------------------------------------------------------------- 1 | const glib = @import("glib2"); 2 | const gobject = @import("gobject2"); 3 | const gtk = @import("gtk4"); 4 | const std = @import("std"); 5 | 6 | pub const BindTemplateChildOptions = struct { 7 | field: ?[]const u8 = null, 8 | internal: bool = false, 9 | }; 10 | 11 | /// Implementation helpers not meant to be used outside implementations of 12 | /// new classes. 13 | pub const impl_helpers = struct { 14 | /// Binds a field of the instance struct to an object declared in the 15 | /// class template. 16 | /// 17 | /// The name of the field in the instance struct defaults to the name of 18 | /// the template object, but can be set explicitly via the `field` option 19 | /// if it differs. 20 | pub fn bindTemplateChild( 21 | class: anytype, 22 | comptime name: [:0]const u8, 23 | comptime options: gtk.ext.BindTemplateChildOptions, 24 | ) void { 25 | const field = options.field orelse name; 26 | const Instance = @typeInfo(@TypeOf(class)).pointer.child.Instance; 27 | ensureWidgetType(Instance, field); 28 | gtk.Widget.Class.bindTemplateChildFull( 29 | gobject.ext.as(gtk.Widget.Class, class), 30 | name, 31 | @intFromBool(options.internal), 32 | @offsetOf(Instance, field), 33 | ); 34 | } 35 | 36 | test bindTemplateChild { 37 | const MyWidget = extern struct { 38 | parent_instance: Parent, 39 | label: *gtk.Label, 40 | 41 | const template = 42 | \\ 43 | \\ 44 | \\ 51 | \\ 52 | \\ 53 | ; 54 | 55 | pub const Parent = gtk.Widget; 56 | const Self = @This(); 57 | 58 | pub const getGObjectType = gobject.ext.defineClass(Self, .{ 59 | .name = "BindTemplateChildTest_MyWidget", 60 | .instanceInit = &init, 61 | .classInit = &Class.init, 62 | .parent_class = &Class.parent, 63 | }); 64 | 65 | pub fn as(widget: *Self, comptime T: type) *T { 66 | return gobject.ext.as(T, widget); 67 | } 68 | 69 | pub fn new() *Self { 70 | return gobject.ext.newInstance(Self, .{}); 71 | } 72 | 73 | pub fn unref(widget: *Self) void { 74 | gobject.Object.unref(widget.as(gobject.Object)); 75 | } 76 | 77 | fn init(widget: *Self, _: *Class) callconv(.c) void { 78 | gtk.Widget.initTemplate(widget.as(gtk.Widget)); 79 | gtk.Widget.setLayoutManager(widget.as(gtk.Widget), gtk.BinLayout.new().as(gtk.LayoutManager)); 80 | } 81 | 82 | fn dispose(widget: *Self) callconv(.c) void { 83 | gtk.Widget.disposeTemplate(widget.as(gtk.Widget), getGObjectType()); 84 | gobject.Object.virtual_methods.dispose.call(Class.parent, widget.as(Parent)); 85 | } 86 | 87 | pub const Class = extern struct { 88 | parent_class: Parent.Class, 89 | 90 | var parent: *Parent.Class = undefined; 91 | 92 | pub const Instance = Self; 93 | 94 | pub fn as(class: *Class, comptime T: type) *T { 95 | return gobject.ext.as(T, class); 96 | } 97 | 98 | fn init(class: *Class) callconv(.c) void { 99 | gobject.Object.virtual_methods.dispose.implement(class, &dispose); 100 | gtk.ext.WidgetClass.setTemplateFromSlice(class.as(gtk.Widget.Class), template); 101 | class.bindTemplateChild("label", .{}); 102 | } 103 | 104 | fn bindTemplateChild(class: *Class, comptime name: [:0]const u8, comptime options: gtk.ext.BindTemplateChildOptions) void { 105 | gtk.ext.impl_helpers.bindTemplateChild(class, name, options); 106 | } 107 | }; 108 | }; 109 | 110 | gtk.init(); 111 | const widget = MyWidget.new(); 112 | _ = gobject.Object.refSink(widget.as(gobject.Object)); 113 | defer widget.unref(); 114 | try std.testing.expectEqualStrings("Hello, world!", std.mem.span(widget.label.getLabel())); 115 | } 116 | 117 | /// Binds a template child to a private field. 118 | /// 119 | /// ```zig 120 | /// fn bindTemplateChildPrivate(class: *Class, comptime name: [:0]const u8, comptime options: gtk.BindTemplateChildOptions) void { 121 | /// gtk.ext.impl_helpers.bindTemplateChildPrivate(class, name, Private, Private.offset, options); 122 | /// } 123 | /// ``` 124 | pub fn bindTemplateChildPrivate( 125 | class: anytype, 126 | comptime name: [:0]const u8, 127 | comptime Private: type, 128 | private_offset: c_int, 129 | comptime options: gtk.ext.BindTemplateChildOptions, 130 | ) void { 131 | const field = options.field orelse name; 132 | ensureWidgetType(Private, field); 133 | gtk.Widget.Class.bindTemplateChildFull( 134 | gobject.ext.as(gtk.Widget.Class, class), 135 | name, 136 | @intFromBool(options.internal), 137 | private_offset + @offsetOf(Private, field), 138 | ); 139 | } 140 | 141 | test bindTemplateChildPrivate { 142 | const MyWidget = extern struct { 143 | parent_instance: Parent, 144 | 145 | const template = 146 | \\ 147 | \\ 148 | \\ 155 | \\ 156 | \\ 157 | ; 158 | 159 | pub const Parent = gtk.Widget; 160 | const Self = @This(); 161 | 162 | const Private = struct { 163 | label: *gtk.Label, 164 | 165 | var offset: c_int = 0; 166 | }; 167 | 168 | pub const getGObjectType = gobject.ext.defineClass(Self, .{ 169 | .name = "BindTemplateChildPrivateTest_MyWidget", 170 | .instanceInit = &init, 171 | .classInit = &Class.init, 172 | .parent_class = &Class.parent, 173 | .private = .{ .Type = Private, .offset = &Private.offset }, 174 | }); 175 | 176 | pub fn as(widget: *Self, comptime T: type) *T { 177 | return gobject.ext.as(T, widget); 178 | } 179 | 180 | pub fn new() *Self { 181 | return gobject.ext.newInstance(Self, .{}); 182 | } 183 | 184 | pub fn unref(widget: *Self) void { 185 | gobject.Object.unref(widget.as(gobject.Object)); 186 | } 187 | 188 | pub fn getLabel(widget: *Self) [:0]const u8 { 189 | return std.mem.span(widget.private().label.getLabel()); 190 | } 191 | 192 | fn init(widget: *Self, _: *Class) callconv(.c) void { 193 | gtk.Widget.initTemplate(widget.as(gtk.Widget)); 194 | gtk.Widget.setLayoutManager(widget.as(gtk.Widget), gtk.BinLayout.new().as(gtk.LayoutManager)); 195 | } 196 | 197 | fn dispose(widget: *Self) callconv(.c) void { 198 | gtk.Widget.disposeTemplate(widget.as(gtk.Widget), getGObjectType()); 199 | gobject.Object.virtual_methods.dispose.call(Class.parent, widget.as(Parent)); 200 | } 201 | 202 | fn private(widget: *Self) *Private { 203 | return gobject.ext.impl_helpers.getPrivate(widget, Private, Private.offset); 204 | } 205 | 206 | pub const Class = extern struct { 207 | parent_class: Parent.Class, 208 | 209 | var parent: *Parent.Class = undefined; 210 | 211 | pub const Instance = Self; 212 | 213 | pub fn as(class: *Class, comptime T: type) *T { 214 | return gobject.ext.as(T, class); 215 | } 216 | 217 | fn init(class: *Class) callconv(.c) void { 218 | gobject.Object.virtual_methods.dispose.implement(class, &dispose); 219 | gtk.ext.WidgetClass.setTemplateFromSlice(class.as(gtk.Widget.Class), template); 220 | class.bindTemplateChildPrivate("label", .{}); 221 | } 222 | 223 | fn bindTemplateChildPrivate(class: *Class, comptime name: [:0]const u8, comptime options: gtk.ext.BindTemplateChildOptions) void { 224 | gtk.ext.impl_helpers.bindTemplateChildPrivate(class, name, Private, Private.offset, options); 225 | } 226 | }; 227 | }; 228 | 229 | gtk.init(); 230 | const widget = MyWidget.new(); 231 | _ = gobject.Object.refSink(widget.as(gobject.Object)); 232 | defer widget.unref(); 233 | try std.testing.expectEqualStrings("Hello, world!", widget.getLabel()); 234 | } 235 | 236 | fn ensureWidgetType(comptime Container: type, comptime field_name: []const u8) void { 237 | inline for (@typeInfo(Container).@"struct".fields) |field| { 238 | if (comptime std.mem.eql(u8, field.name, field_name)) { 239 | const WidgetType = switch (@typeInfo(field.type)) { 240 | .pointer => |pointer| widget_type: { 241 | if (pointer.size != .one) { 242 | @compileError("bound child type must be a single pointer"); 243 | } 244 | break :widget_type pointer.child; 245 | }, 246 | .optional => |optional| switch (@typeInfo(optional.child)) { 247 | .pointer => |pointer| widget_type: { 248 | if (pointer.size != .one) { 249 | @compileError("bound child type must be a single pointer"); 250 | } 251 | break :widget_type pointer.child; 252 | }, 253 | else => @compileError("unrecognized bound child type"), 254 | }, 255 | else => @compileError("unrecognized bound child type"), 256 | }; 257 | if (!gobject.ext.isAssignableFrom(gtk.Widget, WidgetType)) { 258 | @compileError("bound child must be a widget"); 259 | } 260 | // Ensuring the type is registered avoids the user needing to 261 | // explicitly call ensureType on the widget type as long as it's 262 | // bound as a child in some custom widget class. 263 | gobject.ext.ensureType(WidgetType); 264 | break; 265 | } 266 | } 267 | } 268 | }; 269 | 270 | pub const WidgetClass = struct { 271 | /// Sets the template for a widget from a byte slice. 272 | pub fn setTemplateFromSlice(class: *gtk.WidgetClass, template: [:0]const u8) void { 273 | var bytes = glib.ext.Bytes.newFromSlice(template); 274 | defer bytes.unref(); 275 | class.setTemplate(bytes); 276 | } 277 | }; 278 | -------------------------------------------------------------------------------- /flatpak-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sdk_version=${1:-48} 3 | exec flatpak run --filesystem=home --share=network --share=ipc --socket=fallback-x11 --socket=wayland --device=dri --socket=session-bus org.gnome.Sdk//$sdk_version 4 | -------------------------------------------------------------------------------- /gir-fixes/Adw-1.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | compute 17 | 18 | 19 | -------------------------------------------------------------------------------- /gir-fixes/GLib-2.0.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | gpointer 18 | gpointer 19 | 20 | 21 | 22 | 23 | 24 | 25 | gpointer 26 | gpointer 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 1 42 | 43 | 44 | 45 | 46 | 47 | 48 | 1 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /gir-fixes/GObject-2.0.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | gpointer 18 | gpointer 19 | 20 | 21 | 22 | 24 | 25 | 26 | 1 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /gir-fixes/Gck-1.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | gulong 19 | gulong 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | gpointer 28 | gpointer 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | gpointer 37 | gpointer 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /gir-fixes/Gck-2.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | gulong 19 | gulong 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | gpointer 28 | gpointer 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | gpointer 37 | gpointer 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /gir-fixes/Gee-0.8.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | Gee.Future.MapFunc 33 | GeeFutureMapFunc 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Gee.Future.LightMapFunc 42 | GeeFutureLightMapFunc 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Gee.Future.FlatMapFunc 51 | GeeFutureFlatMapFunc 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Gee.Future.ZipFunc 60 | GeeFutureZipFunc 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /gir-fixes/Gio-2.0.xslt: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 1 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 1 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /gir-fixes/Gst-1.0.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | gpointer 19 | gpointer 20 | 21 | 22 | 23 | 24 | 25 | 26 | gpointer 27 | gpointer 28 | 29 | 30 | 31 | 32 | 33 | 34 | gpointer 35 | gpointer 36 | 37 | 38 | 39 | 41 | 42 | 43 | gpointer 44 | gpointer 45 | 46 | 47 | 48 | 50 | 51 | 52 | gpointer 53 | gpointer 54 | 55 | 56 | 57 | 58 | 59 | 60 | gpointer 61 | gpointer 62 | 63 | 64 | 65 | 67 | 68 | 69 | gpointer 70 | gpointer 71 | 72 | 73 | 74 | 76 | 77 | 78 | gpointer 79 | gpointer 80 | 81 | 82 | 83 | 84 | 85 | 86 | gpointer 87 | gpointer 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /gir-fixes/GstController-1.0.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | gpointer 18 | gpointer 19 | 20 | 21 | 22 | 23 | 24 | 25 | gpointer 26 | gpointer 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /gir-fixes/GstGLEGL-1.0.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | gpointer 18 | gpointer 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /gir-fixes/GstVideo-1.0.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | gpointer 18 | gpointer 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /gir-fixes/WebKit2-4.1.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | gpointer 19 | gpointer 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | gpointer 28 | gpointer 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /gir-fixes/WebKit2WebExtension-4.1.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | gpointer 19 | gpointer 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /gir-fixes/freetype2-2.0.xslt: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 1 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/build/build_base.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | /// A library accessible through the generated bindings. 4 | /// 5 | /// While the generated bindings are typically used through modules 6 | /// (e.g. `gobject.module("glib-2.0")`), there are cases where it is 7 | /// useful to have additional information about the libraries exposed 8 | /// to the build script. For example, if any files in the root module 9 | /// of the application want to import a library's C headers directly, 10 | /// it will be necessary to link the library directly to the root module 11 | /// using `Library.linkTo` so the include paths will be available. 12 | pub const Library = struct { 13 | /// System libraries to be linked using pkg-config. 14 | system_libraries: []const []const u8, 15 | 16 | /// Links `lib` to `module`. 17 | pub fn linkTo(lib: Library, module: *std.Build.Module) void { 18 | module.link_libc = true; 19 | for (lib.system_libraries) |system_lib| { 20 | module.linkSystemLibrary(system_lib, .{ .use_pkg_config = .force }); 21 | } 22 | } 23 | }; 24 | 25 | /// Returns a `std.Build.Module` created by compiling the GResources file at `path`. 26 | /// 27 | /// This requires the `glib-compile-resources` system command to be available. 28 | pub fn addCompileResources( 29 | b: *std.Build, 30 | target: std.Build.ResolvedTarget, 31 | path: std.Build.LazyPath, 32 | ) *std.Build.Module { 33 | const compile_resources, const module = addCompileResourcesInternal(b, target, path); 34 | compile_resources.addArg("--sourcedir"); 35 | compile_resources.addDirectoryArg(path.dirname()); 36 | compile_resources.addArg("--dependency-file"); 37 | _ = compile_resources.addDepFileOutputArg("gresources-deps"); 38 | 39 | return module; 40 | } 41 | 42 | fn addCompileResourcesInternal( 43 | b: *std.Build, 44 | target: std.Build.ResolvedTarget, 45 | path: std.Build.LazyPath, 46 | ) struct { *std.Build.Step.Run, *std.Build.Module } { 47 | const compile_resources = b.addSystemCommand(&.{ "glib-compile-resources", "--generate-source" }); 48 | compile_resources.addArg("--target"); 49 | const gresources_c = compile_resources.addOutputFileArg("gresources.c"); 50 | compile_resources.addFileArg(path); 51 | 52 | const module = b.createModule(.{ .target = target }); 53 | module.addCSourceFile(.{ .file = gresources_c }); 54 | @This().libraries.gio2.linkTo(module); 55 | return .{ compile_resources, module }; 56 | } 57 | 58 | /// Returns a builder for a compiled GResource bundle. 59 | /// 60 | /// Calling `CompileResources.build` on the returned builder requires the 61 | /// `glib-compile-resources` system command to be installed. 62 | pub fn buildCompileResources(gobject_dependency: *std.Build.Dependency) CompileResources { 63 | return .{ .b = gobject_dependency.builder }; 64 | } 65 | 66 | /// A builder for a compiled GResource bundle. 67 | pub const CompileResources = struct { 68 | b: *std.Build, 69 | groups: std.ArrayListUnmanaged(*Group) = .{}, 70 | 71 | var build_gresources_xml_exe: ?*std.Build.Step.Compile = null; 72 | 73 | /// Builds the GResource bundle as a module. The module must be imported 74 | /// into the compilation for the resources to be loaded. 75 | pub fn build(cr: CompileResources, target: std.Build.ResolvedTarget) *std.Build.Module { 76 | const run = cr.b.addRunArtifact(build_gresources_xml_exe orelse exe: { 77 | const exe = cr.b.addExecutable(.{ 78 | .name = "build-gresources-xml", 79 | .root_source_file = cr.b.path("build/build_gresources_xml.zig"), 80 | .target = cr.b.graph.host, 81 | .optimize = .Debug, 82 | }); 83 | build_gresources_xml_exe = exe; 84 | break :exe exe; 85 | }); 86 | 87 | for (cr.groups.items) |group| { 88 | run.addArg(cr.b.fmt("--prefix={s}", .{group.prefix})); 89 | for (group.files.items) |file| { 90 | run.addArg(cr.b.fmt("--alias={s}", .{file.name})); 91 | if (file.options.compressed) { 92 | run.addArg("--compressed"); 93 | } 94 | for (file.options.preprocess) |preprocessor| { 95 | run.addArg(cr.b.fmt("--preprocess={s}", .{preprocessor.name()})); 96 | } 97 | run.addPrefixedFileArg("--path=", file.path); 98 | } 99 | } 100 | const xml = run.addPrefixedOutputFileArg("--output=", "gresources.xml"); 101 | 102 | _, const module = addCompileResourcesInternal(cr.b, target, xml); 103 | return module; 104 | } 105 | 106 | /// Adds a group of resources showing a common prefix. 107 | pub fn addGroup(cr: *CompileResources, prefix: []const u8) *Group { 108 | const group = cr.b.allocator.create(Group) catch @panic("OOM"); 109 | group.* = .{ .owner = cr, .prefix = prefix }; 110 | cr.groups.append(cr.b.allocator, group) catch @panic("OOM"); 111 | return group; 112 | } 113 | 114 | pub const Group = struct { 115 | owner: *CompileResources, 116 | prefix: []const u8, 117 | files: std.ArrayListUnmanaged(File) = .{}, 118 | 119 | /// Adds the file at `path` as a resource named `name` (within the 120 | /// prefix of the containing group). 121 | pub fn addFile(g: *Group, name: []const u8, path: std.Build.LazyPath, options: File.Options) void { 122 | g.files.append(g.owner.b.allocator, .{ 123 | .name = name, 124 | .path = path, 125 | .options = options, 126 | }) catch @panic("OOM"); 127 | } 128 | }; 129 | 130 | pub const File = struct { 131 | name: []const u8, 132 | path: std.Build.LazyPath, 133 | options: Options = .{}, 134 | 135 | pub const Options = struct { 136 | compressed: bool = false, 137 | preprocess: []const Preprocessor = &.{}, 138 | }; 139 | 140 | pub const Preprocessor = union(enum) { 141 | xml_stripblanks, 142 | json_stripblanks, 143 | other: []const u8, 144 | 145 | pub fn name(p: Preprocessor) []const u8 { 146 | return switch (p) { 147 | .xml_stripblanks => "xml-stripblanks", 148 | .json_stripblanks => "json-stripblanks", 149 | .other => |s| s, 150 | }; 151 | } 152 | }; 153 | }; 154 | }; 155 | -------------------------------------------------------------------------------- /src/build/build_gresources_xml.zig: -------------------------------------------------------------------------------- 1 | //! A simple wrapper to build a `gresources.xml` description from command-line 2 | //! arguments. 3 | //! 4 | //! This program is only meant to be invoked internally as part of the 5 | //! zig-gobject build helper logic, hence, having a "user-friendly" interface 6 | //! is not a goal. 7 | 8 | const std = @import("std"); 9 | 10 | pub fn main() !void { 11 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); 12 | defer arena_state.deinit(); 13 | const arena = arena_state.allocator(); 14 | 15 | const args = try std.process.argsAlloc(arena); 16 | 17 | var output_path: ?[]const u8 = null; 18 | var output = std.ArrayList(u8).init(arena); 19 | try output.appendSlice(""); 20 | 21 | var i: usize = 1; 22 | var seen_prefix = false; 23 | var seen_preprocessor = false; 24 | while (i < args.len) : (i += 1) { 25 | const arg = args[i]; 26 | if (std.mem.startsWith(u8, arg, "--prefix=")) { 27 | if (seen_prefix) { 28 | try output.appendSlice(""); 29 | } 30 | try output.writer().print("", .{fmtXml(arg["--prefix=".len..])}); 31 | seen_prefix = true; 32 | } else if (std.mem.startsWith(u8, arg, "--alias=")) { 33 | try output.writer().print("{}", .{fmtXml(arg["--path=".len..])}); 48 | seen_preprocessor = false; 49 | } else if (std.mem.startsWith(u8, arg, "--output=")) { 50 | output_path = arg["--output=".len..]; 51 | } else { 52 | return error.UnrecognizedOption; 53 | } 54 | } 55 | 56 | if (seen_prefix) { 57 | try output.appendSlice(""); 58 | } 59 | try output.appendSlice(""); 60 | 61 | try std.fs.cwd().writeFile(.{ 62 | .sub_path = output_path orelse return error.MissingOutput, 63 | .data = output.items, 64 | }); 65 | } 66 | 67 | fn fmtXml(s: []const u8) std.fmt.Formatter(formatXml) { 68 | return .{ .data = s }; 69 | } 70 | 71 | fn formatXml(s: []const u8, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { 72 | _ = fmt; 73 | _ = options; 74 | 75 | var start: usize = 0; 76 | while (std.mem.indexOfAnyPos(u8, s, start, "&<\"")) |pos| { 77 | try writer.writeAll(s[start..pos]); 78 | try writer.writeAll(switch (s[pos]) { 79 | '&' => "&", 80 | '<' => "<", 81 | '"' => """, 82 | else => unreachable, 83 | }); 84 | start = pos + 1; 85 | } 86 | try writer.writeAll(s[start..]); 87 | } 88 | -------------------------------------------------------------------------------- /src/compat.zig: -------------------------------------------------------------------------------- 1 | //! Compatibility wrappers for the latest tagged release of Zig. 2 | 3 | // This file intentionally left blank. Any future compatibility wrappers needed 4 | // for Zig master changes will be added here. 5 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const gir = @import("gir.zig"); 4 | const translate = @import("translate.zig"); 5 | const log = std.log; 6 | const mem = std.mem; 7 | const Allocator = mem.Allocator; 8 | 9 | const usage = 10 | \\Usage: translate-gir [options] [root...] 11 | \\ 12 | \\Generates bindings for the given root namespaces and their dependencies. 13 | \\ 14 | \\Options: 15 | \\ -h, --help Show this help 16 | \\ --bindings-dir DIR Add a directory to the bindings search path (for manual bindings) 17 | \\ --extensions-dir DIR Add a directory to the extensions search path 18 | \\ --gir-dir DIR Add a directory to the GIR search path 19 | \\ --gir-fixes-dir DIR Add a directory to the GIR fixes search path 20 | \\ --output-dir DIR Set the output directory 21 | \\ --abi-test-output-dir DIR Set the output directory for ABI tests 22 | \\ --dependency-file PATH Generate a dependency file 23 | \\ 24 | ; 25 | 26 | var log_tty_config: std.io.tty.Config = undefined; // Will be initialized immediately in main 27 | 28 | pub const std_options: std.Options = .{ 29 | .log_level = if (builtin.mode == .Debug) log.Level.debug else log.Level.info, 30 | .logFn = logImpl, 31 | }; 32 | 33 | pub fn logImpl( 34 | comptime level: log.Level, 35 | comptime scope: @Type(.enum_literal), 36 | comptime format: []const u8, 37 | args: anytype, 38 | ) void { 39 | const prefix = if (scope == .default) 40 | comptime level.asText() ++ ": " 41 | else 42 | comptime level.asText() ++ "(" ++ @tagName(scope) ++ "): "; 43 | std.debug.lockStdErr(); 44 | defer std.debug.unlockStdErr(); 45 | const stderr = std.io.getStdErr().writer(); 46 | log_tty_config.setColor(stderr, switch (level) { 47 | .err => .bright_red, 48 | .warn => .bright_yellow, 49 | .info => .bright_blue, 50 | .debug => .bright_magenta, 51 | }) catch return; 52 | stderr.writeAll(prefix) catch return; 53 | log_tty_config.setColor(stderr, .reset) catch return; 54 | stderr.print(format ++ "\n", args) catch return; 55 | } 56 | 57 | pub fn main() Allocator.Error!void { 58 | log_tty_config = std.io.tty.detectConfig(std.io.getStdErr()); 59 | 60 | const allocator = std.heap.c_allocator; 61 | 62 | var cli_arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); 63 | defer cli_arena_state.deinit(); 64 | const cli_arena = cli_arena_state.allocator(); 65 | 66 | var gir_dir_paths = std.ArrayList([]u8).init(cli_arena); 67 | var gir_fixes_dir_paths = std.ArrayList([]u8).init(cli_arena); 68 | var bindings_dir_paths = std.ArrayList([]u8).init(cli_arena); 69 | var extensions_dir_paths = std.ArrayList([]u8).init(cli_arena); 70 | var maybe_output_dir_path: ?[]u8 = null; 71 | var maybe_abi_test_output_dir_path: ?[]u8 = null; 72 | var maybe_dependency_file_path: ?[]u8 = null; 73 | var roots = std.ArrayList(gir.Include).init(cli_arena); 74 | 75 | var args: ArgIterator = .{ .args = try std.process.argsWithAllocator(cli_arena) }; 76 | _ = args.next(); 77 | while (args.next()) |arg| { 78 | switch (arg) { 79 | .option => |option| if (option.is('h', "help")) { 80 | std.io.getStdOut().writeAll(usage) catch {}; 81 | std.process.exit(0); 82 | } else if (option.is(null, "bindings-dir")) { 83 | const path = args.optionValue() orelse fatal("expected value for --bindings-dir", .{}); 84 | try bindings_dir_paths.append(try cli_arena.dupe(u8, path)); 85 | } else if (option.is(null, "extensions-dir")) { 86 | const path = args.optionValue() orelse fatal("expected value for --extensions-dir", .{}); 87 | try extensions_dir_paths.append(try cli_arena.dupe(u8, path)); 88 | } else if (option.is(null, "gir-dir")) { 89 | const path = args.optionValue() orelse fatal("expected value for --gir-dir", .{}); 90 | try gir_dir_paths.append(try cli_arena.dupe(u8, path)); 91 | } else if (option.is(null, "gir-fixes-dir")) { 92 | const path = args.optionValue() orelse fatal("expected value for --gir-fixes-dir", .{}); 93 | try gir_fixes_dir_paths.append(try cli_arena.dupe(u8, path)); 94 | } else if (option.is(null, "output-dir")) { 95 | const path = args.optionValue() orelse fatal("expected value for --output-dir", .{}); 96 | maybe_output_dir_path = try cli_arena.dupe(u8, path); 97 | } else if (option.is(null, "abi-test-output-dir")) { 98 | const path = args.optionValue() orelse fatal("expected value for --abi-test-output-dir", .{}); 99 | maybe_abi_test_output_dir_path = try cli_arena.dupe(u8, path); 100 | } else if (option.is(null, "dependency-file")) { 101 | const path = args.optionValue() orelse fatal("expected value for --dependency-file", .{}); 102 | maybe_dependency_file_path = try cli_arena.dupe(u8, path); 103 | } else { 104 | fatal("unrecognized option: {}", .{option}); 105 | }, 106 | .param => |param| { 107 | const sep_pos = mem.indexOfScalar(u8, param, '-') orelse fatal("invalid GIR repository name: {s}", .{param}); 108 | try roots.append(.{ 109 | .name = try cli_arena.dupe(u8, param[0..sep_pos]), 110 | .version = try cli_arena.dupe(u8, param[sep_pos + 1 ..]), 111 | }); 112 | }, 113 | .unexpected_value => |unexpected_value| fatal("unexpected value to --{s}: {s}", .{ 114 | unexpected_value.option, 115 | unexpected_value.value, 116 | }), 117 | } 118 | } 119 | 120 | const output_dir_path = maybe_output_dir_path orelse fatal("no output directory provided", .{}); 121 | if (roots.items.len == 0) fatal("no modules specified to codegen", .{}); 122 | 123 | const repositories = repositories: { 124 | var diag: Diagnostics = .{ .allocator = allocator }; 125 | defer diag.deinit(); 126 | const repositories = try gir.findRepositories( 127 | allocator, 128 | gir_dir_paths.items, 129 | gir_fixes_dir_paths.items, 130 | roots.items, 131 | &diag, 132 | ); 133 | diag.report("failed to find and parse GIR repositories", .{}); 134 | break :repositories repositories; 135 | }; 136 | defer allocator.free(repositories); 137 | defer for (repositories) |*repository| repository.deinit(); 138 | 139 | var deps = Dependencies.init(allocator); 140 | defer deps.deinit(); 141 | 142 | { 143 | var diag: Diagnostics = .{ .allocator = allocator }; 144 | defer diag.deinit(); 145 | try translate.createBuildFiles( 146 | allocator, 147 | repositories, 148 | output_dir_path, 149 | &deps, 150 | &diag, 151 | ); 152 | diag.report("failed to create build file", .{}); 153 | } 154 | 155 | { 156 | const src_output_dir_path = try std.fs.path.join(cli_arena, &.{ output_dir_path, "src" }); 157 | var diag: Diagnostics = .{ .allocator = allocator }; 158 | defer diag.deinit(); 159 | try translate.createBindings( 160 | allocator, 161 | repositories, 162 | bindings_dir_paths.items, 163 | extensions_dir_paths.items, 164 | src_output_dir_path, 165 | &deps, 166 | &diag, 167 | ); 168 | diag.report("failed to translate source files", .{}); 169 | } 170 | 171 | if (maybe_abi_test_output_dir_path) |abi_test_output_dir_path| { 172 | var diag: Diagnostics = .{ .allocator = allocator }; 173 | defer diag.deinit(); 174 | try translate.createAbiTests( 175 | allocator, 176 | repositories, 177 | abi_test_output_dir_path, 178 | &deps, 179 | &diag, 180 | ); 181 | diag.report("failed to create ABI tests", .{}); 182 | } 183 | 184 | if (maybe_dependency_file_path) |dependency_file_path| { 185 | var diag: Diagnostics = .{ .allocator = allocator }; 186 | defer diag.deinit(); 187 | try writeDependencies(dependency_file_path, deps, &diag); 188 | diag.report("failed to create dependency file", .{}); 189 | } 190 | } 191 | 192 | fn fatal(comptime format: []const u8, args: anytype) noreturn { 193 | log.err(format, args); 194 | std.process.exit(1); 195 | } 196 | 197 | fn writeDependencies(path: []const u8, deps: Dependencies, diag: *Diagnostics) !void { 198 | var file = std.fs.cwd().createFile(path, .{}) catch |err| 199 | return diag.add("failed to create dependency file {s}: {}", .{ path, err }); 200 | defer file.close(); 201 | 202 | var buffered_writer = std.io.bufferedWriter(file.writer()); 203 | deps.write(buffered_writer.writer()) catch |err| 204 | return diag.add("failed to write dependency file {s}: {}", .{ path, err }); 205 | buffered_writer.flush() catch |err| 206 | return diag.add("failed to write dependency file {s}: {}", .{ path, err }); 207 | } 208 | 209 | pub const Dependencies = struct { 210 | paths: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged([]u8)) = .{}, 211 | arena: std.heap.ArenaAllocator, 212 | 213 | pub fn init(allocator: Allocator) Dependencies { 214 | return .{ .arena = std.heap.ArenaAllocator.init(allocator) }; 215 | } 216 | 217 | pub fn deinit(deps: *Dependencies) void { 218 | deps.arena.deinit(); 219 | deps.* = undefined; 220 | } 221 | 222 | pub fn add(deps: *Dependencies, target: []const u8, dependencies: []const []const u8) Allocator.Error!void { 223 | const arena = deps.arena.allocator(); 224 | const gop = try deps.paths.getOrPut(arena, target); 225 | if (!gop.found_existing) { 226 | gop.key_ptr.* = try arena.dupe(u8, target); 227 | gop.value_ptr.* = .{}; 228 | } 229 | try gop.value_ptr.ensureUnusedCapacity(arena, dependencies.len); 230 | for (dependencies) |dependency| { 231 | gop.value_ptr.appendAssumeCapacity(try arena.dupe(u8, dependency)); 232 | } 233 | } 234 | 235 | pub fn addRepository(deps: *Dependencies, target: []const u8, repository: gir.Repository) Allocator.Error!void { 236 | try deps.add(target, &.{repository.path}); 237 | if (repository.fix_path) |fix_path| try deps.add(target, &.{fix_path}); 238 | } 239 | 240 | pub fn write(deps: Dependencies, writer: anytype) @TypeOf(writer).Error!void { 241 | for (deps.paths.keys(), deps.paths.values()) |target, prereqs| { 242 | try writer.print("{s}:", .{target}); 243 | for (prereqs.items) |prereq| { 244 | try writer.print(" {s}", .{prereq}); 245 | } 246 | try writer.writeByte('\n'); 247 | } 248 | } 249 | }; 250 | 251 | pub const Diagnostics = struct { 252 | errors: std.ArrayListUnmanaged([]u8) = .{}, 253 | allocator: Allocator, 254 | 255 | pub fn deinit(diag: *Diagnostics) void { 256 | for (diag.errors.items) |err| diag.allocator.free(err); 257 | diag.errors.deinit(diag.allocator); 258 | diag.* = undefined; 259 | } 260 | 261 | pub fn add(diag: *Diagnostics, comptime fmt: []const u8, args: anytype) Allocator.Error!void { 262 | const formatted = try std.fmt.allocPrint(diag.allocator, fmt, args); 263 | errdefer diag.allocator.free(formatted); 264 | try diag.errors.append(diag.allocator, formatted); 265 | } 266 | 267 | /// Report any errors present and fail if any are present. 268 | fn report(diag: Diagnostics, comptime fmt: []const u8, args: anytype) void { 269 | if (diag.errors.items.len > 0) { 270 | for (diag.errors.items) |err| { 271 | log.err("{s}", .{err}); 272 | } 273 | fatal(fmt, args); 274 | } 275 | } 276 | }; 277 | 278 | // Inspired by https://github.com/judofyr/parg 279 | const ArgIterator = struct { 280 | args: std.process.ArgIterator, 281 | state: union(enum) { 282 | normal, 283 | short: []const u8, 284 | long: struct { 285 | option: []const u8, 286 | value: []const u8, 287 | }, 288 | params_only, 289 | } = .normal, 290 | 291 | const Arg = union(enum) { 292 | option: union(enum) { 293 | short: u8, 294 | long: []const u8, 295 | 296 | fn is(option: @This(), short: ?u8, long: ?[]const u8) bool { 297 | return switch (option) { 298 | .short => |c| short == c, 299 | .long => |s| mem.eql(u8, long orelse return false, s), 300 | }; 301 | } 302 | 303 | pub fn format(option: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { 304 | switch (option) { 305 | .short => |c| try writer.print("-{c}", .{c}), 306 | .long => |s| try writer.print("--{s}", .{s}), 307 | } 308 | } 309 | }, 310 | param: []const u8, 311 | unexpected_value: struct { 312 | option: []const u8, 313 | value: []const u8, 314 | }, 315 | }; 316 | 317 | fn deinit(iter: *ArgIterator) void { 318 | iter.args.deinit(); 319 | iter.* = undefined; 320 | } 321 | 322 | fn next(iter: *ArgIterator) ?Arg { 323 | switch (iter.state) { 324 | .normal => { 325 | const arg = iter.args.next() orelse return null; 326 | if (mem.eql(u8, arg, "--")) { 327 | iter.state = .params_only; 328 | return .{ .param = iter.args.next() orelse return null }; 329 | } else if (mem.startsWith(u8, arg, "--")) { 330 | if (mem.indexOfScalar(u8, arg, '=')) |equals_index| { 331 | const option = arg["--".len..equals_index]; 332 | iter.state = .{ .long = .{ 333 | .option = option, 334 | .value = arg[equals_index + 1 ..], 335 | } }; 336 | return .{ .option = .{ .long = option } }; 337 | } else { 338 | return .{ .option = .{ .long = arg["--".len..] } }; 339 | } 340 | } else if (mem.startsWith(u8, arg, "-") and arg.len > 1) { 341 | if (arg.len > 2) { 342 | iter.state = .{ .short = arg["-".len + 1 ..] }; 343 | } 344 | return .{ .option = .{ .short = arg["-".len] } }; 345 | } else { 346 | return .{ .param = arg }; 347 | } 348 | }, 349 | .short => |rest| { 350 | if (rest.len > 1) { 351 | iter.state = .{ .short = rest[1..] }; 352 | } 353 | return .{ .option = .{ .short = rest[0] } }; 354 | }, 355 | .long => |long| return .{ .unexpected_value = .{ 356 | .option = long.option, 357 | .value = long.value, 358 | } }, 359 | .params_only => return .{ .param = iter.args.next() orelse return null }, 360 | } 361 | } 362 | 363 | fn optionValue(iter: *ArgIterator) ?[]const u8 { 364 | switch (iter.state) { 365 | .normal => return iter.args.next(), 366 | .short => |rest| { 367 | iter.state = .normal; 368 | return rest; 369 | }, 370 | .long => |long| { 371 | iter.state = .normal; 372 | return long.value; 373 | }, 374 | .params_only => unreachable, 375 | } 376 | } 377 | }; 378 | 379 | test { 380 | _ = translate; 381 | } 382 | -------------------------------------------------------------------------------- /src/zig_writer.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const compat = @import("compat.zig"); 3 | 4 | pub fn zigWriter(out: anytype) ZigWriter(@TypeOf(out)) { 5 | return .{ .out = out }; 6 | } 7 | 8 | pub fn ZigWriter(comptime Writer: type) type { 9 | return struct { 10 | out: Writer, 11 | 12 | const Self = @This(); 13 | pub const Error = Writer.Error; 14 | 15 | /// Prints Zig code to the output using the provided format string and 16 | /// arguments. 17 | /// 18 | /// Placeholders in the format string look like `$?`, where `?` may be 19 | /// any of the following: 20 | /// 21 | /// - `$`: a literal `$` character 22 | /// - `L`: the literal value of the argument (no escaping) 23 | /// - `S`: a string literal with the argument as its text 24 | /// - `I`: an identifier, escaped using raw identifier syntax if needed 25 | /// 26 | /// The syntax here is inspired by JavaPoet. 27 | /// 28 | /// This is a much simpler implementation than Zig's usual format 29 | /// function and could use better design and error handling if it's ever 30 | /// made into its own project. 31 | pub fn print(w: Self, comptime fmt: []const u8, args: anytype) Error!void { 32 | @setEvalBranchQuota(100_000); 33 | const arg_fields = @typeInfo(@TypeOf(args)).@"struct".fields; 34 | 35 | comptime var current_arg = 0; 36 | comptime var i = 0; 37 | comptime var start = 0; 38 | 39 | inline while (i < fmt.len) : (i += 1) { 40 | if (fmt[i] != '$') { 41 | // Normal literal content 42 | continue; 43 | } 44 | if (i + 1 == fmt.len) { 45 | @compileError("unterminated placeholder"); 46 | } 47 | if (i > start) { 48 | try w.out.writeAll(fmt[start..i]); 49 | } 50 | 51 | start = i + 2; 52 | switch (fmt[i + 1]) { 53 | '$' => { 54 | // Use the second $ as the beginning of literal content 55 | start = i + 1; 56 | }, 57 | 'L' => { 58 | const arg = @field(args, arg_fields[current_arg].name); 59 | if (isString(@TypeOf(arg))) { 60 | for (arg) |char| { 61 | switch (char) { 62 | // Zig is very tab-hostile, so we have to replace tabs with spaces. 63 | // This is most relevant when translating documentation. 64 | '\t' => try w.out.writeAll(" "), 65 | else => try w.out.writeByte(char), 66 | } 67 | } 68 | } else { 69 | try w.out.print("{}", .{arg}); 70 | } 71 | current_arg += 1; 72 | }, 73 | 'S' => { 74 | const arg = @field(args, arg_fields[current_arg].name); 75 | try w.out.print("\"{}\"", .{std.zig.fmtEscapes(arg)}); 76 | current_arg += 1; 77 | }, 78 | 'I' => { 79 | const arg = @field(args, arg_fields[current_arg].name); 80 | // zig.fmtId does not escape primitive type names 81 | if (std.zig.isValidId(arg) and !std.zig.primitives.isPrimitive(arg)) { 82 | try w.out.print("{s}", .{arg}); 83 | } else { 84 | try w.out.print("@\"{}\"", .{std.zig.fmtEscapes(arg)}); 85 | } 86 | current_arg += 1; 87 | }, 88 | else => @compileError("illegal format character: " ++ &[_]u8{fmt[i + 1]}), 89 | } 90 | } 91 | 92 | if (i > start) { 93 | try w.out.writeAll(fmt[start..i]); 94 | } 95 | 96 | if (current_arg != arg_fields.len) { 97 | @compileError("unused arguments remaining"); 98 | } 99 | } 100 | }; 101 | } 102 | 103 | inline fn isString(comptime T: type) bool { 104 | return switch (@typeInfo(T)) { 105 | .pointer => |pointer| if (pointer.size == .slice) 106 | pointer.child == u8 107 | else if (pointer.size == .one) 108 | switch (@typeInfo(pointer.child)) { 109 | .array => |array| array.child == u8, 110 | else => false, 111 | } 112 | else 113 | false, 114 | else => false, 115 | }; 116 | } 117 | 118 | test "print" { 119 | var buf = std.ArrayList(u8).init(std.testing.allocator); 120 | defer buf.deinit(); 121 | var w = zigWriter(buf.writer()); 122 | try w.print("const std = @import($S);\n\n", .{"std"}); 123 | try w.print("pub fn $I() void {\n", .{"main"}); 124 | try w.print("std.debug.print($S, .{$S});\n", .{ "Hello, {}!", "world" }); 125 | try w.print("}\n", .{}); 126 | try std.testing.expectEqualStrings( 127 | \\const std = @import("std"); 128 | \\ 129 | \\pub fn main() void { 130 | \\std.debug.print("Hello, {}!", .{"world"}); 131 | \\} 132 | \\ 133 | , buf.items); 134 | } 135 | -------------------------------------------------------------------------------- /test/build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const gobject_build = @import("gobject"); 3 | 4 | const ModuleOptions = struct { 5 | test_abi: bool = true, 6 | }; 7 | 8 | const module_options = std.StaticStringMap(ModuleOptions).initComptime(.{ 9 | .{ 10 | "AppStreamCompose-1.0", ModuleOptions{ 11 | // TODO: have to define I_KNOW_THE_APPSTREAM_COMPOSE_API_IS_SUBJECT_TO_CHANGE 12 | .test_abi = false, 13 | }, 14 | }, 15 | .{ 16 | "Atspi-2.0", ModuleOptions{ 17 | // TODO: incorrect translation of time_added field in Application 18 | .test_abi = false, 19 | }, 20 | }, 21 | .{ 22 | "cairo-1.0", ModuleOptions{ 23 | // TODO: the ABI tests don't work for manually created bindings 24 | .test_abi = false, 25 | }, 26 | }, 27 | .{ 28 | "Dex-1", ModuleOptions{ 29 | // Header file libdex.h not found 30 | .test_abi = false, 31 | }, 32 | }, 33 | .{ 34 | "Gck-1", ModuleOptions{ 35 | // C includes yield error "This API has not yet reached stability." 36 | .test_abi = false, 37 | }, 38 | }, 39 | .{ 40 | "Gck-2", ModuleOptions{ 41 | // C includes yield error "This API has not yet reached stability." 42 | .test_abi = false, 43 | }, 44 | }, 45 | .{ 46 | "Gcr-3", ModuleOptions{ 47 | // C includes yield error "This API has not yet reached stability." 48 | .test_abi = false, 49 | }, 50 | }, 51 | .{ 52 | "Gcr-4", ModuleOptions{ 53 | // C includes yield error "This API has not yet reached stability." 54 | .test_abi = false, 55 | }, 56 | }, 57 | .{ 58 | "GcrUi-3", ModuleOptions{ 59 | // C includes yield error "This API has not yet reached stability." 60 | .test_abi = false, 61 | }, 62 | }, 63 | .{ 64 | "Gdk-3.0", ModuleOptions{ 65 | // Needs more comprehensive checks to skip indirect bit field references 66 | .test_abi = false, 67 | }, 68 | }, 69 | .{ 70 | "GdkPixbuf-2.0", ModuleOptions{ 71 | // GdkPixbufAnimation and GdkPixbufAnimationIter seemingly are final 72 | // without being marked as such in GIR 73 | .test_abi = false, 74 | }, 75 | }, 76 | .{ 77 | "Gee-0.8", ModuleOptions{ 78 | // Either the GIR or the C header for gee_hazard_pointer_new is 79 | // incorrect, but I don't know which one. There might be more 80 | // examples of such discrepancies. 81 | .test_abi = false, 82 | }, 83 | }, 84 | .{ 85 | "Gio-2.0", ModuleOptions{ 86 | // Something weird going on with GSettingsBackend being translated as opaque 87 | .test_abi = false, 88 | }, 89 | }, 90 | .{ 91 | "GLib-2.0", ModuleOptions{ 92 | // Needs more comprehensive checks to skip indirect bit field references 93 | .test_abi = false, 94 | }, 95 | }, 96 | .{ 97 | "GObject-2.0", ModuleOptions{ 98 | // Needs more comprehensive checks to skip indirect bit field references 99 | .test_abi = false, 100 | }, 101 | }, 102 | .{ 103 | "Graphene-1.0", ModuleOptions{ 104 | // Uses non-portable conditional SIMD types; the GIR won't work unless it's generated on the same target 105 | .test_abi = false, 106 | }, 107 | }, 108 | .{ 109 | "Gsk-4.0", ModuleOptions{ 110 | // Needs more comprehensive checks to skip indirect bit field references 111 | .test_abi = false, 112 | }, 113 | }, 114 | .{ 115 | "Gst-1.0", ModuleOptions{ 116 | // GstMemoryCopyFunction: https://github.com/ziglang/zig/issues/12325 117 | .test_abi = false, 118 | }, 119 | }, 120 | .{ 121 | "GstApp-1.0", ModuleOptions{ 122 | // Needs more comprehensive checks to skip indirect bit field references 123 | .test_abi = false, 124 | }, 125 | }, 126 | .{ 127 | "GstAudio-1.0", ModuleOptions{ 128 | // Needs more comprehensive checks to skip indirect bit field references 129 | .test_abi = false, 130 | }, 131 | }, 132 | .{ 133 | "GstBase-1.0", ModuleOptions{ 134 | // Needs more comprehensive checks to skip indirect bit field references 135 | .test_abi = false, 136 | }, 137 | }, 138 | .{ 139 | "GstCheck-1.0", ModuleOptions{ 140 | // Needs more comprehensive checks to skip indirect bit field references 141 | .test_abi = false, 142 | }, 143 | }, 144 | .{ 145 | "GstGL-1.0", ModuleOptions{ 146 | // Needs more comprehensive checks to skip indirect bit field references 147 | .test_abi = false, 148 | }, 149 | }, 150 | .{ 151 | "GstGLEGL-1.0", ModuleOptions{ 152 | // GstMemoryCopyFunction: https://github.com/ziglang/zig/issues/12325 153 | .test_abi = false, 154 | }, 155 | }, 156 | .{ 157 | "GstInsertBin-1.0", ModuleOptions{ 158 | // Needs more comprehensive checks to skip indirect bit field references 159 | .test_abi = false, 160 | }, 161 | }, 162 | .{ 163 | "GstMse-1.0", ModuleOptions{ 164 | // Needs more comprehensive checks to skip indirect bit field references 165 | .test_abi = false, 166 | }, 167 | }, 168 | .{ 169 | "GstPbutils-1.0", ModuleOptions{ 170 | // Needs more comprehensive checks to skip indirect bit field references 171 | .test_abi = false, 172 | }, 173 | }, 174 | .{ 175 | "GstRtp-1.0", ModuleOptions{ 176 | // Needs more comprehensive checks to skip indirect bit field references 177 | .test_abi = false, 178 | }, 179 | }, 180 | .{ 181 | "GstTag-1.0", ModuleOptions{ 182 | // Needs more comprehensive checks to skip indirect bit field references 183 | .test_abi = false, 184 | }, 185 | }, 186 | .{ 187 | "GstVideo-1.0", ModuleOptions{ 188 | // Needs more comprehensive checks to skip indirect bit field references 189 | .test_abi = false, 190 | }, 191 | }, 192 | .{ 193 | "Gtk-3.0", ModuleOptions{ 194 | // Needs more comprehensive checks to skip indirect bit field references 195 | .test_abi = false, 196 | }, 197 | }, 198 | .{ 199 | "Handy-1", ModuleOptions{ 200 | // Needs more comprehensive checks to skip indirect bit field references 201 | .test_abi = false, 202 | }, 203 | }, 204 | .{ 205 | "Pango-1.0", ModuleOptions{ 206 | // Needs more comprehensive checks to skip indirect bit field references 207 | .test_abi = false, 208 | }, 209 | }, 210 | .{ 211 | "PangoCairo-1.0", ModuleOptions{ 212 | // Needs more comprehensive checks to skip indirect bit field references 213 | .test_abi = false, 214 | }, 215 | }, 216 | .{ 217 | "PangoFc-1.0", ModuleOptions{ 218 | // Needs more comprehensive checks to skip indirect bit field references 219 | .test_abi = false, 220 | }, 221 | }, 222 | .{ 223 | "PangoFT2-1.0", ModuleOptions{ 224 | // Needs more comprehensive checks to skip indirect bit field references 225 | .test_abi = false, 226 | }, 227 | }, 228 | .{ 229 | "PangoOT-1.0", ModuleOptions{ 230 | // Needs more comprehensive checks to skip indirect bit field references 231 | .test_abi = false, 232 | }, 233 | }, 234 | .{ 235 | "WebKit2-4.1", ModuleOptions{ 236 | // Needs more comprehensive checks to skip indirect bit field references 237 | .test_abi = false, 238 | }, 239 | }, 240 | .{ 241 | "WebKit2WebExtension-4.1", ModuleOptions{ 242 | // Needs more comprehensive checks to skip indirect bit field references 243 | .test_abi = false, 244 | }, 245 | }, 246 | }); 247 | 248 | pub fn build(b: *std.Build) void { 249 | const target = b.standardTargetOptions(.{}); 250 | const optimize = b.standardOptimizeOption(.{}); 251 | 252 | const gobject = b.dependency("gobject", .{ 253 | .target = target, 254 | .optimize = optimize, 255 | }); 256 | 257 | const test_step = b.step("test", "Run binding tests"); 258 | 259 | const GirProfile = enum { gnome47, gnome48 }; 260 | const gir_profile = b.option(GirProfile, "gir-profile", "Predefined GIR profile for tests"); 261 | const test_modules: []const []const u8 = b.option([]const []const u8, "modules", "Modules to test") orelse if (gir_profile) |profile| switch (profile) { 262 | .gnome47 => &.{ 263 | "Adw-1", 264 | "AppStream-1.0", 265 | "AppStreamCompose-1.0", 266 | "Atk-1.0", 267 | "Atspi-2.0", 268 | "cairo-1.0", 269 | "CudaGst-1.0", 270 | "DBus-1.0", 271 | "Dex-1", 272 | "fontconfig-2.0", 273 | "freetype2-2.0", 274 | "GCab-1.0", 275 | "Gck-1", 276 | "Gck-2", 277 | "Gcr-3", 278 | "Gcr-4", 279 | "GcrUi-3", 280 | "GDesktopEnums-3.0", 281 | "Gdk-3.0", 282 | "Gdk-4.0", 283 | "GdkPixbuf-2.0", 284 | "GdkPixdata-2.0", 285 | "GdkWayland-4.0", 286 | "GdkX11-3.0", 287 | "GdkX11-4.0", 288 | "Gee-0.8", 289 | "Geoclue-2.0", 290 | "Gio-2.0", 291 | "GioUnix-2.0", 292 | "GIRepository-2.0", 293 | "GIRepository-3.0", 294 | "GL-1.0", 295 | "GLib-2.0", 296 | "GLibUnix-2.0", 297 | "GModule-2.0", 298 | "GObject-2.0", 299 | "Graphene-1.0", 300 | "Gsk-4.0", 301 | "Gst-1.0", 302 | "GstAllocators-1.0", 303 | "GstAnalytics-1.0", 304 | "GstApp-1.0", 305 | "GstAudio-1.0", 306 | "GstBadAudio-1.0", 307 | "GstBase-1.0", 308 | "GstCheck-1.0", 309 | "GstController-1.0", 310 | "GstCuda-1.0", 311 | // "GstDxva-1.0", // Not usable on Linux 312 | "GstGL-1.0", 313 | "GstGLEGL-1.0", 314 | "GstGLWayland-1.0", 315 | "GstGLX11-1.0", 316 | "GstInsertBin-1.0", 317 | "GstMpegts-1.0", 318 | "GstMse-1.0", 319 | "GstNet-1.0", 320 | "GstPbutils-1.0", 321 | "GstPlay-1.0", 322 | "GstPlayer-1.0", 323 | "GstRtp-1.0", 324 | "GstRtsp-1.0", 325 | "GstSdp-1.0", 326 | "GstTag-1.0", 327 | "GstTranscoder-1.0", 328 | "GstVa-1.0", 329 | "GstVideo-1.0", 330 | // "GstVulkan-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 331 | // "GstVulkanWayland-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 332 | // "GstVulkanXCB-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 333 | "GstWebRTC-1.0", 334 | "Gtk-3.0", 335 | "Gtk-4.0", 336 | "GtkSource-5", 337 | "GUdev-1.0", 338 | "Handy-1", 339 | "HarfBuzz-0.0", 340 | "IBus-1.0", 341 | "JavaScriptCore-4.1", 342 | "JavaScriptCore-6.0", 343 | "Json-1.0", 344 | "Libproxy-1.0", 345 | "libxml2-2.0", 346 | "Manette-0.2", 347 | "Nice-0.1", 348 | "Notify-0.7", 349 | "Pango-1.0", 350 | "PangoCairo-1.0", 351 | "PangoFc-1.0", 352 | "PangoFT2-1.0", 353 | "PangoOT-1.0", 354 | "Polkit-1.0", 355 | "Rsvg-2.0", 356 | "Secret-1", 357 | "Soup-3.0", 358 | "Tracker-3.0", 359 | "Tsparql-3.0", 360 | // "Vulkan-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 361 | "WebKit2-4.1", 362 | "WebKit2WebExtension-4.1", 363 | "WebKit-6.0", 364 | "WebKitWebProcessExtension-6.0", 365 | "win32-1.0", 366 | "xfixes-4.0", 367 | "xft-2.0", 368 | "xlib-2.0", 369 | "Xmlb-2.0", 370 | "xrandr-1.3", 371 | }, 372 | .gnome48 => &.{ 373 | "Adw-1", 374 | "AppStream-1.0", 375 | "AppStreamCompose-1.0", 376 | "Atk-1.0", 377 | "Atspi-2.0", 378 | "cairo-1.0", 379 | "CudaGst-1.0", 380 | "DBus-1.0", 381 | "Dex-1", 382 | "fontconfig-2.0", 383 | "freetype2-2.0", 384 | "GCab-1.0", 385 | "Gck-1", 386 | "Gck-2", 387 | "Gcr-3", 388 | "Gcr-4", 389 | "GcrUi-3", 390 | "GDesktopEnums-3.0", 391 | "Gdk-3.0", 392 | "Gdk-4.0", 393 | "GdkPixbuf-2.0", 394 | "GdkPixdata-2.0", 395 | "GdkWayland-4.0", 396 | "GdkX11-3.0", 397 | "GdkX11-4.0", 398 | "Gee-0.8", 399 | "Geoclue-2.0", 400 | "Gio-2.0", 401 | "GioUnix-2.0", 402 | "GIRepository-2.0", 403 | "GIRepository-3.0", 404 | "GL-1.0", 405 | "GLib-2.0", 406 | "GLibUnix-2.0", 407 | "GModule-2.0", 408 | "GObject-2.0", 409 | "Graphene-1.0", 410 | "Gsk-4.0", 411 | "Gst-1.0", 412 | "GstAllocators-1.0", 413 | "GstAnalytics-1.0", 414 | "GstApp-1.0", 415 | "GstAudio-1.0", 416 | "GstBadAudio-1.0", 417 | "GstBase-1.0", 418 | "GstCheck-1.0", 419 | "GstController-1.0", 420 | "GstCuda-1.0", 421 | // "GstDxva-1.0", // Not usable on Linux 422 | "GstGL-1.0", 423 | "GstGLEGL-1.0", 424 | "GstGLWayland-1.0", 425 | "GstGLX11-1.0", 426 | "GstInsertBin-1.0", 427 | "GstMpegts-1.0", 428 | "GstMse-1.0", 429 | "GstNet-1.0", 430 | "GstPbutils-1.0", 431 | "GstPlay-1.0", 432 | "GstPlayer-1.0", 433 | "GstRtp-1.0", 434 | "GstRtsp-1.0", 435 | "GstSdp-1.0", 436 | "GstTag-1.0", 437 | "GstTranscoder-1.0", 438 | "GstVa-1.0", 439 | "GstVideo-1.0", 440 | // "GstVulkan-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 441 | // "GstVulkanWayland-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 442 | // "GstVulkanXCB-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 443 | "GstWebRTC-1.0", 444 | "Gtk-3.0", 445 | "Gtk-4.0", 446 | "GtkSource-5", 447 | "GUdev-1.0", 448 | "Handy-1", 449 | "HarfBuzz-0.0", 450 | "IBus-1.0", 451 | "JavaScriptCore-4.1", 452 | "JavaScriptCore-6.0", 453 | "Json-1.0", 454 | "Libproxy-1.0", 455 | "libxml2-2.0", 456 | "Manette-0.2", 457 | "Nice-0.1", 458 | "Notify-0.7", 459 | "Pango-1.0", 460 | "PangoCairo-1.0", 461 | "PangoFc-1.0", 462 | "PangoFT2-1.0", 463 | "PangoOT-1.0", 464 | "Polkit-1.0", 465 | "Rsvg-2.0", 466 | "Secret-1", 467 | "Soup-3.0", 468 | "Tracker-3.0", 469 | "Tsparql-3.0", 470 | // "Vulkan-1.0", // https://github.com/ianprime0509/zig-gobject/issues/89 471 | "WebKit2-4.1", 472 | "WebKit2WebExtension-4.1", 473 | "WebKit-6.0", 474 | "WebKitWebProcessExtension-6.0", 475 | "win32-1.0", 476 | "xfixes-4.0", 477 | "xft-2.0", 478 | "xlib-2.0", 479 | "Xmlb-2.0", 480 | "xrandr-1.3", 481 | }, 482 | } else &.{}; 483 | 484 | for (test_modules) |test_module| { 485 | const module_name_end = std.mem.indexOfScalar(u8, test_module, '-') orelse @panic("Invalid module name"); 486 | const module_name = test_module[0..module_name_end]; 487 | const module_major_version_end = std.mem.indexOfScalarPos(u8, test_module, module_name_end, '.') orelse test_module.len; 488 | const module_major_version = test_module[module_name_end + 1 .. module_major_version_end]; 489 | const module = b.fmt("{s}{s}", .{ module_name, module_major_version }); 490 | _ = std.ascii.lowerString(module, module); 491 | 492 | const options: ModuleOptions = module_options.get(test_module) orelse .{}; 493 | 494 | if (options.test_abi) { 495 | const abi_tests = b.addTest(.{ 496 | .root_source_file = b.path(b.pathJoin(&.{ "abi", b.fmt("{s}.abi.zig", .{module}) })), 497 | .target = target, 498 | .optimize = optimize, 499 | }); 500 | abi_tests.root_module.addImport(module, gobject.module(module)); 501 | inline for (comptime std.meta.declarations(gobject_build.libraries)) |lib_decl| { 502 | if (std.mem.eql(u8, lib_decl.name, module)) { 503 | @field(gobject_build.libraries, lib_decl.name).linkTo(abi_tests.root_module); 504 | } 505 | } 506 | test_step.dependOn(&b.addRunArtifact(abi_tests).step); 507 | } 508 | } 509 | } 510 | -------------------------------------------------------------------------------- /test/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .gobject_test, 3 | .version = "0.0.0", 4 | .fingerprint = 0x9f2b01bb96fa122, // Changing this has security and trust implications. 5 | .minimum_zig_version = "0.14.0", 6 | .paths = .{""}, 7 | .dependencies = .{ 8 | .gobject = .{ 9 | .path = "../zig-out/bindings", 10 | }, 11 | }, 12 | } 13 | --------------------------------------------------------------------------------