aboutsummaryrefslogtreecommitdiffstats
path: root/src
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
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')
-rw-r--r--src/control.zig202
-rw-r--r--src/ffmpeg.zig31
-rw-r--r--src/gl.zig42
-rw-r--r--src/source.zig72
-rw-r--r--src/tsv.zig15
-rw-r--r--src/util.zig21
6 files changed, 225 insertions, 158 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;
diff --git a/src/ffmpeg.zig b/src/ffmpeg.zig
index dda3f8c..700638f 100644
--- a/src/ffmpeg.zig
+++ b/src/ffmpeg.zig
@@ -1,6 +1,8 @@
const std = @import("std");
const c = @import("c.zig");
const gl = @import("gl.zig");
+const src = @import("source.zig");
+const ctrl = @import("control.zig");
const build_config = @import("build_config");
pub const Errors = error{
@@ -248,7 +250,7 @@ pub const AVDecoder = struct {
};
pub const VideoSource = struct {
- source: gl.Source,
+ source: src.Source,
decoder: *Decoder,
pub fn init(
@@ -258,7 +260,7 @@ pub const VideoSource = struct {
filename: [*:0]const u8,
format_name: ?[*:0]const u8,
format_options: []const [*c]c.lo_arg,
- ) !*gl.Source {
+ ) !*src.Source {
const self = try allocator.create(VideoSource);
errdefer allocator.destroy(self);
@@ -293,12 +295,13 @@ pub const VideoSource = struct {
else
try AVDecoder.init(allocator, codec_par),
};
+ errdefer self.decoder.deinit(allocator);
self.source.texture = try self.decoder.createTexture(progress, format, video_stream, texture_type);
return &self.source;
}
- fn deinit(source: *const gl.Source, allocator: std.mem.Allocator) void {
+ fn deinit(source: *const src.Source, allocator: std.mem.Allocator) void {
const self: *const VideoSource = @fieldParentPtr("source", source);
self.decoder.deinit(allocator);
@@ -307,7 +310,8 @@ pub const VideoSource = struct {
};
pub const StreamSource = struct {
- source: gl.Source,
+ source: src.Source,
+ flags: src.StreamFlags,
decoder: *Decoder,
format: *c.AVFormatContext,
@@ -322,7 +326,7 @@ pub const StreamSource = struct {
filename: [*:0]const u8,
format_name: ?[*:0]const u8,
format_options: []const [*c]c.lo_arg,
- ) !*gl.Source {
+ ) !*src.Source {
const self = try allocator.create(StreamSource);
errdefer allocator.destroy(self);
@@ -351,12 +355,16 @@ pub const StreamSource = struct {
);
const decoder = try AVDecoder.init(allocator, codec_par);
+ errdefer decoder.deinit(allocator);
self.* = .{
.source = .{
.texture = texture,
.deinit_fn = deinit,
+ .register_fn = register_methods,
+ .unregister_fn = unregister_methods,
},
+ .flags = .{},
.decoder = decoder,
.format = format,
@@ -372,6 +380,15 @@ pub const StreamSource = struct {
return &self.source;
}
+ fn register_methods(source: *src.Source, name: []const u8, control: *ctrl.ControlServer) void {
+ const self: *StreamSource = @fieldParentPtr("source", source);
+ self.flags.register(name, control);
+ }
+ fn unregister_methods(source: *src.Source, name: []const u8, control: *ctrl.ControlServer) void {
+ const self: *StreamSource = @fieldParentPtr("source", source);
+ self.flags.unregister(name, control);
+ }
+
fn update_loop(self: *StreamSource) !void {
while (!self.thread.quit) {
try self.update();
@@ -390,11 +407,11 @@ pub const StreamSource = struct {
_ = try self.decoder.process_packet_fn(
self.decoder,
self.packet,
- if (self.source.shouldStep()) &self.source.texture else null,
+ if (self.flags.shouldStep()) &self.source.texture else null,
);
}
- fn deinit(source: *gl.Source, allocator: std.mem.Allocator) void {
+ fn deinit(source: *src.Source, allocator: std.mem.Allocator) void {
const self: *StreamSource = @fieldParentPtr("source", source);
self.thread.deinit(allocator);
diff --git a/src/gl.zig b/src/gl.zig
index b578350..724340e 100644
--- a/src/gl.zig
+++ b/src/gl.zig
@@ -2,13 +2,7 @@ const std = @import("std");
const c_allocator = @import("std").heap.c_allocator;
const c = @import("c.zig");
const cfg = @import("config.zig");
-
-fn tmpZ(str: []const u8) ![:0]const u8 {
- const state = struct {
- var buf: [256]u8 = undefined;
- };
- return try std.fmt.bufPrintZ(state.buf[0..], "{s}", .{str});
-}
+const util = @import("util.zig");
var image_unit_map = [_]bool{false} ** 1024;
@@ -502,7 +496,7 @@ pub const CachedUniform = struct {
) !CachedUniform {
var index: c.GLuint = undefined;
- const nameZ = try tmpZ(name);
+ const nameZ = try util.tmpZ(name);
c.glGetUniformIndices(shader.program_id, 1, &nameZ.ptr, &index);
if (index == c.GL_INVALID_INDEX)
@@ -934,40 +928,15 @@ pub const Thread = struct {
}
};
-pub const Source = struct {
- pub const Type = enum {
- file,
- texture_share_vk,
- };
-
- texture: Texture,
- freeze: bool = false,
- step: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
-
- deinit_fn: *const fn (self: *Source, allocator: std.mem.Allocator) void,
-
- pub fn deinit(self: *Source, allocator: std.mem.Allocator) void {
- self.deinit_fn(self, allocator);
- }
-
- pub fn shouldStep(self: *Source) bool {
- const step = !self.freeze or self.step.load(.acquire);
- if (step) self.step.store(false, .release);
- return step;
- }
-};
-
pub const UniformCache = struct {
allocator: std.mem.Allocator,
uniforms: std.StringHashMap(CachedUniform),
- textures: std.StringHashMap(*Source),
shader: *ShaderProgram,
pub fn init(allocator: std.mem.Allocator, shader: *ShaderProgram) UniformCache {
return UniformCache{
.allocator = allocator,
.uniforms = std.StringHashMap(CachedUniform).init(allocator),
- .textures = std.StringHashMap(*Source).init(allocator),
.shader = shader,
};
}
@@ -979,14 +948,7 @@ pub const UniformCache = struct {
self.allocator.free(entry.key_ptr.*);
}
- var tit = self.textures.iterator();
- while (tit.next()) |entry| {
- entry.value_ptr.*.deinit(self.allocator);
- self.allocator.free(entry.key_ptr.*);
- }
-
self.uniforms.deinit();
- self.textures.deinit();
}
pub fn get(self: *UniformCache, name: []const u8) !?*CachedUniform {
diff --git a/src/source.zig b/src/source.zig
new file mode 100644
index 0000000..26a92a3
--- /dev/null
+++ b/src/source.zig
@@ -0,0 +1,72 @@
+const std = @import("std");
+const c = @import("c.zig");
+const gl = @import("gl.zig");
+const ctrl = @import("control.zig");
+const util = @import("util.zig");
+
+pub const Source = struct {
+ texture: gl.Texture,
+ deinit_fn: *const fn (self: *Source, allocator: std.mem.Allocator) void,
+ register_fn: ?*const fn (self: *Source, name: []const u8, control: *ctrl.ControlServer) void = null,
+ unregister_fn: ?*const fn (self: *Source, name: []const u8, control: *ctrl.ControlServer) void = null,
+
+ pub fn deinit(self: *Source, allocator: std.mem.Allocator) void {
+ self.deinit_fn(self, allocator);
+ }
+
+ pub fn register(self: *Source, name: []const u8, control: *ctrl.ControlServer) void {
+ if (self.register_fn) |register_fn| {
+ register_fn(self, name, control);
+ }
+ }
+
+ pub fn unregister(self: *Source, name: []const u8, control: *ctrl.ControlServer) void {
+ if (self.unregister_fn) |unregister_fn| {
+ unregister_fn(self, name, control);
+ }
+ }
+};
+
+pub const StreamFlags = struct {
+ freeze: bool = false,
+ step: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
+
+ pub fn register(self: *StreamFlags, name: []const u8, control: *ctrl.ControlServer) void {
+ control.add_method_for(StreamFlags, self, &.{ "", "texture", name, "freeze" }, "T", handle_freeze) catch unreachable;
+ control.add_method_for(StreamFlags, self, &.{ "", "texture", name, "freeze" }, "F", handle_freeze) catch unreachable;
+ control.add_method_for(StreamFlags, self, &.{ "", "texture", name, "step" }, "", handle_step) catch unreachable;
+ }
+
+ pub fn unregister(self: *const StreamFlags, name: []const u8, control: *ctrl.ControlServer) void {
+ _ = self;
+
+ control.del_method(&.{ "", "texture", name, "freeze" }, "T") catch unreachable;
+ control.del_method(&.{ "", "texture", name, "freeze" }, "F") catch unreachable;
+ control.del_method(&.{ "", "texture", name, "step" }, "") catch unreachable;
+ }
+
+ pub fn shouldStep(self: *StreamFlags) bool {
+ const step = !self.freeze or self.step.load(.acquire);
+ if (step) self.step.store(false, .release);
+ return step;
+ }
+
+ fn handle_freeze(self: *StreamFlags, path: []const u8, types: []const u8, argv: []const [*c]c.lo_arg) !bool {
+ _ = path;
+ _ = argv;
+
+ self.freeze = types[0] == 'T';
+
+ return true;
+ }
+
+ fn handle_step(self: *StreamFlags, path: []const u8, types: []const u8, argv: []const [*c]c.lo_arg) !bool {
+ _ = path;
+ _ = types;
+ _ = argv;
+
+ self.step.store(true, .monotonic);
+
+ return true;
+ }
+};
diff --git a/src/tsv.zig b/src/tsv.zig
index a723a52..7a121d7 100644
--- a/src/tsv.zig
+++ b/src/tsv.zig
@@ -1,7 +1,8 @@
const std = @import("std");
const c = @import("c.zig");
const gl = @import("gl.zig");
-const Source = gl.Source;
+const src = @import("source.zig");
+const ctrl = @import("control.zig");
const Output = @import("output.zig").Output;
var global_client: ?*c.GlClient = null;
@@ -28,7 +29,9 @@ fn get_or_init_client() *c.GlClient {
}
pub const TSVSource = struct {
- source: gl.Source,
+ source: src.Source,
+ stream: src.StreamFlags,
+
name: [:0]const u8,
thread: *gl.Thread,
@@ -37,7 +40,7 @@ pub const TSVSource = struct {
constants: *const gl.Constants,
texture_type: gl.Texture.Type,
name: [*:0]const u8,
- ) !*gl.Source {
+ ) !*src.Source {
const self = try allocator.create(TSVSource);
errdefer allocator.destroy(self);
@@ -46,6 +49,8 @@ pub const TSVSource = struct {
.texture = gl.Texture.create(texture_type),
.deinit_fn = deinit,
},
+ .stream = .{},
+
.name = try allocator.dupeZ(u8, std.mem.span(name)),
.thread = undefined,
};
@@ -68,7 +73,7 @@ pub const TSVSource = struct {
return &self.source;
}
- fn deinit(source: *const gl.Source, allocator: std.mem.Allocator) void {
+ fn deinit(source: *const src.Source, allocator: std.mem.Allocator) void {
const self: *const TSVSource = @fieldParentPtr("source", source);
self.thread.deinit(allocator);
@@ -85,7 +90,7 @@ pub const TSVSource = struct {
}
fn update(self: *TSVSource) void {
- if (!self.source.shouldStep()) return;
+ if (!self.stream.shouldStep()) return;
_ = c.gl_client_recv_image(
get_or_init_client(),
self.name.ptr,
diff --git a/src/util.zig b/src/util.zig
new file mode 100644
index 0000000..e168326
--- /dev/null
+++ b/src/util.zig
@@ -0,0 +1,21 @@
+const std = @import("std");
+
+const state = struct {
+ var buf: [256]u8 = undefined;
+};
+var fba = std.heap.FixedBufferAllocator.init(state.buf[0..]);
+
+pub fn tmpZ(str: []const u8) ![:0]const u8 {
+ fba.reset();
+ return try std.fmt.allocPrintZ(fba.allocator(), "{s}", .{str});
+}
+
+pub fn tmpFmtZ(comptime fmt: []const u8, args: anytype) ![:0]const u8 {
+ fba.reset();
+ return try std.fmt.allocPrintZ(fba.allocator(), fmt, args);
+}
+
+pub fn tmpJoinZ(separator: []const u8, slices: []const []const u8) ![:0]const u8 {
+ fba.reset();
+ return try std.mem.joinZ(fba.allocator(), separator, slices);
+}