const std = @import("std"); const wayland = @import("root.zig"); pub fn EventSet(T: type, events: anytype) type { return struct { pub fn on_event(ptr: *anyopaque, ctx: wayland.Context, opcode: u16, args: []const u8) void { var offset: usize = 0; inline for (events, 0..) |event, index| { if (@TypeOf(event) != @TypeOf(null) and index == opcode) { const Args = std.meta.ArgsTuple(@TypeOf(event)); var fn_args: Args = undefined; fn_args[0] = @alignCast(@ptrCast(ptr)); fn_args[1] = ctx; inline for (std.meta.fields(Args)[2..], 2..) |f, arg_index| { switch (f.type) { void => {}, u32, i32 => { const result = std.mem.readPackedIntNative(f.type, args, offset * 8); fn_args[arg_index] = result; offset += @sizeOf(f.type); }, []const u8 => { const size: u32 = std.mem.readPackedIntNative(u32, args, offset * 8); const padding_size: usize = @mod(@as(usize, @bitCast(-@as(isize, @intCast(size)))), 4); offset += @sizeOf(u32); const result = args[offset..offset + size - 1]; offset += size + padding_size; fn_args[arg_index] = result; }, *wayland.Object => { const result = std.mem.readPackedIntNative(u32, args, offset * 8); fn_args[arg_index] = ctx.display.registry.get_by_id(result) orelse unreachable; offset += @sizeOf(u32); }, else => |t| switch (@typeInfo(t)) { .@"enum" => { const result = std.mem.readPackedIntNative(u32, args, offset * 8); fn_args[arg_index] = @enumFromInt(result); offset += @sizeOf(u32); }, else => @compileError(std.fmt.comptimePrint("unsupported type {}", .{t})) }, } } switch (@typeInfo(@typeInfo(@TypeOf(event)).@"fn".return_type orelse void)) { .void => @call(.auto, event, fn_args), .error_union => @call(.auto, event, fn_args) catch |err| { std.log.err("error on event {} ({}): {}", .{opcode, T, err}); }, else => |t| @compileError(std.fmt.comptimePrint("unsupported return type {}", .{t})), } return; } } std.log.warn("unimplemented event {} for {}", .{opcode, T}); } }; } test { const Dummy = struct { a: u32 = 0, b: u32 = 0, pub fn something(self: *@This(), _: wayland.Context, a: u32, b: u32) void { self.a = a; self.b = b; } }; var dummy = Dummy{}; const Events = EventSet(Dummy, .{null, Dummy.something }); var ctx: wayland.Context = undefined; Events.on_event(&dummy, &ctx, 1, &(std.mem.toBytes(@as(u32, 1)) ++ std.mem.toBytes(@as(u32, 2)))); try std.testing.expectEqual(dummy.a, 1); try std.testing.expectEqual(dummy.b, 2); }