├── .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 | \\
54 | \\ Example Application
55 | \\ 600
56 | \\ 400
57 | \\
58 | \\
79 | \\
80 | \\
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 | \\
45 | \\
46 | \\
47 | \\ Hello, world!
48 | \\
49 | \\
50 | \\
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 | \\
149 | \\
150 | \\
151 | \\ Hello, world!
152 | \\
153 | \\
154 | \\
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 |
--------------------------------------------------------------------------------