diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-08-05 00:48:42 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-08-05 01:06:20 +0000 |
| commit | 57f37fbd6a4816f3a70041e04877beeb0a37b45c (patch) | |
| tree | bd054c31e2a3b8705197f85705484bda4fe56698 /src/control.zig | |
| parent | add freeze and step controls to Source (diff) | |
| download | glsl-view-57f37fbd6a4816f3a70041e04877beeb0a37b45c.tar.gz glsl-view-57f37fbd6a4816f3a70041e04877beeb0a37b45c.zip | |
dynamic OSC method registration for sources
Diffstat (limited to 'src/control.zig')
| -rw-r--r-- | src/control.zig | 202 |
1 files changed, 96 insertions, 106 deletions
diff --git a/src/control.zig b/src/control.zig index 4f4d547..71e688a 100644 --- a/src/control.zig +++ b/src/control.zig @@ -1,7 +1,9 @@ +const std = @import("std"); const c = @import("c.zig"); const gl = @import("gl.zig"); -const std = @import("std"); +const src = @import("source.zig"); const cfg = @import("config.zig"); +const util = @import("util.zig"); const build_config = @import("build_config"); fn verify_args(expected: u8, got: []const u8) !void { @@ -84,26 +86,9 @@ fn set_array( } } -fn set_texture( - cache: *gl.UniformCache, - dest: *?*gl.Texture, - texture_type: gl.Texture.Type, - types: []const u8, - argv: []const [*c]c.lo_arg, -) !void { - if (types.len != 1 or types[0] != 's') return error.invalidType; - - const nameZ: [*:0]const u8 = @ptrCast(&argv[0].*.s); - const name = std.mem.span(nameZ); - - const source = cache.textures.get(name) orelse return error.texNotFound; - if (source.texture.type != texture_type) return error.wrongTextureType; - - dest.* = &source.texture; -} - pub const ControlServer = struct { server: c.lo_server, + sources: std.StringHashMap(*src.Source), cache: *gl.UniformCache, config: *cfg.Config, @@ -111,34 +96,6 @@ pub const ControlServer = struct { progress: std.Progress.Node, allocator: std.mem.Allocator, - const ZigCallback = fn (*ControlServer, []const u8, []const u8, []const [*c]c.lo_arg) anyerror!bool; - const CCallback = fn ([*c]const u8, [*c]const u8, [*c][*c]c.lo_arg, c_int, c.lo_message, ?*anyopaque) callconv(.C) c_int; - fn wrapCallback(comptime inner: ZigCallback) CCallback { - return struct { - fn wrapped( - _path: ?[*:0]const u8, - _types: ?[*:0]const u8, - _argv: [*c][*c]c.lo_arg, - argc: c_int, - msg: c.lo_message, - userdata: ?*anyopaque, - ) callconv(.C) c_int { - const self: *ControlServer = @ptrCast(@alignCast(userdata.?)); - const path = std.mem.span(_path.?); - const types = std.mem.span(_types.?); - const args = if (_argv) |argv| argv[0..@intCast(argc)] else &.{}; - - _ = msg; - - const res = inner(self, path, types, args) catch |err| { - std.debug.print("Error handling {s}: {}\n", .{ path, err }); - return 1; - }; - return if (res) 0 else 1; - } - }.wrapped; - } - pub fn init( allocator: std.mem.Allocator, progress: std.Progress.Node, @@ -149,6 +106,7 @@ pub const ControlServer = struct { var self: *ControlServer = try allocator.create(ControlServer); self.* = .{ .server = null, + .sources = std.StringHashMap(*src.Source).init(cache.allocator), .cache = cache, .config = config, @@ -164,31 +122,74 @@ pub const ControlServer = struct { if (self.server == null) return error.serverInitializationError; - _ = c.lo_server_add_method(self.server, "/shader", "s", wrapCallback(handle_shader), @as(*anyopaque, @ptrCast(self))); - // _ = c.lo_server_add_method(self.server, "/reload", "", handle_reload, @as(*anyopaque, @ptrCast(self))); - _ = c.lo_server_add_method(self.server, "/uniform/*", null, wrapCallback(handle_uniform), @as(*anyopaque, @ptrCast(self))); + try self.add_method(&.{ "", "shader" }, "s", handle_shader); + // try self.add_method(&.{ "", "reload" }, "", handle_reload); + try self.add_method(&.{ "", "uniform", "*" }, null, handle_uniform); if (build_config.have_ffmpeg) { - _ = c.lo_server_add_method(self.server, "/texture/*/video", null, wrapCallback(handle_texture_video), @as(*anyopaque, @ptrCast(self))); - _ = c.lo_server_add_method(self.server, "/texture/*/stream", null, wrapCallback(handle_texture_stream), @as(*anyopaque, @ptrCast(self))); + try self.add_method(&.{ "", "texture", "*", "video" }, null, handle_texture_video); + try self.add_method(&.{ "", "texture", "*", "stream" }, null, handle_texture_stream); } if (build_config.have_tsv) { - _ = c.lo_server_add_method(self.server, "/texture/*/tsv", "ss", wrapCallback(handle_texture_tsv), @as(*anyopaque, @ptrCast(self))); + try self.add_method(&.{ "", "texture", "*", "tsv" }, "ss", handle_texture_tsv); } - _ = c.lo_server_add_method(self.server, "/texture/*/freeze", "T", wrapCallback(handle_texture_freeze), @as(*anyopaque, @ptrCast(self))); - _ = c.lo_server_add_method(self.server, "/texture/*/freeze", "F", wrapCallback(handle_texture_freeze), @as(*anyopaque, @ptrCast(self))); - _ = c.lo_server_add_method(self.server, "/texture/*/step", "", wrapCallback(handle_texture_step), @as(*anyopaque, @ptrCast(self))); - _ = c.lo_server_add_method(self.server, "/texture/*/destroy", "", wrapCallback(handle_destroy_texture), @as(*anyopaque, @ptrCast(self))); + try self.add_method(&.{ "", "texture", "*", "destroy" }, "", handle_destroy_texture); return self; } + fn add_method(self: *ControlServer, pieces: []const []const u8, types: ?[]const u8, handler: anytype) !void { + try self.add_method_for(ControlServer, self, pieces, types, handler); + } + + pub fn add_method_for(self: *ControlServer, comptime Self: type, data: *Self, pieces: []const []const u8, types: ?[]const u8, handler: anytype) !void { + const path = try util.tmpJoinZ("/", pieces); + + const wrapped = struct { + fn wrapped( + _path: ?[*:0]const u8, + _types: ?[*:0]const u8, + _argv: [*c][*c]c.lo_arg, + argc: c_int, + msg: c.lo_message, + userdata: ?*anyopaque, + ) callconv(.C) c_int { + const i_self: *Self = @ptrCast(@alignCast(userdata.?)); + const i_path = std.mem.span(_path.?); + const i_types = std.mem.span(_types.?); + const args = if (_argv) |argv| argv[0..@intCast(argc)] else &.{}; + + _ = msg; + + const res = handler(i_self, i_path, i_types, args) catch |err| { + std.debug.print("Error handling {s}: {}\n", .{ i_path, err }); + return 1; + }; + return if (res) 0 else 1; + } + }.wrapped; + + _ = c.lo_server_add_method(self.server, path, if (types) |t| t.ptr else null, wrapped, @as(*anyopaque, @ptrCast(data))); + } + + pub fn del_method(self: *ControlServer, pieces: []const []const u8, types: ?[]const u8) !void { + const path = try util.tmpJoinZ("/", pieces); + _ = c.lo_server_del_method(self.server, path, if (types) |t| t.ptr else null); + } + pub fn update(self: *ControlServer) void { while (c.lo_server_recv_noblock(self.server, 0) > 0) {} } - pub fn destroy(self: ControlServer) void { + pub fn destroy(self: *ControlServer) void { c.lo_server_free(self.server); + + var tit = self.sources.iterator(); + while (tit.next()) |entry| { + entry.value_ptr.*.deinit(self.allocator); + self.allocator.free(entry.key_ptr.*); + } + self.sources.deinit(); } fn handle_error(num: c_int, msg: [*c]const u8, where: [*c]const u8) callconv(.C) void { @@ -219,6 +220,24 @@ pub const ControlServer = struct { return true; } + fn set_texture( + self: *ControlServer, + dest: *?*gl.Texture, + texture_type: gl.Texture.Type, + types: []const u8, + argv: []const [*c]c.lo_arg, + ) !void { + if (types.len != 1 or types[0] != 's') return error.invalidType; + + const nameZ: [*:0]const u8 = @ptrCast(&argv[0].*.s); + const name = std.mem.span(nameZ); + + const source = self.sources.get(name) orelse return error.texNotFound; + if (source.texture.type != texture_type) return error.wrongTextureType; + + dest.* = &source.texture; + } + fn handle_uniform(self: *ControlServer, path: []const u8, types: []const u8, argv: []const [*c]c.lo_arg) !bool { var parts = std.mem.tokenizeScalar(u8, path, '/'); @@ -292,18 +311,18 @@ pub const ControlServer = struct { .SAMPLER_2D_SHADOW, .INT_SAMPLER_2D, .UNSIGNED_INT_SAMPLER_2D, - => |val| set_texture(self.cache, val, .TEXTURE_2D, types, argv), + => |val| self.set_texture(val, .TEXTURE_2D, types, argv), .SAMPLER_2D_ARRAY, .SAMPLER_2D_ARRAY_SHADOW, .INT_SAMPLER_2D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_ARRAY, - => |val| set_texture(self.cache, val, .TEXTURE_2D_ARRAY, types, argv), + => |val| self.set_texture(val, .TEXTURE_2D_ARRAY, types, argv), .SAMPLER_3D, .INT_SAMPLER_3D, .UNSIGNED_INT_SAMPLER_3D, - => |val| set_texture(self.cache, val, .TEXTURE_3D, types, argv), + => |val| self.set_texture(val, .TEXTURE_3D, types, argv), else => error.uniformNotSupported, }; @@ -312,13 +331,21 @@ pub const ControlServer = struct { return true; } + fn add_source(self: *ControlServer, name: []const u8, source: *src.Source) !void { + const key = try self.cache.allocator.dupe(u8, name); + errdefer self.cache.allocator.free(key); + try self.sources.put(key, source); + + source.register(name, self); + } + fn handle_texture_video(self: *ControlServer, path: []const u8, types: []const u8, argv: []const [*c]c.lo_arg) !bool { try verify_args('s', types); var parts = std.mem.tokenizeScalar(u8, path, '/'); _ = parts.next(); const name = parts.next() orelse unreachable; - if (self.cache.textures.contains(name)) return error.textureNameTaken; + if (self.sources.contains(name)) return error.textureNameTaken; const texture_typeZ: [*:0]const u8 = @ptrCast(&argv[0].*.s); const texture_type = std.meta.stringToEnum(gl.Texture.Type, std.mem.span(texture_typeZ)) orelse return error.invalidType; @@ -328,10 +355,7 @@ pub const ControlServer = struct { const ffmpeg = @import("ffmpeg.zig"); const source = try ffmpeg.VideoSource.init(self.cache.allocator, self.progress, texture_type, filenameZ, formatZ, args); - - const key = try self.cache.allocator.dupe(u8, name); - errdefer self.cache.allocator.free(key); - try self.cache.textures.put(key, source); + try self.add_source(name, source); return true; } @@ -342,7 +366,7 @@ pub const ControlServer = struct { var parts = std.mem.tokenizeScalar(u8, path, '/'); _ = parts.next(); const name = parts.next() orelse unreachable; - if (self.cache.textures.contains(name)) return error.textureNameTaken; + if (self.sources.contains(name)) return error.textureNameTaken; const texture_typeZ: [*:0]const u8 = @ptrCast(&argv[0].*.s); const filenameZ: [*:0]const u8 = @ptrCast(&argv[1].*.s); @@ -353,10 +377,7 @@ pub const ControlServer = struct { const ffmpeg = @import("ffmpeg.zig"); const source = try ffmpeg.StreamSource.init(self.cache.allocator, self.constants, filenameZ, formatZ, args); - - const key = try self.cache.allocator.dupe(u8, name); - errdefer self.cache.allocator.free(key); - try self.cache.textures.put(key, source); + try self.add_source(name, source); return true; } @@ -367,7 +388,7 @@ pub const ControlServer = struct { var parts = std.mem.tokenizeScalar(u8, path, '/'); _ = parts.next(); const name = parts.next() orelse unreachable; - if (self.cache.textures.contains(name)) return error.textureNameTaken; + if (self.sources.contains(name)) return error.textureNameTaken; const texture_typeZ: [*:0]const u8 = @ptrCast(&argv[0].*.s); const texture_type = std.meta.stringToEnum(gl.Texture.Type, std.mem.span(texture_typeZ)) orelse return error.invalidType; @@ -375,39 +396,7 @@ pub const ControlServer = struct { const tsv = @import("tsv.zig"); const source = try tsv.TSVSource.init(self.cache.allocator, self.constants, texture_type, tsv_nameZ); - - const key = try self.cache.allocator.dupe(u8, name); - errdefer self.cache.allocator.free(key); - try self.cache.textures.put(key, source); - - return true; - } - - fn handle_texture_freeze(self: *ControlServer, path: []const u8, types: []const u8, argv: []const [*c]c.lo_arg) !bool { - _ = argv; - - var parts = std.mem.tokenizeScalar(u8, path, '/'); - _ = parts.next(); - const name = parts.next() orelse unreachable; - - if (self.cache.textures.get(name)) |source| { - source.freeze = types[0] == 'T'; - } else return false; - - return true; - } - - fn handle_texture_step(self: *ControlServer, path: []const u8, types: []const u8, argv: []const [*c]c.lo_arg) !bool { - _ = argv; - _ = types; - - var parts = std.mem.tokenizeScalar(u8, path, '/'); - _ = parts.next(); - const name = parts.next() orelse unreachable; - - if (self.cache.textures.get(name)) |source| { - source.step.store(true, .monotonic); - } else return false; + try self.add_source(name, source); return true; } @@ -420,8 +409,9 @@ pub const ControlServer = struct { _ = parts.next(); const name = parts.next() orelse unreachable; - if (self.cache.textures.fetchRemove(name)) |kv| { + if (self.sources.fetchRemove(name)) |kv| { // @TODO: find all references to kv.value.texture in UniformCache and unassign + kv.value.unregister(name, self); kv.value.deinit(self.cache.allocator); self.cache.allocator.free(kv.key); } else return false; |
