aboutsummaryrefslogtreecommitdiff
path: root/src/event-set.zig
blob: da17734fa845147ab8b5d7dd24aa2fce7905eb3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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: *const 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(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(), _: *const 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);
}