module: sending global event requires sender module awareness
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
9eac721f24
commit
3661cb8721
4 changed files with 39 additions and 31 deletions
|
|
@ -122,6 +122,6 @@ test "example" {
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
// Send events to modules
|
// Send events to modules
|
||||||
world.modules.send(.tick, .{});
|
world.mod.renderer.sendGlobal(.tick, .{});
|
||||||
try world.dispatch();
|
try world.dispatch();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ pub fn World(comptime mods: anytype) type {
|
||||||
pub inline fn sendGlobal(m: *@This(), comptime event_name: Modules.GlobalEvent, args: anytype) void {
|
pub inline fn sendGlobal(m: *@This(), comptime event_name: Modules.GlobalEvent, args: anytype) void {
|
||||||
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
||||||
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
||||||
world.modules.send(event_name, args);
|
world.modules.sendGlobal(module_tag, event_name, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatchNoError(m: *@This()) void {
|
pub fn dispatchNoError(m: *@This()) void {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ pub const Engine = struct {
|
||||||
.{ .local = .begin_pass, .handler = beginPass },
|
.{ .local = .begin_pass, .handler = beginPass },
|
||||||
.{ .local = .end_pass, .handler = endPass },
|
.{ .local = .end_pass, .handler = endPass },
|
||||||
.{ .local = .present, .handler = present },
|
.{ .local = .present, .handler = present },
|
||||||
|
.{ .global = .init, .handler = fn () void },
|
||||||
|
.{ .global = .deinit, .handler = fn () void },
|
||||||
.{ .global = .tick, .handler = fn () void },
|
.{ .global = .tick, .handler = fn () void },
|
||||||
.{ .global = .exit, .handler = fn () void },
|
.{ .global = .exit, .handler = fn () void },
|
||||||
};
|
};
|
||||||
|
|
@ -115,7 +117,7 @@ pub const App = struct {
|
||||||
|
|
||||||
pub fn update(app: *@This()) !bool {
|
pub fn update(app: *@This()) !bool {
|
||||||
// TODO: better dispatch implementation
|
// TODO: better dispatch implementation
|
||||||
app.world.modules.send(.tick, .{});
|
app.world.mod.engine.sendGlobal(.tick, .{});
|
||||||
try app.world.dispatch(); // dispatch .tick
|
try app.world.dispatch(); // dispatch .tick
|
||||||
try app.world.dispatch(); // dispatch any events produced by .tick
|
try app.world.dispatch(); // dispatch any events produced by .tick
|
||||||
return app.world.mod.engine.state.should_exit;
|
return app.world.mod.engine.state.should_exit;
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ pub fn Modules(comptime mods: anytype) type {
|
||||||
|
|
||||||
const Handler = switch (@typeInfo(@TypeOf(event.handler))) {
|
const Handler = switch (@typeInfo(@TypeOf(event.handler))) {
|
||||||
.Fn => @TypeOf(event.handler),
|
.Fn => @TypeOf(event.handler),
|
||||||
.Type => |t| switch (@typeInfo(t)) {
|
.Type => switch (@typeInfo(event.handler)) {
|
||||||
.Fn => event.handler,
|
.Fn => event.handler,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
},
|
},
|
||||||
|
|
@ -106,39 +106,45 @@ pub fn Modules(comptime mods: anytype) type {
|
||||||
|
|
||||||
/// Returns an args tuple representing the standard, uninjected, arguments which the given
|
/// Returns an args tuple representing the standard, uninjected, arguments which the given
|
||||||
/// global event handler requires.
|
/// global event handler requires.
|
||||||
fn Args(event_name: GlobalEvent) type {
|
fn GlobalArgs(module_name: ModuleName(mods), event_name: GlobalEvent) type {
|
||||||
inline for (modules) |M| {
|
inline for (modules) |M| {
|
||||||
_ = Module(M); // Validate the module
|
_ = Module(M); // Validate the module
|
||||||
|
if (M.name != module_name) continue;
|
||||||
inline for (M.events) |event| {
|
return GlobalArgsM(M, event_name);
|
||||||
const Ev = @TypeOf(event);
|
|
||||||
const name_tag = if (@hasField(Ev, "global")) event.global else continue;
|
|
||||||
if (name_tag != event_name) continue;
|
|
||||||
|
|
||||||
const Handler = switch (@typeInfo(@TypeOf(event.handler))) {
|
|
||||||
.Fn => @TypeOf(event.handler),
|
|
||||||
.Type => switch (@typeInfo(event.handler)) {
|
|
||||||
.Fn => event.handler,
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: passing std.meta.Tuple here instead of TupleHACK results in a compiler
|
|
||||||
// segfault. The only difference is that TupleHACk does not produce a real tuple,
|
|
||||||
// `@Type(.{.Struct = .{ .is_tuple = false }})` instead of `.is_tuple = true`.
|
|
||||||
return UninjectedArgsTuple(TupleHACK, Handler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@compileError("No global event handler ." ++ @tagName(event_name) ++ " is defined in any module.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a global event
|
pub fn GlobalArgsM(comptime M: type, event_name: GlobalEvent) type {
|
||||||
pub fn send(
|
_ = Module(M); // Validate the module
|
||||||
|
inline for (M.events) |event| {
|
||||||
|
const Ev = @TypeOf(event);
|
||||||
|
const name_tag = if (@hasField(Ev, "global")) event.global else continue;
|
||||||
|
if (name_tag != event_name) continue;
|
||||||
|
|
||||||
|
const Handler = switch (@typeInfo(@TypeOf(event.handler))) {
|
||||||
|
.Fn => @TypeOf(event.handler),
|
||||||
|
.Type => switch (@typeInfo(event.handler)) {
|
||||||
|
.Fn => event.handler,
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: passing std.meta.Tuple here instead of TupleHACK results in a compiler
|
||||||
|
// segfault. The only difference is that TupleHACk does not produce a real tuple,
|
||||||
|
// `@Type(.{.Struct = .{ .is_tuple = false }})` instead of `.is_tuple = true`.
|
||||||
|
return UninjectedArgsTuple(TupleHACK, Handler);
|
||||||
|
}
|
||||||
|
@compileError("mach: module ." ++ @tagName(M.name) ++ " has no .global event handler for ." ++ @tagName(event_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a global event which the specified module defines
|
||||||
|
pub fn sendGlobal(
|
||||||
m: *@This(),
|
m: *@This(),
|
||||||
// TODO: is a variant of this function where event_name is not comptime known, but asserted to be a valid enum, useful?
|
// TODO: is a variant of this function where event_name is not comptime known, but asserted to be a valid enum, useful?
|
||||||
|
comptime module_name: ModuleName(mods),
|
||||||
comptime event_name: GlobalEvent,
|
comptime event_name: GlobalEvent,
|
||||||
args: Args(event_name),
|
args: GlobalArgs(module_name, event_name),
|
||||||
) void {
|
) void {
|
||||||
// TODO: comptime safety/debugging
|
// TODO: comptime safety/debugging
|
||||||
m.sendInternal(null, @intFromEnum(event_name), args);
|
m.sendInternal(null, @intFromEnum(event_name), args);
|
||||||
|
|
@ -986,7 +992,7 @@ test "dispatch" {
|
||||||
// global event handler declaration within a module. It is required that all global event handlers
|
// global event handler declaration within a module. It is required that all global event handlers
|
||||||
// of the same name have the same standard arguments, although they can start with different
|
// of the same name have the same standard arguments, although they can start with different
|
||||||
// injected arguments.
|
// injected arguments.
|
||||||
modules.send(.tick, .{});
|
modules.sendGlobal(.tick, .{});
|
||||||
try testing.expect(usize, 0).eql(global.ticks);
|
try testing.expect(usize, 0).eql(global.ticks);
|
||||||
try modules.dispatch(.{&foo});
|
try modules.dispatch(.{&foo});
|
||||||
try testing.expect(usize, 2).eql(global.ticks);
|
try testing.expect(usize, 2).eql(global.ticks);
|
||||||
|
|
@ -998,7 +1004,7 @@ test "dispatch" {
|
||||||
// Global events which are not handled by anyone yet can be written as `pub const fooBar = fn() void;`
|
// Global events which are not handled by anyone yet can be written as `pub const fooBar = fn() void;`
|
||||||
// within a module, which allows pre-declaring that `fooBar` is a valid global event, and enables
|
// within a module, which allows pre-declaring that `fooBar` is a valid global event, and enables
|
||||||
// its arguments to be inferred still like this:
|
// its arguments to be inferred still like this:
|
||||||
modules.send(.frame_done, .{ .@"0" = 1337 });
|
modules.sendGlobal(.frame_done, .{ .@"0" = 1337 });
|
||||||
|
|
||||||
// Local events
|
// Local events
|
||||||
modules.sendToModule(.engine_renderer, .update, .{});
|
modules.sendToModule(.engine_renderer, .update, .{});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue