├── .gitignore ├── src ├── glue.zig ├── test.zig ├── gintro.zig └── lib.zig ├── gyro.zzz ├── .vscode └── launch.json ├── README.md ├── gen.py └── tozigfn.py /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache/ 2 | zig-out 3 | -------------------------------------------------------------------------------- /src/glue.zig: -------------------------------------------------------------------------------- 1 | const C = @cImport(@cInclude("glib.h")); 2 | -------------------------------------------------------------------------------- /gyro.zzz: -------------------------------------------------------------------------------- 1 | pkgs: 2 | gintro: 3 | version: 0.1.0 4 | license: MIT 5 | description: Bindings to Gobject Introspection to generate other bindings. 6 | source_url: "https://github.com/diegovsky/gintro" 7 | root: src/main 8 | files: 9 | build.zig 10 | src/*.zig 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug", 11 | "program": "${workspaceFolder}/zig-out/bin/gintro", 12 | "args": [], 13 | "cwd": "${workspaceFolder}" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gintro ⚡ 2 | Gobject Introspection bindings to Zig. This enables you to generate bindings to mapped libraries of GLib to Zig or other languages if you want. Examples include Gtk, Glib, GIO and much more! 3 | 4 | # Build 5 | Using the latest zig compiler just run `zig build` at the project root. For now, there isn't a good way to use build dependencies on with zig's build system, so use the example from `main.zig` to do cool stuff. 6 | 7 | # Code 8 | There are a *LOT* of issues with my code because I'm new to the language & there are some underdocumented parts which are hard (for me at least) to understand, which brings me to 9 | 10 | # Contributing 11 | If you notice any problems in the library, spot some bug or just feel something could be changed, do open an issue or pull request! They are always welcome and I will be happy to review them :) 12 | -------------------------------------------------------------------------------- /src/test.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const gintro = @import("lib.zig"); 3 | const expect = std.testing.expect; 4 | 5 | const GI = struct { 6 | repo: gintro.Repository, 7 | gi: gintro.Namespace, 8 | 9 | fn getAnyOf(gi: *@This(), comptime T: type) ?T { 10 | var iterator = gi.repo.getInfoIterator(&gi.gi); 11 | while (iterator.next()) |info| { 12 | if (info.tryCast(T)) |val| { 13 | return val; 14 | } 15 | info.unref(); 16 | } 17 | return null; 18 | } 19 | }; 20 | 21 | fn getRepo() GI { 22 | var repo = gintro.Repository.default(); 23 | var gi = gintro.Namespace{ .name = "GIRepository", .version = "2.0" }; 24 | _ = repo.require(&gi, .LoadLazy) catch unreachable; 25 | return .{ .repo = repo, .gi = gi }; 26 | } 27 | 28 | test "BaseInfo Casting" { 29 | var gi = getRepo(); 30 | const si = gi.getAnyOf(gintro.StructInfo) orelse unreachable; 31 | const bi = si.tryCast(gintro.BaseInfo) orelse unreachable; 32 | try expect(bi.tryCast(gintro.StructInfo) != null); 33 | try expect(bi.tryCast(gintro.FunctionInfo) == null); 34 | } 35 | 36 | test "Function Casting" { 37 | var gi = getRepo(); 38 | const si = gi.getAnyOf(gintro.FunctionInfo) orelse unreachable; 39 | const bi = si.tryCast(gintro.CallableInfo) orelse unreachable; 40 | try expect(bi.tryCast(gintro.BaseInfo) != null); 41 | } 42 | -------------------------------------------------------------------------------- /gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Stuff used to generate some structures that fall out of the scope of gintro. 3 | import sys 4 | 5 | # yes I copy pasted from the docs. sue me. 6 | typeinfo = """ 7 | GI_INFO_TYPE_INVALID 8 | GI_INFO_TYPE_FUNCTION 9 | GI_INFO_TYPE_CALLBACK 10 | GI_INFO_TYPE_STRUCT 11 | GI_INFO_TYPE_BOXED 12 | GI_INFO_TYPE_ENUM 13 | GI_INFO_TYPE_FLAGS 14 | GI_INFO_TYPE_OBJECT 15 | GI_INFO_TYPE_INTERFACE 16 | GI_INFO_TYPE_CONSTANT 17 | GI_INFO_TYPE_UNION 18 | GI_INFO_TYPE_VALUE 19 | GI_INFO_TYPE_SIGNAL 20 | GI_INFO_TYPE_VFUNC 21 | GI_INFO_TYPE_PROPERTY 22 | GI_INFO_TYPE_FIELD 23 | GI_INFO_TYPE_ARG 24 | GI_INFO_TYPE_TYPE 25 | GI_INFO_TYPE_UNRESOLVED 26 | """ 27 | 28 | typetag = """ 29 | GI_TYPE_TAG_VOID 30 | GI_TYPE_TAG_BOOLEAN 31 | GI_TYPE_TAG_INT8 32 | GI_TYPE_TAG_UINT8 33 | GI_TYPE_TAG_INT16 34 | GI_TYPE_TAG_UINT16 35 | GI_TYPE_TAG_INT32 36 | GI_TYPE_TAG_UINT32 37 | GI_TYPE_TAG_INT64 38 | GI_TYPE_TAG_UINT64 39 | GI_TYPE_TAG_FLOAT 40 | GI_TYPE_TAG_DOUBLE 41 | GI_TYPE_TAG_GTYPE 42 | GI_TYPE_TAG_UTF8 43 | GI_TYPE_TAG_FILENAME 44 | GI_TYPE_TAG_ARRAY 45 | GI_TYPE_TAG_INTERFACE 46 | GI_TYPE_TAG_GLIST 47 | GI_TYPE_TAG_GSLIST 48 | GI_TYPE_TAG_GHASH 49 | GI_TYPE_TAG_ERROR 50 | GI_TYPE_TAG_UNICHAR 51 | """ 52 | 53 | 54 | def die(text=""): 55 | sys.stderr.write(text+"\n") 56 | sys.exit(-1) 57 | 58 | 59 | def generateEnum(name: str, prefix: str, variantstr: str) -> list: 60 | variants = [x for x in variantstr.splitlines() if len(x) != 0] 61 | out = [] 62 | indent = 4 63 | out.append("const {} = enum(c_int) {{\n".format(name)) 64 | for var in variants: 65 | if not var.startswith(prefix): 66 | die("Variant '{}' of '{}' doesn't start with '{}'".format(var, name, prefix)) 67 | 68 | # Yes, this could be a one-liner but I like readability. 69 | variant = var[len(prefix):] 70 | variant = variant.split("_") 71 | variant = map(str.capitalize, variant) 72 | variant = ''.join(variant) 73 | out.append(' '*indent + "{} = C.{},\n".format(variant, var)) 74 | 75 | out.append("};\n") 76 | return out 77 | 78 | 79 | if __name__ == '__main__': 80 | # sys.stdout.writelines(generateEnum("InfoType", "GI_INFO_TYPE", typeinfo)) 81 | sys.stdout.writelines(generateEnum("TypeTag", "GI_TYPE_TAG", typetag)) 82 | -------------------------------------------------------------------------------- /tozigfn.py: -------------------------------------------------------------------------------- 1 | from subprocess import check_output 2 | import sys 3 | from pycparser import c_ast 4 | from pycparser.c_lexer import CLexer 5 | from pycparser.c_parser import CParser 6 | 7 | def toCamel(st: str) -> str: 8 | ret = [] 9 | has_underline = False 10 | for x in st: 11 | if x == '_': 12 | has_underline = True 13 | else: 14 | if has_underline: 15 | x = x.upper() 16 | ret.append(x) 17 | has_underline = False 18 | 19 | return ''.join(ret) 20 | 21 | def zigtypename(type: c_ast.Node) -> str: 22 | if isinstance(type, c_ast.PtrDecl): 23 | return '*' + zigtypename(type.type) 24 | if isinstance(type, c_ast.TypeDecl): 25 | return ' '.join(type.quals + [type.type.names[0]]) 26 | 27 | raise Exception('Not dealt with yet.') 28 | 29 | def getname(type: c_ast.Node) -> str: 30 | if isinstance(type, c_ast.FuncDecl) or isinstance(type, c_ast.PtrDecl): 31 | return getname(type.type) 32 | if isinstance(type, c_ast.TypeDecl): 33 | return type.declname 34 | else: 35 | print(type) 36 | 37 | raise Exception('Not dealt with yet.') 38 | 39 | # A simple visitor for FuncDef nodes that prints the names and 40 | # locations of function definitions. 41 | class FuncDefVisitor(c_ast.NodeVisitor): 42 | def visit_FuncDecl(self, node): 43 | fnname = getname(node) 44 | rettype = zigtypename(node.type) 45 | def args(): 46 | args = node.args 47 | if args == None: 48 | return [] 49 | else: 50 | return args.params 51 | 52 | argszig = [] 53 | for x in args(): 54 | parname = x.name 55 | argszig.append('{}: {}'.format(parname, zigtypename(x.type))) 56 | 57 | argscall = [] 58 | for x in args(): 59 | parname = x.name 60 | argscall.append(parname) 61 | 62 | 63 | argszig = ', '.join(['self: Self'] + argszig[1:]) 64 | argscall = ', '.join(['self.raw'] + argscall[1:]) 65 | zigfnname = fnname.strip('g_').strip('gi_') 66 | zigfnname = toCamel(zigfnname); 67 | print(f'pub fn {zigfnname}({argszig}) {rettype} {{\n\treturn C.{fnname}({argscall});\n}}') 68 | 69 | class AllEncompassingLexer(CLexer): 70 | pass 71 | 72 | def show_func_defs(string): 73 | ast = CParser().parse(string) 74 | 75 | v = FuncDefVisitor() 76 | v.visit(ast) 77 | 78 | def collect_defs(string, pkg) -> str: 79 | import tempfile 80 | import os 81 | flags = check_output(f'pkg-config --cflags {pkg}'.split()).decode().split() 82 | fd, name = tempfile.mkstemp() 83 | with os.fdopen(fd, 'w') as file: 84 | file.write(string) 85 | file.flush() 86 | expanded = check_output(['cpp'] + flags + [name]) 87 | with open("expanded.c", 'wb') as f: 88 | f.write(expanded) 89 | os.remove(name) 90 | return expanded.decode() 91 | 92 | 93 | if __name__ == "__main__": 94 | if len(sys.argv) > 1: 95 | string = sys.argv[1] 96 | else: 97 | string = sys.stdin.read(-1) 98 | 99 | string = collect_defs(string, 'gobject-introspection-1.0') 100 | print('\n'.join(['// '+x for x in ('input: \n' + string).splitlines() ])) 101 | 102 | show_func_defs(string) 103 | -------------------------------------------------------------------------------- /src/gintro.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const gintro = @import("lib.zig"); 3 | 4 | const StringMap = std.StringHashMap([]const u8); 5 | 6 | fn Iter(comptime Item: type) type { 7 | return struct { 8 | pub fn assert(iterator: anytype) void { 9 | const Iterator = @TypeOf(iterator); 10 | const functions = .{ .next = Item }; 11 | inline for (@typeInfo(@TypeOf(functions)).Struct.decls) |name| { 12 | if (!@hasDecl(Iterator, name)) { 13 | @compileError(std.fmt.allocPrint("Type \"{s}\" is missing function `{s}`", .{ Iterator, name })); 14 | } 15 | const actual_return_type = @typeInfo(@field(Iterator, name)).Fn.return_type; 16 | const expected_return_type = @field(functions, name); 17 | if (actual_return_type != expected_return_type) { 18 | @compileError(std.fmt.allocPrint("Function `{s}.{s}` expected to return `{s}`, but returns `{s}`", .{ Iterator, name, expected_return_type, actual_return_type })); 19 | } 20 | } 21 | } 22 | }; 23 | } 24 | 25 | fn escape_name(name: []const u8, allocator: std.mem.Allocator) ![]const u8 { 26 | var escname = try allocator.alloc(u8, name.len + 1); 27 | std.mem.copy(u8, escname, name); 28 | escname[escname.len - 1] = '_'; 29 | return escname; 30 | } 31 | 32 | const ItemSet = struct { 33 | storage: Storage, 34 | 35 | const Storage = std.StringArrayHashMap(void); 36 | 37 | pub fn init(allocator: std.mem.Allocator) ItemSet { 38 | return .{ .storage = Storage.init(allocator) }; 39 | } 40 | 41 | pub fn contains(self: *const ItemSet, name: []const u8) bool { 42 | return self.storage.contains(name); 43 | } 44 | 45 | pub fn add(self: *ItemSet, name: []const u8) !void { 46 | try self.storage.put(name, {}); 47 | } 48 | 49 | pub fn remove(self: *ItemSet, name: []const u8) !void { 50 | try self.storage.remove(name); 51 | } 52 | }; 53 | const MissingTypesMap = std.AutoArrayHashMap(gintro.TypeLib, ItemSet); 54 | 55 | pub fn ZigEmmiter(comptime Writer: type) type { 56 | return struct { 57 | writer: Writer, 58 | current_module: gintro.TypeLib = undefined, 59 | allocator: std.heap.ArenaAllocator, 60 | alloc: std.mem.Allocator = undefined, 61 | indent_level: u8 = 0, 62 | c_module_name: []const u8 = "C", 63 | tab_stop: u8 = 4, 64 | rename_table: StringMap, 65 | missing_types: MissingTypesMap, 66 | 67 | const Self = @This(); 68 | 69 | fn deinit(self: *Self) void { 70 | self.allocator.deinit(); 71 | } 72 | 73 | fn fmt_indent(self: *Self) !void { 74 | for (0..self.indent_level) |_| { 75 | _ = try self.write(" "); 76 | } 77 | } 78 | 79 | fn indent(self: *Self) void { 80 | self.indent_level += self.tab_stop; 81 | } 82 | 83 | fn unindent(self: *Self) void { 84 | self.indent_level -= self.tab_stop; 85 | } 86 | 87 | fn format(self: *Self, comptime fmt: []const u8, params: anytype) Writer.Error!void { 88 | try self.writer.print(fmt, params); 89 | } 90 | 91 | fn print(self: *Self, comptime fmt: []const u8, params: anytype) Writer.Error!void { 92 | try self.fmt_indent(); 93 | try self.format(fmt, params); 94 | } 95 | 96 | fn println(self: *Self, comptime fmt: []const u8, params: anytype) Writer.Error!void { 97 | try self.print(fmt ++ "\n", params); 98 | } 99 | 100 | fn write(self: *Self, txt: []const u8) Writer.Error!void { 101 | _ = try self.writer.writeAll(txt); 102 | } 103 | 104 | fn writeln(self: *Self, txt: []const u8) Writer.Error!void { 105 | try self.write(txt); 106 | try self.write("\n"); 107 | } 108 | 109 | fn type_info_to_zig(self: *Self, ty: gintro.TypeInfo) (Writer.Error || error{OutOfMemory})!void { 110 | const should_ref = switch (ty.getTag()) { 111 | .UTF8, .Array => false, 112 | .Void => false, 113 | else => true, 114 | }; 115 | if (ty.isPointer() and should_ref) { 116 | try self.write("*"); 117 | } 118 | switch (ty.getTag()) { 119 | .Boolean => try self.write("bool"), 120 | .Int8 => try self.write("i8"), 121 | .UInt8 => try self.write("u8"), 122 | .Int16 => try self.write("i16"), 123 | .UInt16 => try self.write("u16"), 124 | .Int32 => try self.write("i32"), 125 | .Unichar, .UInt32 => try self.write("u32"), 126 | .Int64 => try self.write("i64"), 127 | .UInt64 => try self.write("u64"), 128 | .Float => try self.write("f32"), 129 | .Double => try self.write("f64"), 130 | .GType => try self.write("glib.Type"), 131 | .UTF8 => try self.write("[*:0]const u8"), 132 | .Filename => try self.write("[*:0]const u8"), 133 | .Void => if (!ty.isPointer()) { 134 | try self.write("void"); 135 | } else { 136 | try self.write("*anyopaque"); 137 | }, 138 | .Array => { 139 | var item_type = ty.getParamType(0).?; 140 | defer item_type.unref(); 141 | if (ty.getArrayFixedSize()) |size| { 142 | try self.format("[{d}]", .{size}); 143 | } else { 144 | try self.write("[]"); 145 | } 146 | try self.type_info_to_zig(item_type); 147 | }, 148 | .Interface => { 149 | var interface = ty.getInterface().?; 150 | defer interface.unref(); 151 | if (interface.getTypeLib().raw != self.current_module.raw) { 152 | const tlib = interface.getTypeLib(); 153 | var missings = try self.missing_types.getOrPut(tlib); 154 | if (!missings.found_existing) { 155 | missings.value_ptr.* = ItemSet.init(self.alloc); 156 | } 157 | try missings.value_ptr.add(interface.getName().?); 158 | try self.format("{s}.", .{interface.getTypeLib().getNamespace()}); 159 | } 160 | if (interface.tryCast(gintro.CallbackInfo)) |callback| { 161 | try self.callback_to_zig(callback); 162 | } else { 163 | try self.write(interface.getName().?); 164 | } 165 | }, 166 | .GError => try self.write("glib.Error"), 167 | .GList, .GSList => { 168 | const tname = switch (ty.getTag()) { 169 | .GList => "List", 170 | .GSList => "SList", 171 | else => unreachable, 172 | }; 173 | try self.format("{s}.{s}", .{ self.c_module_name, tname }); 174 | if (ty.getParamType(0)) |item_type| { 175 | defer item_type.unref(); 176 | if (item_type.getTag() != .Void) { 177 | try self.write("("); 178 | try self.type_info_to_zig(item_type); 179 | try self.write(")"); 180 | } 181 | } 182 | }, 183 | .GHashTable => { 184 | try self.format("{s}.HashTable", .{self.c_module_name}); 185 | if (ty.getParamType(0)) |key_type| { 186 | defer key_type.unref(); 187 | if (key_type.getTag() != .Void) { 188 | try self.write("("); 189 | try self.type_info_to_zig(key_type); 190 | try self.write(", "); 191 | try self.type_info_to_zig(ty.getParamType(1).?); 192 | try self.write(")"); 193 | } 194 | } 195 | }, 196 | } 197 | } 198 | 199 | fn emit_prelude(self: *Self, c_lib_names: []const []const u8) !void { 200 | const s = "const {s} = @cImport("; 201 | try self.println(s, .{self.c_module_name}); 202 | const indent_space: u8 = @intCast(s.len - 3 + self.c_module_name.len); 203 | self.indent_level += indent_space; 204 | // C Imports 205 | for (c_lib_names) |c_lib_name| { 206 | try self.println("@cInclude(\"{s}\"),", .{c_lib_name}); 207 | } 208 | try self.println(");\n", .{}); 209 | self.indent_level -= indent_space; 210 | } 211 | 212 | fn get_name(self: *Self, name: []const u8) []const u8 { 213 | if (self.rename_table.get(name)) |new_name| { 214 | return new_name; 215 | } 216 | return name; 217 | } 218 | 219 | fn callback_to_zig(self: *Self, cinfo: gintro.CallbackInfo) !void { 220 | try self.write("fn ("); 221 | var a = cinfo.getArgsIterator(); 222 | while (a.next()) |arg| { 223 | defer arg.unref(); 224 | try self.arg_to_zig(arg); 225 | if (a.i < a.len) { 226 | try self.write(", "); 227 | } 228 | } 229 | try self.write(") "); 230 | if (cinfo.mayReturnNull()) { 231 | try self.write("?"); 232 | } 233 | try self.type_info_to_zig(cinfo.getReturnType()); 234 | } 235 | 236 | fn arg_to_zig(self: *Self, arg: gintro.ArgInfo) !void { 237 | try self.format("{s}", .{self.get_name(arg.getName().?)}); 238 | try self.write(": "); 239 | if (arg.isOptional()) { 240 | try self.write("?"); 241 | } 242 | const ty = arg.getType(); 243 | defer ty.unref(); 244 | if (arg.getDirection() == .Out) { 245 | try self.write("*"); 246 | } 247 | try self.type_info_to_zig(ty); 248 | } 249 | 250 | fn type_tag_to_zig(self: *Self, type_tag: gintro.TypeTag) []const u8 { 251 | _ = self; 252 | return switch (type_tag) { 253 | .Int8 => "i8", 254 | .UInt8 => "u8", 255 | .Int16 => "i16", 256 | .UInt16 => "u16", 257 | .Int32 => "i32", 258 | .Unichar, .UInt32 => "u32", 259 | .Int64 => "i64", 260 | .UInt64 => "u64", 261 | .Float => "f32", 262 | .Double => "f64", 263 | else => unreachable, 264 | }; 265 | } 266 | 267 | fn field_to_zig(self: *Self, field: gintro.FieldInfo) !void { 268 | if (field.getFlags().isReadable()) { 269 | var ftype = field.getType(); 270 | defer ftype.unref(); 271 | try self.println("// tag: {s}", .{@tagName(ftype.getTag())}); 272 | try self.print("{s}: ", .{self.get_name(field.getName().?)}); 273 | try self.type_info_to_zig(ftype); 274 | } 275 | } 276 | 277 | fn method_to_zig(self: *Self, this_type: ?[]const u8, method: gintro.FunctionInfo) !void { 278 | const fname = self.get_name(method.getName().?); 279 | try self.rename_table.put(fname, try escape_name(fname, self.alloc)); 280 | defer { 281 | const item = self.rename_table.fetchRemove(fname).?; 282 | self.alloc.free(item.value); 283 | } 284 | try self.print("pub fn {s}(", .{fname}); 285 | if (this_type) |t| { 286 | try self.format("self: {s}, ", .{t}); 287 | } 288 | var a = method.getArgsIterator(); 289 | var arg_names = try std.ArrayList([]const u8).initCapacity(self.alloc, @intCast(a.len + 1)); 290 | if (this_type) |_| { 291 | arg_names.appendAssumeCapacity(try self.alloc.dupe(u8, "self")); 292 | } 293 | while (a.next()) |arg| { 294 | defer arg.unref(); 295 | try self.arg_to_zig(arg); 296 | if (a.i < a.len) { 297 | try self.write(", "); 298 | } 299 | arg_names.appendAssumeCapacity(try self.alloc.dupe(u8, self.get_name(arg.getName().?))); 300 | } 301 | try self.write(") "); 302 | if (method.mayReturnNull()) { 303 | try self.write("?"); 304 | } 305 | try self.type_info_to_zig(method.getReturnType()); 306 | try self.writeln(" {"); 307 | self.indent(); 308 | { 309 | defer self.unindent(); 310 | const args = try std.mem.join(self.alloc, ", ", arg_names.items); 311 | defer self.alloc.free(args); 312 | try self.println("return {s}.{s}({s});", .{ self.c_module_name, method.getSymbol(), args }); 313 | } 314 | try self.fmt_indent(); 315 | try self.writeln("}"); 316 | } 317 | 318 | pub fn fields_to_zig(self: *Self, field_iterator: anytype) !void { 319 | Iter(gintro.FieldInfo).assert(field_iterator); 320 | var iter = field_iterator; 321 | while (iter.next()) |field| { 322 | defer field.unref(); 323 | try self.field_to_zig(field); 324 | try self.write(",\n"); 325 | } 326 | } 327 | 328 | pub fn methods_to_zig(self: *Self, this_type: []const u8, method_iterator: anytype) !void { 329 | Iter(gintro.FunctionInfo).assert(method_iterator); 330 | var iter = method_iterator; 331 | while (iter.next()) |method| { 332 | defer method.unref(); 333 | try self.method_to_zig(this_type, method); 334 | } 335 | } 336 | 337 | pub fn constant_to_zig(self: *Self, constant: gintro.ConstantInfo) !void { 338 | try self.print("pub extern const {s}: ", .{self.get_name(constant.getName().?)}); 339 | const ty = constant.getType(); 340 | defer ty.unref(); 341 | try self.type_info_to_zig(ty); 342 | try self.write(";\n"); 343 | } 344 | 345 | pub fn attrs_to_zig(self: *Self, attr_iterator: anytype) !void { 346 | Iter([2][]const u8).assert(attr_iterator); 347 | var iter = attr_iterator; 348 | while (iter.next()) |attr| { 349 | try self.println("// Attr: {s}={s}", .{ attr[0], attr[1] }); 350 | } 351 | } 352 | 353 | pub fn struct_to_zig(self: *Self, sinfo: gintro.StructInfo) !void { 354 | if (sinfo.getTypeName()) |tname| { 355 | try self.println("// GObject name: {s}", .{tname}); 356 | } 357 | try self.println("// Struct", .{}); 358 | try self.println("pub const {s} = extern struct {{", .{sinfo.getName().?}); 359 | self.indent(); 360 | { 361 | defer self.unindent(); 362 | 363 | // Attrs 364 | try self.attrs_to_zig(sinfo.getAttributeIterator()); 365 | 366 | // fields 367 | try self.fields_to_zig(sinfo.getFieldsIterator()); 368 | // methods 369 | try self.methods_to_zig("*@This()", sinfo.getMethodsIterator()); 370 | } 371 | try self.println("}};", .{}); 372 | } 373 | 374 | pub fn object_to_zig(self: *Self, oinfo: gintro.ObjectInfo) !void { 375 | if (oinfo.getTypeName()) |tname| { 376 | try self.println("// GObject name: {s}", .{tname}); 377 | } 378 | try self.println("// Object", .{}); 379 | const oname = oinfo.getName().?; 380 | 381 | // Object Struct 382 | try self.println("pub const {s} = struct {{", .{oname}); 383 | self.indent(); 384 | { 385 | defer self.unindent(); 386 | 387 | // Attrs 388 | try self.attrs_to_zig(oinfo.getAttributeIterator()); 389 | 390 | // fields 391 | try self.fields_to_zig(oinfo.getFieldsIterator()); 392 | 393 | try self.println("}};", .{}); 394 | } 395 | // Methods 396 | try self.println("pub fn Extend{s}(comptime Self: type, comptime Raw: type) type {{", .{oname}); 397 | self.indent(); 398 | { 399 | defer self.unindent(); 400 | try self.println("return struct {{", .{}); 401 | self.indent(); 402 | { 403 | defer self.unindent(); 404 | 405 | // Attrs 406 | try self.attrs_to_zig(oinfo.getAttributeIterator()); 407 | 408 | // fields 409 | try self.fields_to_zig(oinfo.getFieldsIterator()); 410 | // methods 411 | try self.methods_to_zig("Self", oinfo.getMethodsIterator()); 412 | } 413 | try self.println("}};", .{}); 414 | } 415 | try self.println("}}", .{}); 416 | } 417 | 418 | fn enum_to_zig(self: *Self, einfo: gintro.EnumInfo) !void { 419 | try self.println("// Enum", .{}); 420 | try self.println("pub const {s} = enum({s}) {{", .{ einfo.getName().?, self.type_tag_to_zig(einfo.getStorageType()) }); 421 | self.indent(); 422 | { 423 | defer self.unindent(); 424 | var iter = einfo.getValuesIterator(); 425 | while (iter.next()) |value| { 426 | defer value.unref(); 427 | try self.println("{s} = {d},", .{ self.get_name(value.getName().?), value.getValue() }); 428 | } 429 | } 430 | try self.println("}};", .{}); 431 | } 432 | 433 | pub fn baseinfo_to_zig(self: *Self, info: gintro.BaseInfo) !void { 434 | switch (info.getInfoType()) { 435 | .Struct => try self.struct_to_zig(info.uncheckedCast(gintro.StructInfo)), 436 | .Object => try self.object_to_zig(info.uncheckedCast(gintro.ObjectInfo)), 437 | .Constant => try self.constant_to_zig(info.uncheckedCast(gintro.ConstantInfo)), 438 | .Enum, .Flags => try self.enum_to_zig(info.uncheckedCast(gintro.EnumInfo)), 439 | .Function => { 440 | var f = info.uncheckedCast(gintro.FunctionInfo); 441 | if (f.isMethod()) { 442 | try self.method_to_zig(null, f); 443 | } 444 | }, 445 | else => try self.println("// {?s}: {} not implemented", .{ info.getName(), info.getInfoType() }), 446 | // else => {} 447 | } 448 | } 449 | 450 | pub fn namespace_to_zig(self: *Self, namespace: gintro.Namespace, repo: ?gintro.Repository) !void { 451 | try self.namespace_to_zig_filtered(namespace, repo, null); 452 | } 453 | 454 | pub fn namespace_to_zig_filtered(self: *Self, namespace: gintro.Namespace, repo: ?gintro.Repository, only: ?ItemSet) !void { 455 | var repository = repo orelse gintro.Repository.default(); 456 | self.current_module = try repository.require(namespace, .None); 457 | 458 | // Require dependencies 459 | const deps = repository.getDependencies(namespace).?; 460 | defer gintro.freeVString(deps); 461 | 462 | var typelibs = std.AutoArrayHashMap(gintro.TypeLib, gintro.Namespace).init(self.alloc); 463 | 464 | for (deps) |dep| { 465 | const dep2 = dep[0..std.mem.len(dep)]; 466 | var sequences = std.mem.splitScalar(u8, dep2, '-'); 467 | const name0 = sequences.next().?; 468 | const name = try self.alloc.dupeZ(u8, name0); 469 | const ver = try self.alloc.dupeZ(u8, sequences.next().?); 470 | const r = .{ .name = name, .version = ver }; 471 | 472 | const deplib = try repository.require(r, .None); 473 | try typelibs.put(deplib, r); 474 | } 475 | 476 | var iterator = repository.getInfoIterator(namespace); 477 | while (iterator.next()) |info| { 478 | defer info.unref(); 479 | if (only) |only2| { 480 | if (!only2.contains(info.getName().?)) { 481 | continue; 482 | } 483 | } 484 | try self.baseinfo_to_zig(info); 485 | } 486 | 487 | // Add missing types 488 | var missed_iter = self.missing_types.iterator(); 489 | while (missed_iter.next()) |item| { 490 | var missing_items_namespace = typelibs.get(item.key_ptr.*).?; 491 | const missing_items = item.value_ptr.*; 492 | try self.namespace_to_zig_filtered(missing_items_namespace, repository, missing_items); 493 | } 494 | } 495 | }; 496 | } 497 | 498 | fn zig_emitter(writer: anytype, alloc: std.mem.Allocator) !ZigEmmiter(@TypeOf(writer)) { 499 | var a = std.heap.ArenaAllocator.init(alloc); 500 | var a2 = a.allocator(); 501 | return .{ .writer = writer, .rename_table = try name_map(a2), .missing_types = MissingTypesMap.init(alloc), .allocator = a, .alloc = a2 }; 502 | } 503 | 504 | pub fn name_map(allocator: std.mem.Allocator) !StringMap { 505 | var map = StringMap.init(allocator); 506 | try map.put("error", "error_"); 507 | try map.put("type", "type_"); 508 | try map.put("anytype", "anytype_"); 509 | try map.put("continue", "continue_"); 510 | try map.put("self", "self_"); 511 | try map.put("enum", "enum_"); 512 | try map.put("union", "union_"); 513 | try map.put("struct", "struct_"); 514 | try map.put("async", "async_"); 515 | return map; 516 | } 517 | 518 | pub fn main() !void { 519 | var repo = gintro.Repository.default(); 520 | var girepository = gintro.Namespace{ .name = "GIRepository", .version = "2.0" }; 521 | var glib = gintro.Namespace{ .name = "GLib", .version = "2.0" }; 522 | _ = glib; 523 | // var gi = gintro.Namespace{ .name = "Gtk", .version = "4.0" }; 524 | var out = (try std.fs.cwd().createFile("/tmp/ex.zig", .{ .truncate = true })); 525 | defer out.close(); 526 | var buf = std.io.bufferedWriter(out.writer()); 527 | defer buf.flush() catch unreachable; 528 | var a = std.heap.GeneralPurposeAllocator(.{}){}; 529 | var emitter = try zig_emitter(buf.writer(), a.allocator()); 530 | try emitter.emit_prelude(&.{"girepository.h"}); 531 | try emitter.namespace_to_zig(girepository, repo); 532 | } 533 | -------------------------------------------------------------------------------- /src/lib.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const C = @cImport(@cInclude("girepository.h")); 3 | 4 | pub const RepositoryLoadFlags = enum(c_int) { 5 | LoadLazy = C.G_IREPOSITORY_LOAD_FLAG_LAZY, 6 | None = 0, 7 | _, 8 | }; 9 | 10 | pub const Namespace = struct { 11 | name: [*:0]const u8, 12 | version: [*:0]const u8, 13 | }; 14 | 15 | pub fn gbool(value: C.gboolean) bool { 16 | return value != 0; 17 | } 18 | 19 | pub const GString = []const C.gchar; 20 | pub fn stringFromC(raw: [*c]const C.gchar) ?GString { 21 | if (raw) |ptr| { 22 | const len = std.mem.len(ptr); 23 | return ptr[0..len]; 24 | } 25 | return null; 26 | } 27 | 28 | pub fn freeGString(str: GString) void { 29 | C.g_free(@intFromPtr(str.ptr)); 30 | } 31 | 32 | pub const VString = []const [*c]const C.gchar; 33 | 34 | pub fn vstringFromC(raw: [*c]const [*c]const C.gchar) ?VString { 35 | if (raw) |ptr| { 36 | const len = std.mem.len(ptr); 37 | return ptr[0..len]; 38 | } 39 | return null; 40 | } 41 | 42 | pub fn freeVString(str: VString) void { 43 | C.g_strfreev(@ptrFromInt(@intFromPtr(str.ptr))); 44 | } 45 | 46 | /// Currently, acquiring a typelib from a repository is the only supported way. 47 | pub const TypeLib = struct { 48 | raw: *C.GITypelib, 49 | 50 | pub fn getNamespace(self: @This()) GString { 51 | return stringFromC(C.g_typelib_get_namespace(self.raw)).?; 52 | } 53 | }; 54 | 55 | /// This handles every error directly caused by the GObject Introspection library. 56 | pub const RepositoryError = error{ 57 | TypeLibNotFound, 58 | NamespaceMismatch, 59 | NamespaceVersionConflict, 60 | LibraryNotFound, 61 | UnknownError, 62 | }; 63 | 64 | /// A generic iterator type used to iterate over Introspection's types that follow this pattern: 65 | /// int _get_n_s() 66 | /// int _get_() 67 | /// 68 | /// As of now, I'm not sure what this type absolutely needs, to it may change. 69 | /// Also, I'm not sure I like this paradigm I made, so if you have a better idea, DO make a pull request. 70 | fn Iterator(comptime Iterable: type, comptime Get: type) type { 71 | return struct { 72 | iterable: Iterable, 73 | len: c_int, 74 | i: c_int, 75 | 76 | pub fn new(iterable: Iterable) @This() { 77 | const len = iterable.len(); 78 | return .{ .iterable = iterable, .len = len, .i = 0 }; 79 | } 80 | 81 | pub fn next(self: *@This()) ?Get { 82 | if (self.i >= self.len) { 83 | return null; 84 | } 85 | defer self.i += 1; 86 | return Get.fromC(self.iterable.index(self.i)); 87 | } 88 | }; 89 | } 90 | 91 | // Auto Generated by `gen.py` 92 | /// Enum that encodes all of Introspection's types. 93 | pub const InfoType = enum(c_int) { 94 | Invalid = C.GI_INFO_TYPE_INVALID, 95 | Function = C.GI_INFO_TYPE_FUNCTION, 96 | Callback = C.GI_INFO_TYPE_CALLBACK, 97 | Struct = C.GI_INFO_TYPE_STRUCT, 98 | Boxed = C.GI_INFO_TYPE_BOXED, 99 | Enum = C.GI_INFO_TYPE_ENUM, 100 | Flags = C.GI_INFO_TYPE_FLAGS, 101 | Object = C.GI_INFO_TYPE_OBJECT, 102 | Interface = C.GI_INFO_TYPE_INTERFACE, 103 | Constant = C.GI_INFO_TYPE_CONSTANT, 104 | Union = C.GI_INFO_TYPE_UNION, 105 | Value = C.GI_INFO_TYPE_VALUE, 106 | Signal = C.GI_INFO_TYPE_SIGNAL, 107 | Vfunc = C.GI_INFO_TYPE_VFUNC, 108 | Property = C.GI_INFO_TYPE_PROPERTY, 109 | Field = C.GI_INFO_TYPE_FIELD, 110 | Arg = C.GI_INFO_TYPE_ARG, 111 | Type = C.GI_INFO_TYPE_TYPE, 112 | Unresolved = C.GI_INFO_TYPE_UNRESOLVED, 113 | 114 | pub fn fromC(en: c_uint) @This() { 115 | return @as(InfoType, @enumFromInt(en)); 116 | } 117 | }; 118 | 119 | /// Gnome docs says different repos might never be supported, but we make the assumption that it's a possibility. 120 | pub const Repository = struct { 121 | raw: *C.GIRepository, 122 | last_error: ?*C.GError, 123 | 124 | const Self = @This(); 125 | 126 | /// Returns the default Repository of this process. 127 | pub fn default() Self { 128 | return .{ 129 | .last_error = null, 130 | .raw = C.g_irepository_get_default(), 131 | }; 132 | } 133 | /// Be aware the flag argument *will probably change* due to the API only specifying ONE flag. 134 | /// If this returns an UnknownError, it happened in GLib. In that case, more information is avaliable at Repository.last_error. 135 | pub fn require(self: *Self, namespace: Namespace, flag: RepositoryLoadFlags) RepositoryError!TypeLib { 136 | var raw = C.g_irepository_require(self.raw, namespace.name, namespace.version, @as(c_uint, @intCast(@intFromEnum(flag))), &self.last_error); 137 | if (raw) |ptr| { 138 | return TypeLib{ .raw = ptr }; 139 | } else if (self.last_error) |err| { 140 | return switch (err.code) { 141 | C.G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND => error.TypeLibNotFound, 142 | C.G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH => error.NamespaceMismatch, 143 | C.G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT => error.NamespaceVersionConflict, 144 | C.G_IREPOSITORY_ERROR_LIBRARY_NOT_FOUND => error.LibraryNotFound, 145 | else => error.UnknownError, 146 | }; 147 | } else { 148 | unreachable; 149 | } 150 | } 151 | 152 | /// You should free the resulting array with `VString.deinit`. 153 | /// 154 | /// Note: before you call this, you should call `require` to make sure the namespace is loaded. 155 | pub fn getDependencies(self: *Self, namespace: Namespace) ?VString { 156 | var raw = C.g_irepository_get_dependencies(self.raw, namespace.name); 157 | return vstringFromC(raw); 158 | } 159 | /// See [getDependencies] for more info. 160 | pub fn getImmediateDependencies(self: *Self, namespace: Namespace) ?VString { 161 | var raw = C.g_irepository_get_immediate_dependencies(self.raw, namespace.name); 162 | return vstringFromC(raw); 163 | } 164 | 165 | const InfoIterator = Iterator(struct { 166 | namespace: Namespace, 167 | repo: *C.GIRepository, 168 | 169 | const Raw = C.GIBaseInfo; 170 | 171 | pub fn len(self: @This()) i32 { 172 | return C.g_irepository_get_n_infos(self.repo, self.namespace.name); 173 | } 174 | 175 | pub fn index(self: @This(), i: i32) *Raw { 176 | return C.g_irepository_get_info(self.repo, self.namespace.name, i); 177 | } 178 | }, BaseInfo); 179 | 180 | /// Returns an `Iterator` that goes through all the `BaseInfo`s in the given namespace. 181 | pub fn getInfoIterator(self: *Self, namespace: Namespace) InfoIterator { 182 | return InfoIterator.new(.{ .namespace = namespace, .repo = self.raw }); 183 | } 184 | }; 185 | 186 | fn FallbackDecl(comptime name: []const u8, comptime Decl: type) type { 187 | if (!@hasDecl(Decl, name)) { 188 | return Decl; 189 | } else { 190 | return struct {}; 191 | } 192 | } 193 | 194 | pub const BaseInfo = struct { 195 | raw: *Raw, 196 | 197 | const Self = @This(); 198 | 199 | const Raw = C.GIBaseInfo; 200 | pub usingnamespace ExtendBaseInfo(@This(), Raw); 201 | pub fn is(value: anytype) bool { 202 | _ = value; 203 | return true; 204 | } 205 | }; 206 | 207 | pub fn ExtendBaseInfo(comptime Self: type, comptime Raw: type) type { 208 | return struct { 209 | raw: *Raw, 210 | 211 | pub fn fromC(raw: [*c]Raw) Self { 212 | const self = Self{ .raw = @alignCast(@ptrCast(raw)) }; 213 | std.debug.assert(Self.is(self)); 214 | return self; 215 | } 216 | 217 | /// Call this to create a strong reference to this `BaseInfo`. 218 | pub fn ref(self: Self) Self { 219 | return .{ .raw = C.g_base_info_ref(self.raw).? }; 220 | } 221 | /// Call this when you're done with this `BaseInfo`. 222 | pub fn unref(self: Self) void { 223 | C.g_base_info_unref(self.raw); 224 | } 225 | /// Gets this `BaseInfo` introspection type. Note that this is a "meta" 226 | /// function that describes the type of the `BaseInfo` instance. 227 | /// 228 | /// Note: This is useful for casting to the given `subclass`. 229 | pub fn getInfoType(self: Self) InfoType { 230 | var value = C.g_base_info_get_type(self.raw); 231 | return InfoType.fromC(value); 232 | } 233 | /// Returns the name of this `BaseInfo`. 234 | /// 235 | /// Note: Often, this is a human readable name and 99% what you'll want 236 | /// to use when generating code. 237 | pub fn getName(self: Self) ?GString { 238 | const res = C.g_base_info_get_name(self.raw); 239 | return stringFromC(res); 240 | } 241 | 242 | /// Tries to cast to `T` and returns `null` if it fails 243 | pub fn tryCast(self: Self, comptime T: type) ?T { 244 | if (T.is(self)) { 245 | return Self.uncheckedCast(self, T); 246 | } 247 | return null; 248 | } 249 | 250 | /// Casts to `T` without checking if it's the correct type 251 | pub fn uncheckedCast(self: Self, comptime T: type) T { 252 | return T.fromC(@as(*T.Raw, @ptrCast(self.raw))); 253 | } 254 | 255 | pub fn equal(self: Self, other: anytype) bool { 256 | return gbool(C.g_base_info_equal(self.raw, other.raw)); 257 | } 258 | 259 | const BaseInfoAttributesIterator = struct { 260 | raw: C.GIAttributeIter, 261 | ref: Self, 262 | 263 | pub fn new(ref_: Self) @This() { 264 | return .{ 265 | .raw = std.mem.zeroes(C.GIAttributeIter), 266 | .ref = ref_, 267 | }; 268 | } 269 | 270 | pub fn next(self: *@This()) ?[2]GString { 271 | var name: [*c]C.gchar = undefined; 272 | var val: [*c]C.gchar = undefined; 273 | if (C.g_base_info_iterate_attributes(self.ref.raw, &self.raw, &name, &val) == 1) { 274 | const nstr = stringFromC(name).?; 275 | const vstr = stringFromC(val).?; 276 | return .{ nstr, vstr }; 277 | } else { 278 | return null; 279 | } 280 | } 281 | }; 282 | 283 | pub fn getTypeLib(self: Self) TypeLib { 284 | return TypeLib{ .raw = @ptrCast(C.g_base_info_get_typelib(self.raw)) }; 285 | } 286 | 287 | pub fn getAttributeIterator(self: Self) BaseInfoAttributesIterator { 288 | return BaseInfoAttributesIterator.new(self); 289 | } 290 | }; 291 | } 292 | 293 | pub const ConstantInfo = struct { 294 | raw: *Raw, 295 | pub const Raw = C.GIConstantInfo; 296 | pub usingnamespace ExtendConstantInfo(@This(), Raw); 297 | const Self = @This(); 298 | pub fn is(value: anytype) bool { 299 | return C.GI_IS_CONSTANT_INFO(value.raw); 300 | } 301 | }; 302 | 303 | pub fn ExtendConstantInfo(comptime Self: type, comptime Raw: type) type { 304 | return struct { 305 | pub usingnamespace ExtendBaseInfo(Self, Raw); 306 | pub fn getType(self: Self) TypeInfo { 307 | return TypeInfo.fromC(C.g_constant_info_get_type(self.raw)); 308 | } 309 | pub fn getValue(self: Self) Argument { 310 | var value: Argument = undefined; 311 | C.g_constant_info_get_value(self.raw, &value); 312 | return value; 313 | } 314 | }; 315 | } 316 | 317 | pub const RegisteredTypeInfo = struct { 318 | raw: *Raw, 319 | 320 | pub const Raw = C.GIRegisteredTypeInfo; 321 | pub usingnamespace ExtendRegisteredTypeInfo(Self, Raw); 322 | 323 | const Self = @This(); 324 | }; 325 | 326 | pub fn ExtendRegisteredTypeInfo(comptime Self: type, comptime Raw: type) type { 327 | return struct { 328 | raw: *Raw, 329 | 330 | pub usingnamespace ExtendBaseInfo(Self, Raw); 331 | 332 | /// Obtain the type name of the struct within the GObject type system. 333 | /// This type can be passed to g_type_name() to get a GType. 334 | pub fn getTypeName(self: Self) ?GString { 335 | var str = C.g_registered_type_info_get_type_name(self.raw); 336 | return stringFromC(str); 337 | } 338 | 339 | pub fn getGType(self: Self) C.GType { 340 | return C.g_registered_type_info_get_g_type(self.raw); 341 | } 342 | }; 343 | } 344 | 345 | pub const StructInfo = struct { 346 | raw: *Raw, 347 | 348 | pub const Raw = C.GIStructInfo; 349 | pub usingnamespace ExtendStructInfo(@This(), Raw); 350 | 351 | pub fn is(value: anytype) bool { 352 | return C.GI_IS_STRUCT_INFO(value.raw); 353 | } 354 | }; 355 | 356 | pub fn ExtendStructInfo(comptime Self: type, comptime Raw: type) type { 357 | return struct { 358 | pub usingnamespace ExtendRegisteredTypeInfo(Self, Raw); 359 | 360 | const FieldsIterator = Iterator(struct { 361 | raw: *StructInfo.Raw, 362 | 363 | pub fn index(self: @This(), i: c_int) *FieldInfo.Raw { 364 | return C.g_struct_info_get_field(self.raw, i); 365 | } 366 | pub fn len(self: @This()) c_int { 367 | return C.g_struct_info_get_n_fields(self.raw); 368 | } 369 | }, FieldInfo); 370 | 371 | /// Iterates over all the fields of this struct. Don't forget to call `unref`. 372 | pub fn getFieldsIterator(self: Self) FieldsIterator { 373 | return FieldsIterator.new(.{ .raw = self.raw }); 374 | } 375 | 376 | pub fn getMethodsIterator(self: Self) MethodsIterator { 377 | return MethodsIterator.new(.{ .raw = self.raw }); 378 | } 379 | 380 | const MethodsIterator = Iterator(struct { 381 | raw: *StructInfo.Raw, 382 | 383 | pub fn index(self: @This(), i: c_int) *FunctionInfo.Raw { 384 | return C.g_struct_info_get_method(self.raw, i); 385 | } 386 | pub fn len(self: @This()) c_int { 387 | return C.g_struct_info_get_n_methods(self.raw); 388 | } 389 | }, FunctionInfo); 390 | }; 391 | } 392 | 393 | pub const TypeInfoFlags = struct { 394 | raw: C.GIFieldInfoFlags, 395 | 396 | pub fn isReadable(self: @This()) bool { 397 | return (self.raw & C.GI_FIELD_IS_READABLE) != 0; 398 | } 399 | 400 | pub fn isWritable(self: @This()) bool { 401 | return (self.raw & C.GI_FIELD_IS_WRITABLE) != 0; 402 | } 403 | }; 404 | 405 | pub const FieldInfo = struct { 406 | raw: *Raw, 407 | 408 | pub const Raw = C.GIFieldInfo; 409 | pub usingnamespace ExtendFieldInfo(@This(), Raw); 410 | 411 | pub fn is(value: anytype) bool { 412 | return C.GI_IS_FIELD_INFO(value.raw); 413 | } 414 | }; 415 | 416 | pub fn ExtendFieldInfo(comptime Self: type, comptime Raw: type) type { 417 | return struct { 418 | pub usingnamespace ExtendBaseInfo(Self, Raw); 419 | 420 | pub fn getType(self: Self) TypeInfo { 421 | return TypeInfo.fromC(C.g_field_info_get_type(self.raw)); 422 | } 423 | pub fn getFlags(self: Self) TypeInfoFlags { 424 | return TypeInfoFlags{ .raw = C.g_field_info_get_flags(self.raw) }; 425 | } 426 | 427 | pub fn getSize(self: Self) usize { 428 | return @as(usize, @intCast(C.g_field_info_get_size(self.raw))); 429 | } 430 | 431 | pub fn getOffset(self: Self) usize { 432 | return @as(usize, @intCast(C.g_field_info_get_offset(self.raw))); 433 | } 434 | }; 435 | } 436 | 437 | pub const TypeTag = enum(c_uint) { 438 | Void = C.GI_TYPE_TAG_VOID, 439 | Boolean = C.GI_TYPE_TAG_BOOLEAN, 440 | Int8 = C.GI_TYPE_TAG_INT8, 441 | UInt8 = C.GI_TYPE_TAG_UINT8, 442 | Int16 = C.GI_TYPE_TAG_INT16, 443 | UInt16 = C.GI_TYPE_TAG_UINT16, 444 | Int32 = C.GI_TYPE_TAG_INT32, 445 | UInt32 = C.GI_TYPE_TAG_UINT32, 446 | Int64 = C.GI_TYPE_TAG_INT64, 447 | UInt64 = C.GI_TYPE_TAG_UINT64, 448 | Float = C.GI_TYPE_TAG_FLOAT, 449 | Double = C.GI_TYPE_TAG_DOUBLE, 450 | GType = C.GI_TYPE_TAG_GTYPE, 451 | UTF8 = C.GI_TYPE_TAG_UTF8, 452 | Filename = C.GI_TYPE_TAG_FILENAME, 453 | Array = C.GI_TYPE_TAG_ARRAY, 454 | Interface = C.GI_TYPE_TAG_INTERFACE, 455 | GList = C.GI_TYPE_TAG_GLIST, 456 | GSList = C.GI_TYPE_TAG_GSLIST, 457 | GHashTable = C.GI_TYPE_TAG_GHASH, 458 | GError = C.GI_TYPE_TAG_ERROR, 459 | Unichar = C.GI_TYPE_TAG_UNICHAR, 460 | 461 | fn fromC(raw: c_uint) @This() { 462 | return @as(@This(), @enumFromInt(raw)); 463 | } 464 | 465 | pub fn toString(self: @This()) GString { 466 | return stringFromC(C.g_type_tag_to_string(@intFromEnum(self))).?; 467 | } 468 | }; 469 | const ArrayType = enum(c_uint) { 470 | C = C.GI_ARRAY_TYPE_C, 471 | Array = C.GI_ARRAY_TYPE_ARRAY, 472 | PtrArray = C.GI_ARRAY_TYPE_PTR_ARRAY, 473 | ByteArray = C.GI_ARRAY_TYPE_BYTE_ARRAY, 474 | 475 | fn fromC(raw: c_uint) @This() { 476 | return @as(@This(), @enumFromInt(raw)); 477 | } 478 | }; 479 | 480 | pub const TypeInfo = struct { 481 | raw: *Raw, 482 | 483 | pub usingnamespace ExtendTypeInfo(Self, Raw); 484 | 485 | const Self = @This(); 486 | pub const Raw = C.GITypeInfo; 487 | 488 | pub fn is(value: anytype) bool { 489 | return C.GI_IS_TYPE_INFO(value.raw); 490 | } 491 | }; 492 | 493 | pub fn ExtendTypeInfo(comptime Self: type, comptime Raw: type) type { 494 | return struct { 495 | pub const Super = BaseInfo; 496 | 497 | pub usingnamespace ExtendBaseInfo(Self, Raw); 498 | 499 | pub fn getTag(self: Self) TypeTag { 500 | return TypeTag.fromC(C.g_type_info_get_tag(self.raw)); 501 | } 502 | 503 | pub fn getInterface(self: Self) ?BaseInfo { 504 | if (C.g_type_info_get_interface(self.raw)) |raw| { 505 | return BaseInfo.fromC(raw); 506 | } 507 | return null; 508 | } 509 | 510 | pub fn isPointer(self: Self) bool { 511 | return gbool(C.g_type_info_is_pointer(self.raw)); 512 | } 513 | 514 | pub fn getArrayType(self: Self) ArrayType { 515 | return ArrayType.fromC(C.g_type_info_get_array_type(self.raw)); 516 | } 517 | 518 | pub fn getParamType(self: Self, n: c_int) ?TypeInfo { 519 | if (C.g_type_info_get_param_type(self.raw, n)) |ptr| { 520 | return TypeInfo.fromC(ptr); 521 | } 522 | return null; 523 | } 524 | 525 | pub fn getArrayFixedSize(self: Self) ?usize { 526 | const size = C.g_type_info_get_array_fixed_size(self.raw); 527 | if (size == -1) { 528 | return null; 529 | } 530 | return @intCast(size); 531 | } 532 | pub fn getArrayLength(self: Self) ?usize { 533 | const size = C.g_type_info_get_array_length(self.raw); 534 | if (size == -1) { 535 | return null; 536 | } 537 | return @intCast(size); 538 | } 539 | }; 540 | } 541 | 542 | const Direction = enum(c_uint) { 543 | In = C.GI_DIRECTION_IN, 544 | Out = C.GI_DIRECTION_OUT, 545 | InOut = C.GI_DIRECTION_INOUT, 546 | }; 547 | 548 | pub const ArgInfo = struct { 549 | raw: *Raw, 550 | 551 | pub usingnamespace ExtendArgInfo(Self, Raw); 552 | 553 | const Self = @This(); 554 | pub const Raw = C.GIArgInfo; 555 | 556 | pub fn is(value: anytype) bool { 557 | return C.GI_IS_ARG_INFO(value.raw); 558 | } 559 | }; 560 | 561 | pub fn ExtendArgInfo(comptime Self: type, comptime Raw: type) type { 562 | return struct { 563 | pub usingnamespace ExtendBaseInfo(Self, Raw); 564 | 565 | pub fn getClosure(self: Self) ?usize { 566 | const index = C.g_arg_info_get_closure(self.raw); 567 | if (index == -1) null else @as(usize, @intCast(index)); 568 | } 569 | pub fn getDestroy(self: Self) ?usize { 570 | const index = C.g_arg_info_get_destroy(self.raw); 571 | if (index == -1) null else @as(usize, @intCast(index)); 572 | } 573 | pub fn getDirection(self: Self) Direction { 574 | return @enumFromInt(C.g_arg_info_get_direction(self.raw)); 575 | } 576 | pub fn getOwnershipTransfer(self: Self) C.GITransfer { 577 | return C.g_arg_info_get_ownership_transfer(self.raw); 578 | } 579 | pub fn getScope(self: Self) C.GIScopeType { 580 | return C.g_arg_info_get_scope( 581 | self.raw, 582 | ); 583 | } 584 | pub fn getType(self: Self) TypeInfo { 585 | return TypeInfo.fromC(C.g_arg_info_get_type( 586 | self.raw, 587 | )); 588 | } 589 | // Takes ownership of `ty` 590 | pub fn loadType(self: Self, ty: TypeInfo) void { 591 | return C.g_arg_info_load_type(self.raw, ty.raw); 592 | } 593 | pub fn mayBeNull(self: Self) bool { 594 | return gbool(C.g_arg_info_may_be_null(self.raw)); 595 | } 596 | pub fn isCallerAllocates(self: Self) bool { 597 | return gbool(C.g_arg_info_is_caller_allocates(self.raw)); 598 | } 599 | pub fn isOptional(self: Self) bool { 600 | return gbool(C.g_arg_info_is_optional(self.raw)); 601 | } 602 | pub fn isReturnValue(self: Self) C.gboolean { 603 | return gbool(C.g_arg_info_is_return_value(self.raw)); 604 | } 605 | pub fn isSkip(self: Self) C.gboolean { 606 | return gbool(C.g_arg_info_is_skip(self.raw)); 607 | } 608 | }; 609 | } 610 | 611 | const Argument = C.GIArgument; 612 | 613 | pub fn toArgument(value: anytype) Argument { 614 | const utils = struct { 615 | fn fail() noreturn { 616 | @compileError("Value of type " ++ @typeName(@TypeOf(value)) ++ " is not convertible to GIArgument."); 617 | } 618 | fn ceilPow2(number: comptime_int) comptime_int { 619 | const log2 = std.math.log_2_ceil(@TypeOf(number), number); 620 | return log2 * log2; 621 | } 622 | fn isWrapper() bool { 623 | return @hasField(value, "raw") and 624 | @typeInfo(@TypeOf(value.raw)) == .Pointer; 625 | } 626 | fn pointer(ptr: anytype) Argument { 627 | return .{ .v_pointer = @as(C.gpointer, @ptrCast(ptr)) }; 628 | } 629 | }; 630 | 631 | if (utils.isWrapper()) { 632 | return utils.pointer(value.raw); 633 | } 634 | 635 | return switch (@TypeOf(value)) { 636 | .Pointer => utils.pointer(value), 637 | .Float => |fl| if (fl.bits == 32) .{ .v_float = value } else if (fl.bits == 64) .{ .v_doube = value } else utils.fail(), 638 | .Bool => .{ .v_boolean = value }, 639 | .Optional => |opt| if (@typeInfo(opt.child) == .Pointer) utils.pointer(value) else utils.fail(), 640 | _ => utils.fail(), 641 | }; 642 | } 643 | 644 | pub const CallableInfo = struct { 645 | raw: *Raw, 646 | 647 | pub usingnamespace ExtendCallableInfo(Self, Raw); 648 | 649 | const Self = @This(); 650 | pub const Raw = C.GICallableInfo; 651 | 652 | pub fn is(value: anytype) bool { 653 | return C.GI_IS_CALLABLE_INFO(value.raw); 654 | } 655 | }; 656 | 657 | pub fn ExtendCallableInfo(comptime Self: type, comptime Raw: type) type { 658 | return struct { 659 | pub usingnamespace ExtendBaseInfo(Self, Raw); 660 | 661 | pub fn isMethod(self: Self) bool { 662 | return gbool(C.g_callable_info_is_method(self.raw)); 663 | } 664 | 665 | pub fn getReturnType(self: Self) TypeInfo { 666 | const raw = C.g_callable_info_get_return_type(self.raw); 667 | return TypeInfo.fromC(raw); 668 | } 669 | 670 | pub fn mayReturnNull(self: Self) bool { 671 | return gbool(C.g_callable_info_may_return_null(self.raw)); 672 | } 673 | 674 | pub fn getArgsIterator(self: Self) ArgsIterator { 675 | return ArgsIterator.new(.{ .raw = self.raw }); 676 | } 677 | 678 | pub fn invoke( 679 | self: Self, 680 | in_args: []const Argument, 681 | out_args: []Argument, 682 | ) !Argument { 683 | var ret: Argument = undefined; 684 | var err: *C.GError = undefined; 685 | const result = C.g_function_info_invoke(self.raw, in_args.ptr, in_args.len, out_args.ptr, out_args.len, &ret, &err); 686 | if (gbool(result)) { 687 | return ret; 688 | } else { 689 | unreachable; 690 | } 691 | } 692 | const ArgsIterator = Iterator(struct { 693 | raw: *Raw, 694 | 695 | pub fn len(self: @This()) c_int { 696 | return C.g_callable_info_get_n_args(self.raw); 697 | } 698 | 699 | pub fn index(self: @This(), i: c_int) *ArgInfo.Raw { 700 | return C.g_callable_info_get_arg(self.raw, i); 701 | } 702 | }, ArgInfo); 703 | }; 704 | } 705 | 706 | pub const FunctionInfo = struct { 707 | raw: *Raw, 708 | 709 | pub usingnamespace ExtendFunctionInfo(Self, Raw); 710 | 711 | const Self = @This(); 712 | pub const Raw = C.GIFunctionInfo; 713 | 714 | pub fn is(value: anytype) bool { 715 | return C.GI_IS_FUNCTION_INFO(value.raw); 716 | } 717 | }; 718 | 719 | pub fn ExtendFunctionInfo(comptime Self: type, comptime Raw: type) type { 720 | return struct { 721 | pub usingnamespace ExtendCallableInfo(Self, Raw); 722 | 723 | pub fn getSymbol(self: Self) GString { 724 | const raw = C.g_function_info_get_symbol(self.raw); 725 | return stringFromC(raw).?; 726 | } 727 | }; 728 | } 729 | 730 | pub const CallbackInfo = struct { 731 | raw: *Raw, 732 | 733 | pub usingnamespace ExtendCallbackInfo(Self, Raw); 734 | 735 | const Self = @This(); 736 | pub const Raw = C.GICallbackInfo; 737 | 738 | pub fn is(value: anytype) bool { 739 | return value.getInfoType() == .Callback; 740 | } 741 | }; 742 | 743 | pub fn ExtendCallbackInfo(comptime Self: type, comptime Raw: type) type { 744 | return struct { 745 | pub usingnamespace ExtendCallableInfo(Self, Raw); 746 | }; 747 | } 748 | 749 | pub const ValueInfo = struct { 750 | raw: *Raw, 751 | 752 | pub usingnamespace ExtendValueInfo(Self, Raw); 753 | 754 | const Self = @This(); 755 | pub const Raw = C.GIValueInfo; 756 | 757 | pub fn is(value: anytype) bool { 758 | return C.GI_IS_VALUE_INFO(value.raw); 759 | } 760 | }; 761 | 762 | pub fn ExtendValueInfo(comptime Self: type, comptime Raw: type) type { 763 | return struct { 764 | pub usingnamespace ExtendRegisteredTypeInfo(Self, Raw); 765 | pub fn getValue(self: Self) u64 { 766 | return @intCast(C.g_value_info_get_value(self.raw)); 767 | } 768 | }; 769 | } 770 | 771 | pub const EnumInfo = struct { 772 | raw: *Raw, 773 | 774 | pub usingnamespace ExtendEnumInfo(Self, Raw); 775 | 776 | const Self = @This(); 777 | pub const Raw = C.GIEnumInfo; 778 | 779 | pub fn is(value: anytype) bool { 780 | return C.GI_IS_ENUM_INFO(value.raw); 781 | } 782 | }; 783 | 784 | pub fn ExtendEnumInfo(comptime Self: type, comptime Raw: type) type { 785 | return struct { 786 | pub usingnamespace ExtendRegisteredTypeInfo(Self, Raw); 787 | 788 | pub const ValuesIterator = Iterator(struct { 789 | raw: *Raw, 790 | pub fn len(self: @This()) c_int { 791 | return C.g_enum_info_get_n_values(self.raw); 792 | } 793 | pub fn index(self: @This(), i: c_int) *ValueInfo.Raw { 794 | return C.g_enum_info_get_value(self.raw, i); 795 | } 796 | }, ValueInfo); 797 | 798 | pub const MethodsIterator = Iterator(struct { 799 | raw: *Raw, 800 | pub fn len(self: @This()) c_int { 801 | return C.g_enum_info_get_n_methods(self.raw); 802 | } 803 | pub fn index(self: @This(), i: c_int) *FunctionInfo.Raw { 804 | return C.g_enum_info_get_method(self.raw, i); 805 | } 806 | }, FunctionInfo); 807 | 808 | pub fn getValuesIterator(self: Self) ValuesIterator { 809 | return ValuesIterator.new(.{ .raw = self.raw }); 810 | } 811 | 812 | pub fn getMethodsIterator(self: Self) MethodsIterator { 813 | return MethodsIterator.new(.{ .raw = self.raw }); 814 | } 815 | 816 | pub fn getStorageType(self: Self) TypeTag { 817 | const raw = C.g_enum_info_get_storage_type(self.raw); 818 | return TypeTag.fromC(raw); 819 | } 820 | 821 | pub fn getErrorDomain(self: Self) ?GString { 822 | const raw = C.g_enum_info_get_error_domain(self.raw); 823 | return stringFromC(raw); 824 | } 825 | }; 826 | } 827 | 828 | pub const ObjectInfo = struct { 829 | raw: *Raw, 830 | 831 | pub usingnamespace ExtendObjectInfo(Self, Raw); 832 | 833 | const Self = @This(); 834 | pub const Raw = C.GIObjectInfo; 835 | 836 | pub fn is(value: anytype) bool { 837 | return C.GI_IS_OBJECT_INFO(value.raw); 838 | } 839 | }; 840 | 841 | pub fn ExtendObjectInfo(comptime Self: type, comptime Raw: type) type { 842 | return struct { 843 | pub usingnamespace ExtendRegisteredTypeInfo(Self, Raw); 844 | 845 | pub fn getParent(self: Self) Self { 846 | return Self.fromC(C.g_object_info_get_parent(self.raw)); 847 | } 848 | 849 | pub fn getMethodsIterator(self: Self) MethodsIterator { 850 | return MethodsIterator.new(.{ .raw = self.raw }); 851 | } 852 | 853 | pub fn getFieldsIterator(self: Self) FieldsIterator { 854 | return FieldsIterator.new(.{ .raw = self.raw }); 855 | } 856 | 857 | pub const MethodsIterator = Iterator(struct { 858 | raw: *Raw, 859 | pub fn len(self: @This()) c_int { 860 | return C.g_object_info_get_n_methods(self.raw); 861 | } 862 | pub fn index(self: @This(), i: c_int) *FunctionInfo.Raw { 863 | return C.g_object_info_get_method(self.raw, i); 864 | } 865 | }, FunctionInfo); 866 | 867 | pub const FieldsIterator = Iterator(struct { 868 | raw: *Raw, 869 | pub fn len(self: @This()) c_int { 870 | return C.g_object_info_get_n_fields(self.raw); 871 | } 872 | pub fn index(self: @This(), i: c_int) *FieldInfo.Raw { 873 | return C.g_object_info_get_field(self.raw, i); 874 | } 875 | }, FieldInfo); 876 | }; 877 | } 878 | 879 | // Creating a new sub struct boilerplate: 880 | // pub const YourType = struct { 881 | // raw: *Raw, 882 | 883 | // pub usingnamespace ExtendYourType(Self, Raw); 884 | 885 | // const Self = @This(); 886 | // pub const Raw = C.GIYourType; 887 | // 888 | // pub fn is(value: anytype) bool { 889 | // return C.GI_IS_YOURTYPE_INFO(value.raw); 890 | // } 891 | // }; 892 | 893 | // pub fn ExtendYourType(comptime Self: type, comptime Raw: type) type { 894 | // return struct { 895 | // pub usingnamespace ExtendPARENTTYPE(Self, Raw); 896 | // 897 | // // Your methods: 898 | // 899 | // }; 900 | // } 901 | --------------------------------------------------------------------------------