aboutsummaryrefslogtreecommitdiffstats
path: root/src/control.zig
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2025-08-05 00:48:42 +0000
committers-ol <s+removethis@s-ol.nu>2025-08-05 01:06:20 +0000
commit57f37fbd6a4816f3a70041e04877beeb0a37b45c (patch)
treebd054c31e2a3b8705197f85705484bda4fe56698 /src/control.zig
parentadd freeze and step controls to Source (diff)
downloadglsl-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.zig202
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;