diff options
| -rw-r--r-- | build.zig | 1 | ||||
| -rw-r--r-- | src/c.zig | 1 | ||||
| -rw-r--r-- | src/config.zig | 309 | ||||
| -rw-r--r-- | src/control.zig | 206 | ||||
| -rw-r--r-- | src/gl.zig | 165 | ||||
| -rw-r--r-- | src/main.zig | 130 | ||||
| -rw-r--r-- | src/output.zig | 73 |
7 files changed, 283 insertions, 602 deletions
@@ -22,7 +22,6 @@ pub fn build(b: *std.Build) void { exe.root_module.addOptions("build_config", options); exe.linkLibC(); - exe.linkSystemLibrary("yaml-0.1"); exe.linkSystemLibrary("glfw3"); exe.linkSystemLibrary("epoxy"); exe.linkSystemLibrary("liblo"); @@ -3,7 +3,6 @@ const build_config = @import("build_config"); pub usingnamespace @cImport({ @cInclude("epoxy/gl.h"); @cInclude("GLFW/glfw3.h"); - @cInclude("yaml.h"); @cInclude("lo/lo.h"); if (build_config.have_ffmpeg) { diff --git a/src/config.zig b/src/config.zig index 97985c9..529b3bd 100644 --- a/src/config.zig +++ b/src/config.zig @@ -1,305 +1,58 @@ -const c = @import("c.zig"); -const fs = @import("std").fs; const std = @import("std"); const debug = @import("std").debug; -const yaml = @import("yaml.zig"); const out = @import("output.zig"); pub const OutputConfig = union(enum) { window: out.WindowOutput.Config, texture_share_vk: out.TSVOutput.Config, -}; -pub const ParameterConfig = struct { - name: []const u8, - type: []const u8, + const default: OutputConfig = .{ .window = .default }; }; -pub const OSCConfig = union(enum) { - const Protocol = enum { - udp, - tcp, - unix, - }; - - Manual: struct { - protocol: Protocol = Protocol.udp, - port: u16 = 9000, - }, - URL: []const u8, -}; +fn parseInt(it: *std.process.ArgIterator, comptime T: type) !T { + const value = it.next() orelse return error.missingArgument; + return try std.fmt.parseInt(T, value, 10); +} -const defaultOutput: OutputConfig = .{ .window = .default }; +fn parseString(it: *std.process.ArgIterator, allocator: std.mem.Allocator) ![]u8 { + const value = it.next() orelse return error.missingArgument; + return allocator.dupe(u8, value); +} pub const Config = struct { width: i32, height: i32, outputs: []const OutputConfig, - fragment: []const u8, - parameters: []const ParameterConfig, - project_root: fs.Dir, - osc: OSCConfig, + osc: []const u8, - pub fn parse(allocator: std.mem.Allocator, filename: []const u8) !Config { - var parser: c.yaml_parser_t = undefined; - _ = c.yaml_parser_initialize(&parser); - defer c.yaml_parser_delete(&parser); - - const file = try fs.cwd().openFile(filename, .{}); - var buffer: [1024]u8 = undefined; - const len: usize = try file.read(buffer[0..]); - c.yaml_parser_set_input_string(&parser, buffer[0..], len); - - const dirname = fs.path.dirname(filename) orelse "."; - debug.print("file/dirname is {s} / {s}\n", .{ filename, dirname }); - var config: Config = .{ - .width = 1920, - .height = 1080, - .outputs = ([_]OutputConfig{defaultOutput})[0..], - .fragment = "", - .parameters = ([0]ParameterConfig{})[0..], - .project_root = try fs.cwd().openDir(dirname, .{}), - .osc = .{ .URL = "osc.udp://:9000" }, - }; + pub const default: Config = .{ + .width = 1920, + .height = 1080, + .outputs = ([_]OutputConfig{.default})[0..], + .osc = "osc.udp://:9000", + }; - try yaml.expectEvent(&parser, c.YAML_STREAM_START_EVENT); - try yaml.expectEvent(&parser, c.YAML_DOCUMENT_START_EVENT); - try yaml.expectEvent(&parser, c.YAML_MAPPING_START_EVENT); + pub fn init(allocator: std.mem.Allocator) !Config { + var config: Config = .default; - while (true) { - var event: c.yaml_event_t = undefined; - if (c.yaml_parser_parse(&parser, &event) != 1) - return error.YAMLParserError; - defer c.yaml_event_delete(&event); + var it = try std.process.argsWithAllocator(allocator); + defer it.deinit(); - switch (event.type) { - c.YAML_MAPPING_END_EVENT => break, - c.YAML_SCALAR_EVENT => { - const data = event.data.scalar; - const key: []const u8 = data.value[0..data.length]; + _ = it.skip(); - if (std.mem.eql(u8, key, "width")) { - config.width = try yaml.parseInt(&parser, i32); - } else if (std.mem.eql(u8, key, "height")) { - config.height = try yaml.parseInt(&parser, i32); - } else if (std.mem.eql(u8, key, "fragment")) { - config.fragment = try yaml.parseString(&parser, allocator); - } else if (std.mem.eql(u8, key, "outputs")) { - config.outputs = try parseOutputs(&parser, allocator); - } else if (std.mem.eql(u8, key, "parameters")) { - config.parameters = try parseParameters(&parser, allocator); - } else if (std.mem.eql(u8, key, "project_root")) { - config.project_root = try fs.cwd().openDir(try yaml.parseString(&parser, allocator), .{}); - } else if (std.mem.eql(u8, key, "osc")) { - config.osc = try parseOSC(&parser, allocator); - } else { - debug.print("unknown key: '{s}'\n", .{key}); - try yaml.skipAny(&parser); - } - }, - else => { - debug.print("unexpected event: {}\n", .{event.type}); - return error.InvalidConfiguration; - }, + while (it.next()) |arg| { + if (std.mem.eql(u8, arg, "--width")) { + config.width = try parseInt(&it, i32); + } else if (std.mem.eql(u8, arg, "--height")) { + config.height = try parseInt(&it, i32); + } else if (std.mem.eql(u8, arg, "--osc")) { + config.osc = try parseString(&it, allocator); + } else { + return error.invalidArgument; } + // @TODO: output config } - try yaml.expectEvent(&parser, c.YAML_DOCUMENT_END_EVENT); - try yaml.expectEvent(&parser, c.YAML_STREAM_END_EVENT); - return config; } }; - -fn parseOutputs(parser: *c.yaml_parser_t, allocator: std.mem.Allocator) ![]OutputConfig { - try yaml.expectEvent(parser, c.YAML_SEQUENCE_START_EVENT); - - var outputs = try allocator.alloc(OutputConfig, 1024); - var output_count: usize = 0; - - while (true) { - outputs[output_count] = yaml.parseUnion(parser, OutputConfig, allocator) catch |err| { - if (err == error.InvalidType) break; - return err; - }; - output_count += 1; - - // var seqEvent: c.yaml_event_t = undefined; - // if (c.yaml_parser_parse(parser, &seqEvent) != 1) - // return error.YAMLParserError; - // defer c.yaml_event_delete(&seqEvent); - - // switch (seqEvent.type) { - // c.YAML_SEQUENCE_END_EVENT => break, - // c.YAML_MAPPING_START_EVENT => { - // const output = &outputs[output_count]; - // output.* = defaultOutput; - // - // while (true) { - // var event: c.yaml_event_t = undefined; - // if (c.yaml_parser_parse(parser, &event) != 1) - // return error.YAMLParserError; - // defer c.yaml_event_delete(&event); - // - // switch (event.type) { - // c.YAML_MAPPING_END_EVENT => break, - // c.YAML_SCALAR_EVENT => { - // const data = event.data.scalar; - // const key: []const u8 = data.value[0..data.length]; - // - // if (!std.mem.eql(u8, key, "type")) { - // const ouput_type = try yaml.parseEnum(parser, OutputConfig.Type); - // output.* = switch (ouput_type) { - // inline else => |t| { - // - // } - // } - // } - // - // if (std.mem.eql(u8, key, "width")) { - // output.*.width = try yaml.parseInt(parser, i32); - // } else if (std.mem.eql(u8, key, "height")) { - // output.*.height = try yaml.parseInt(parser, i32); - // } else if (std.mem.eql(u8, key, "filter")) { - // output.*.filter = try yaml.parseEnum(parser, OutputConfig.FilterMode); - // } else if (std.mem.eql(u8, key, "monitor")) { - // output.*.monitor = try yaml.parseString(parser, allocator); - // } else if (std.mem.eql(u8, key, "fullscreen")) { - // output.*.fullscreen = try yaml.parseBool(parser); - // } else { - // debug.print("unknown key: '{s}'\n", .{key}); - // try yaml.skipAny(parser); - // } - // }, - // else => { - // debug.print("unexpected event: {}\n", .{event.type}); - // return error.InvalidConfiguration; - // }, - // } - // } - // - // output_count += 1; - // }, - // else => { - // debug.print("unexpected event: {}\n", .{seqEvent.type}); - // return error.InvalidConfiguration; - // }, - // } - } - - _ = allocator.realloc(outputs, output_count) catch 0; - return outputs[0..output_count]; -} - -fn parseParameters(parser: *c.yaml_parser_t, allocator: std.mem.Allocator) ![]ParameterConfig { - try yaml.expectEvent(parser, c.YAML_SEQUENCE_START_EVENT); - - var parameters = try allocator.alloc(ParameterConfig, 1024); - var param_count: usize = 0; - - while (true) { - var seqEvent: c.yaml_event_t = undefined; - if (c.yaml_parser_parse(parser, &seqEvent) != 1) - return error.YAMLParserError; - defer c.yaml_event_delete(&seqEvent); - - switch (seqEvent.type) { - c.YAML_SEQUENCE_END_EVENT => break, - c.YAML_MAPPING_START_EVENT => { - const param = ¶meters[param_count]; - var have_name = false; - var have_type = false; - - while (true) { - var event: c.yaml_event_t = undefined; - if (c.yaml_parser_parse(parser, &event) != 1) - return error.YAMLParserError; - defer c.yaml_event_delete(&event); - - switch (event.type) { - c.YAML_MAPPING_END_EVENT => break, - c.YAML_SCALAR_EVENT => { - const data = event.data.scalar; - const key: []const u8 = data.value[0..data.length]; - - if (std.mem.eql(u8, key, "name")) { - param.*.name = try yaml.parseString(parser, allocator); - have_name = true; - } else if (std.mem.eql(u8, key, "type")) { - param.*.type = try yaml.parseString(parser, allocator); - have_type = true; - } else { - debug.print("unknown key: '{s}'\n", .{key}); - try yaml.skipAny(parser); - } - }, - else => { - debug.print("unexpected event: {}\n", .{event.type}); - return error.InvalidConfiguration; - }, - } - } - - if (!(have_name and have_type)) { - debug.print("name and type are mandatory for parameters.\n", .{}); - return error.InvalidConfiguration; - } - - param_count += 1; - }, - else => { - debug.print("unexpected event: {}\n", .{seqEvent.type}); - return error.InvalidConfiguration; - }, - } - } - - _ = allocator.realloc(parameters, param_count) catch 0; - return parameters[0..param_count]; -} - -fn parseOSC(parser: *c.yaml_parser_t, allocator: std.mem.Allocator) !OSCConfig { - var event: c.yaml_event_t = undefined; - if (c.yaml_parser_parse(parser, &event) != 1) - return error.YAMLParserError; - defer c.yaml_event_delete(&event); - - if (event.type == c.YAML_SCALAR_EVENT) { - const url = try yaml.parseStringEvent(&event, allocator); - return OSCConfig{ .URL = url }; - } else if (event.type != c.YAML_MAPPING_START_EVENT) { - debug.print("unexpected event: {}\n", .{event.type}); - return error.InvalidConfiguration; - } - - var config: OSCConfig = .{ .Manual = .{} }; - - while (true) { - if (c.yaml_parser_parse(parser, &event) != 1) - return error.YAMLParserError; - defer c.yaml_event_delete(&event); - - switch (event.type) { - c.YAML_MAPPING_END_EVENT => break, - c.YAML_SCALAR_EVENT => { - const data = event.data.scalar; - const key: []const u8 = data.value[0..data.length]; - - if (std.mem.eql(u8, key, "protocol")) { - config.Manual.protocol = try yaml.parseEnum(parser, OSCConfig.Protocol); - } else if (std.mem.eql(u8, key, "port")) { - config.Manual.port = try yaml.parseInt(parser, u16); - } else { - debug.print("unknown key: '{s}'\n", .{key}); - try yaml.skipAny(parser); - } - }, - else => { - debug.print("unexpected event: {}\n", .{event.type}); - return error.InvalidConfiguration; - }, - } - } - - return config; -} diff --git a/src/control.zig b/src/control.zig index 0ea6712..72abf6f 100644 --- a/src/control.zig +++ b/src/control.zig @@ -16,11 +16,10 @@ fn verify_args(expected: u8, got: []const u8) !void { fn set_one( comptime T: type, dest: *T, - argc: c_int, - argv: [*c][*c]c.lo_arg, + argv: [][*c]c.lo_arg, types: []const u8, ) !void { - if (argc != 1) + if (types.len != 1) return error.sizeMismatch; switch (T) { @@ -50,11 +49,10 @@ fn set_one( fn set_array( comptime T: type, dest: []T, - argc: c_int, - argv: [*c][*c]c.lo_arg, + argv: [][*c]c.lo_arg, types: []const u8, ) !void { - if (argc != dest.len) + if (types.len != dest.len) return error.sizeMismatch; switch (T) { @@ -88,14 +86,12 @@ fn set_array( fn set_texture( progress: std.Progress.Node, - project_root: std.fs.Dir, dest: *?gl.Texture, texture_type: gl.Texture.Type, - argc: c_int, - argv: [*c][*c]c.lo_arg, + argv: [][*c]c.lo_arg, types: []const u8, ) !void { - if (argc != 1 or types[0] != 's') return error.invalidType; + if (types.len != 1 or types[0] != 's') return error.invalidType; if (dest.*) |old| { old.destroy(); @@ -103,7 +99,7 @@ fn set_texture( } var buffer: [1024]u8 = undefined; - const filepath = try project_root.realpathZ(@ptrCast(&argv[0].*.s), buffer[0..]); + const filepath = try std.fs.cwd().realpathZ(@ptrCast(&argv[0].*.s), buffer[0..]); buffer[filepath.len] = 0; dest.* = try video.loadVideo(progress, @ptrCast(filepath), texture_type); @@ -111,7 +107,6 @@ fn set_texture( pub const ControlServer = struct { server: c.lo_server, - reload_requested: bool, cache: *gl.UniformCache, config: *cfg.Config, @@ -127,7 +122,6 @@ pub const ControlServer = struct { var self: *ControlServer = try allocator.create(ControlServer); self.* = .{ .server = null, - .reload_requested = false, .cache = cache, .config = config, @@ -135,31 +129,15 @@ pub const ControlServer = struct { .allocator = allocator, }; - switch (config.osc) { - .Manual => |conf| { - var port = [_]u8{0} ** 6; - _ = std.fmt.formatIntBuf(port[0..], conf.port, 10, .lower, std.fmt.FormatOptions{}); - const proto: c_int = switch (conf.protocol) { - .udp => c.LO_UDP, - .tcp => c.LO_TCP, - .unix => c.LO_UNIX, - }; - - std.debug.print( - "listening for OSC messages on {} port {}\n", - .{ conf.protocol, conf.port }, - ); - self.server = c.lo_server_new_with_proto(port[0..], proto, handle_error); - }, - .URL => |url| { - std.debug.print("listening for OSC messages at {s}\n", .{url}); - self.server = c.lo_server_new_from_url(url[0..].ptr, handle_error); - }, - } + std.debug.print("listening for OSC messages at {s}\n", .{config.osc}); + self.server = c.lo_server_new_from_url(config.osc.ptr, handle_error); if (self.server == null) return error.serverInitializationError; - _ = c.lo_server_add_method(self.server, null, null, handle_method, @as(*anyopaque, @ptrCast(self))); + + _ = c.lo_server_add_method(self.server, "/-/shader", "s", 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, "/*", null, handle_uniform, @as(*anyopaque, @ptrCast(self))); return self; } @@ -182,8 +160,7 @@ pub const ControlServer = struct { fn set_uniform( self: *ControlServer, path: []const u8, - argv: [*c][*c]c.lo_arg, - argc: c_int, + argv: [][*c]c.lo_arg, types: []const u8, ) !void { var parts = std.mem.tokenizeScalar(u8, path, '/'); @@ -217,61 +194,61 @@ pub const ControlServer = struct { } try switch (value) { - .FLOAT => |val| set_one(f32, val, argc, argv, types), - .DOUBLE => |val| set_one(f64, val, argc, argv, types), - .INT => |val| set_one(i32, val, argc, argv, types), - .UNSIGNED_INT => |val| set_one(u32, val, argc, argv, types), - .BOOL => |val| set_one(u32, val, argc, argv, types), - .FLOAT_VEC2 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_VEC3 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_VEC4 => |val| set_array(f32, val.*[0..], argc, argv, types), - .DOUBLE_VEC2 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_VEC3 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_VEC4 => |val| set_array(f64, val.*[0..], argc, argv, types), - .INT_VEC2 => |val| set_array(i32, val.*[0..], argc, argv, types), - .INT_VEC3 => |val| set_array(i32, val.*[0..], argc, argv, types), - .INT_VEC4 => |val| set_array(i32, val.*[0..], argc, argv, types), - .UNSIGNED_INT_VEC2 => |val| set_array(u32, val.*[0..], argc, argv, types), - .UNSIGNED_INT_VEC3 => |val| set_array(u32, val.*[0..], argc, argv, types), - .UNSIGNED_INT_VEC4 => |val| set_array(u32, val.*[0..], argc, argv, types), - .BOOL_VEC2 => |val| set_array(u32, val.*[0..], argc, argv, types), - .BOOL_VEC3 => |val| set_array(u32, val.*[0..], argc, argv, types), - .BOOL_VEC4 => |val| set_array(u32, val.*[0..], argc, argv, types), - .FLOAT_MAT2 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_MAT3 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_MAT4 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_MAT2x3 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_MAT2x4 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_MAT3x2 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_MAT3x4 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_MAT4x2 => |val| set_array(f32, val.*[0..], argc, argv, types), - .FLOAT_MAT4x3 => |val| set_array(f32, val.*[0..], argc, argv, types), - .DOUBLE_MAT2 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_MAT3 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_MAT4 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_MAT2x3 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_MAT2x4 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_MAT3x2 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_MAT3x4 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_MAT4x2 => |val| set_array(f64, val.*[0..], argc, argv, types), - .DOUBLE_MAT4x3 => |val| set_array(f64, val.*[0..], argc, argv, types), + .FLOAT => |val| set_one(f32, val, argv, types), + .DOUBLE => |val| set_one(f64, val, argv, types), + .INT => |val| set_one(i32, val, argv, types), + .UNSIGNED_INT => |val| set_one(u32, val, argv, types), + .BOOL => |val| set_one(u32, val, argv, types), + .FLOAT_VEC2 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_VEC3 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_VEC4 => |val| set_array(f32, val.*[0..], argv, types), + .DOUBLE_VEC2 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_VEC3 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_VEC4 => |val| set_array(f64, val.*[0..], argv, types), + .INT_VEC2 => |val| set_array(i32, val.*[0..], argv, types), + .INT_VEC3 => |val| set_array(i32, val.*[0..], argv, types), + .INT_VEC4 => |val| set_array(i32, val.*[0..], argv, types), + .UNSIGNED_INT_VEC2 => |val| set_array(u32, val.*[0..], argv, types), + .UNSIGNED_INT_VEC3 => |val| set_array(u32, val.*[0..], argv, types), + .UNSIGNED_INT_VEC4 => |val| set_array(u32, val.*[0..], argv, types), + .BOOL_VEC2 => |val| set_array(u32, val.*[0..], argv, types), + .BOOL_VEC3 => |val| set_array(u32, val.*[0..], argv, types), + .BOOL_VEC4 => |val| set_array(u32, val.*[0..], argv, types), + .FLOAT_MAT2 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_MAT3 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_MAT4 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_MAT2x3 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_MAT2x4 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_MAT3x2 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_MAT3x4 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_MAT4x2 => |val| set_array(f32, val.*[0..], argv, types), + .FLOAT_MAT4x3 => |val| set_array(f32, val.*[0..], argv, types), + .DOUBLE_MAT2 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_MAT3 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_MAT4 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_MAT2x3 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_MAT2x4 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_MAT3x2 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_MAT3x4 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_MAT4x2 => |val| set_array(f64, val.*[0..], argv, types), + .DOUBLE_MAT4x3 => |val| set_array(f64, val.*[0..], argv, types), .SAMPLER_2D, .SAMPLER_2D_SHADOW, .INT_SAMPLER_2D, .UNSIGNED_INT_SAMPLER_2D, - => |val| set_texture(self.progress, self.config.project_root, val, .TEXTURE_2D, argc, argv, types), + => |val| set_texture(self.progress, val, .TEXTURE_2D, argv, types), .SAMPLER_2D_ARRAY, .SAMPLER_2D_ARRAY_SHADOW, .INT_SAMPLER_2D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_ARRAY, - => |val| set_texture(self.progress, self.config.project_root, val, .TEXTURE_2D_ARRAY, argc, argv, types), + => |val| set_texture(self.progress, val, .TEXTURE_2D_ARRAY, argv, types), .SAMPLER_3D, .INT_SAMPLER_3D, .UNSIGNED_INT_SAMPLER_3D, - => |val| set_texture(self.progress, self.config.project_root, val, .TEXTURE_3D, argc, argv, types), + => |val| set_texture(self.progress, val, .TEXTURE_3D, argv, types), else => error.uniformNotSupported, }; @@ -279,29 +256,80 @@ pub const ControlServer = struct { uniform.setShaderValue(self.cache.shader.*); } - fn handle_method( + fn handle_shader( + path: [*c]const u8, + types: [*c]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 code: [*:0]const u8 = @ptrCast(&argv[0].*.s); + + _ = path; + _ = types; + _ = argc; + _ = msg; + + _ = e: { + self.cache.shader.loadString(std.mem.span(code)) catch |err| break :e err; + self.cache.refresh() catch |err| break :e err; + } catch |err| { + std.debug.print("{} while loading shader from OSC\n", .{err}); + }; + + return 0; + } + + fn handle_reload( + path: [*c]const u8, + types: [*c]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.?)); + + _ = path; + _ = types; + _ = argc; + _ = argv; + _ = msg; + + _ = e: { + self.cache.shader.loadFile(self.config.fragment, 2048) catch |err| break :e err; + self.cache.refresh() catch |err| break :e err; + } catch |err| { + std.debug.print("{} while reloading shader from file\n", .{err}); + }; + + return 0; + } + + fn handle_uniform( _path: [*c]const u8, _types: [*c]const u8, - argv: [*c][*c]c.lo_arg, + _argv: [*c][*c]c.lo_arg, argc: c_int, msg: c.lo_message, userdata: ?*anyopaque, ) callconv(.C) c_int { _ = msg; - const self = @as(*ControlServer, @ptrCast(@alignCast(userdata.?))); + const self: *ControlServer = @ptrCast(@alignCast(userdata.?)); const path = _path[0..c.strlen(_path)]; - const types = _types[0..@as(u32, @intCast(argc))]; + const argv: [][*c]c.lo_arg = _argv[0..@intCast(argc)]; + const types = _types[0..@intCast(argc)]; - if (std.mem.eql(u8, path, "/-/reload")) { - self.reload_requested = true; - return 1; - } - - self.set_uniform(path, argv, argc, types) catch |err| { - std.debug.print("{} while processing {s}\n", .{ err, path }); + self.set_uniform(path, argv, types) catch |err| { + if (err != error.notFound) { + std.debug.print("{} while processing {s}\n", .{ err, path }); + } return 1; }; + return 0; } @@ -714,27 +714,34 @@ pub const ShaderProgram = struct { return id; } - pub fn create(vert_source: []const u8, frag_source: []const u8) !ShaderProgram { - var self: ShaderProgram = undefined; + fn compileShader(shader_id: c.GLuint, source: []const u8, name: []const u8) !void { + const source_ptr: ?[*]const u8 = source.ptr; + const source_len = @as(c.GLint, @intCast(source.len)); + c.glShaderSource(shader_id, 1, &source_ptr, &source_len); + c.glCompileShader(shader_id); - self.vert_id = try initGlShader(vert_source, "vertex", c.GL_VERTEX_SHADER); - errdefer c.glDeleteShader(self.vert_id); + var ok: c.GLint = undefined; + c.glGetShaderiv(shader_id, c.GL_COMPILE_STATUS, &ok); + if (ok != 0) return; - self.frag_id = try initGlShader(frag_source, "fragment", c.GL_FRAGMENT_SHADER); - errdefer c.glDeleteShader(self.frag_id); + var error_size: c.GLint = undefined; + c.glGetShaderiv(shader_id, c.GL_INFO_LOG_LENGTH, &error_size); - self.program_id = c.glCreateProgram(); - c.glAttachShader(self.program_id, self.vert_id); - c.glAttachShader(self.program_id, self.frag_id); - c.glLinkProgram(self.program_id); + const message = try c_allocator.alloc(u8, @as(usize, @intCast(error_size))); + c.glGetShaderInfoLog(shader_id, error_size, &error_size, message.ptr); + std.debug.print( + "Error compiling {s} shader:\n{s}\n", + .{ name, @as([*:0]const u8, @ptrCast(message.ptr)) }, + ); + return error.CompileError; + } - errdefer c.glDeleteProgram(self.program_id); - errdefer c.glDetachShader(self.program_id, self.frag_id); - errdefer c.glDetachShader(self.program_id, self.vert_id); + fn linkProgram(self: ShaderProgram) !void { + c.glLinkProgram(self.program_id); var ok: c.GLint = undefined; c.glGetProgramiv(self.program_id, c.GL_LINK_STATUS, &ok); - if (ok != 0) return self; + if (ok != 0) return; var error_size: c.GLint = undefined; c.glGetProgramiv(self.program_id, c.GL_INFO_LOG_LENGTH, &error_size); @@ -744,15 +751,113 @@ pub const ShaderProgram = struct { return error.LinkError; } - pub fn destroy(self: *ShaderProgram) void { - c.glDetachShader(self.program_id, self.frag_id); - c.glDetachShader(self.program_id, self.vert_id); + pub fn create(vert_source: []const u8, frag_source: []const u8) !ShaderProgram { + const self: ShaderProgram = .{ + .program_id = c.glCreateProgram(), + .vert_id = c.glCreateShader(c.GL_VERTEX_SHADER), + .frag_id = c.glCreateShader(c.GL_FRAGMENT_SHADER), + }; + errdefer self.destroy(); + c.glAttachShader(self.program_id, self.vert_id); + c.glAttachShader(self.program_id, self.frag_id); + + try compileShader(self.vert_id, vert_source, "vertex"); + try compileShader(self.frag_id, frag_source, "fragment"); + try self.linkProgram(); + return self; + } + + pub fn destroy(self: *const ShaderProgram) void { c.glDeleteShader(self.frag_id); c.glDeleteShader(self.vert_id); - c.glDeleteProgram(self.program_id); } + + pub fn loadString(self: *ShaderProgram, code: []const u8) !void { + try compileShader(self.frag_id, code, "fragment"); + try self.linkProgram(); + } + + fn escape_dquote(str: []const u8) []const u8 { + const state = struct { + var buf: [1024]u8 = undefined; + }; + + const len = std.mem.replacementSize(u8, str, "\"", "\\\""); + _ = std.mem.replace(u8, str, "\"", "\\\"", state.buf[0..len]); + return state.buf[0..len]; + } + + // given a file and the directory it is in, resolve references + fn readFile(writer: anytype, dir: []const u8, filename: []const u8) !void { + const file_dir = try std.fs.path.resolve(c_allocator, &[_][]const u8{ + dir, + std.fs.path.dirname(filename) orelse "", + }); + defer c_allocator.free(file_dir); + + const file_path = try std.fs.path.resolve(c_allocator, &[_][]const u8{ dir, filename }); + defer c_allocator.free(file_path); + + var file = try std.fs.cwd().openFile(file_path, .{}); + defer file.close(); + var reader = file.reader(); + + var line_number: u32 = 1; + var buf: [1024]u8 = undefined; + var wrote_line = false; + while (try reader.readUntilDelimiterOrEof(&buf, '\n')) |line| { + if (std.mem.startsWith(u8, line, "#pragma ")) { + var parts = std.mem.splitScalar(u8, line, ' '); + _ = parts.next(); + const pragma = parts.next().?; + + if (std.mem.eql(u8, pragma, "include")) { + var include_path = parts.next().?; + if (include_path[0] != '"' or include_path[include_path.len - 1] != '"') { + std.debug.print("{s}:{}: Invalid #pragma include\n", .{ file_path, line_number }); + std.debug.print("{s}:{}: {s}\n", .{ file_path, line_number, line }); + continue; + } + include_path = include_path[1 .. include_path.len - 1]; + + readFile(writer, file_dir, include_path) catch unreachable; + _ = try writer.print("#line {d} \"{s}\"\n", .{ line_number, escape_dquote(filename) }); + + continue; + } else { + std.debug.print("{s}:{}: Unknown #pragma directive '{s}'\n", .{ file_path, line_number, pragma }); + std.debug.print("{s}:{}: {s}\n", .{ file_path, line_number, line }); + } + } + + if (!std.mem.startsWith(u8, line, "#version") and !wrote_line) { + _ = try writer.print("#line {d} \"{s}\"\n", .{ line_number - 1, escape_dquote(filename) }); + wrote_line = true; + } + + _ = try writer.write(line); + _ = try writer.write("\n"); + + line_number += 1; + } + } + + pub fn loadFile(self: *ShaderProgram, filename: []const u8, size: u64) !void { + var buffer = try std.ArrayList(u8).initCapacity(c_allocator, @as(usize, @intCast(size)) + 150); + defer buffer.deinit(); + + const writer = buffer.writer(); + _ = try writer.write( + \\#version 330 core + \\#extension GL_ARB_shading_language_include : require + \\ + ); + try readFile(writer, "", filename); + + try self.loadString(buffer.items); + } }; pub const UniformCache = struct { @@ -821,30 +926,6 @@ pub const UniformCache = struct { } }; -fn initGlShader(source: []const u8, name: []const u8, kind: c.GLenum) !c.GLuint { - const shader_id = c.glCreateShader(kind); - errdefer c.glDeleteShader(shader_id); - const source_ptr: ?[*]const u8 = source.ptr; - const source_len = @as(c.GLint, @intCast(source.len)); - c.glShaderSource(shader_id, 1, &source_ptr, &source_len); - c.glCompileShader(shader_id); - - var ok: c.GLint = undefined; - c.glGetShaderiv(shader_id, c.GL_COMPILE_STATUS, &ok); - if (ok != 0) return shader_id; - - var error_size: c.GLint = undefined; - c.glGetShaderiv(shader_id, c.GL_INFO_LOG_LENGTH, &error_size); - - const message = try c_allocator.alloc(u8, @as(usize, @intCast(error_size))); - c.glGetShaderInfoLog(shader_id, error_size, &error_size, message.ptr); - std.debug.print( - "Error compiling {s} shader:\n{s}\n", - .{ name, @as([*:0]const u8, @ptrCast(message.ptr)) }, - ); - return error.CompileError; -} - pub const FramebufferObject = struct { buffer_id: c.GLuint, texture_id: c.GLuint, diff --git a/src/main.zig b/src/main.zig index 932fdca..11052e5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,8 +1,6 @@ const std = @import("std"); -const fs = std.fs; const debug = std.debug; const panic = debug.panic; -const process = std.process; const c = @import("c.zig"); const debug_gl = @import("debug_gl.zig"); const cfg = @import("config.zig"); @@ -24,11 +22,7 @@ pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); - var args = process.args(); - _ = args.next(); - - const filename = args.next() orelse "config.yaml"; - var config = try cfg.Config.parse(arena.allocator(), filename); + var config = try cfg.Config.init(arena.allocator()); _ = c.glfwSetErrorCallback(errorCallback); @@ -111,9 +105,9 @@ pub fn main() !void { var fbo = try gl.FramebufferObject.create(config.width, config.height); defer fbo.destroy(); - const shader_file = try config.project_root.openFile(config.fragment, .{}); - var last_stat = try shader_file.stat(); - try reloadShader(&config, &main_program, config.fragment, last_stat.size); + // const shader_file = try std.fs.cwd().openFile(config.fragment, .{}); + // var last_stat = try shader_file.stat(); + // try main_program.loadFile(config.fragment, last_stat.size); var cache = gl.UniformCache.init(std.heap.c_allocator, &main_program); defer cache.deinit(); @@ -130,14 +124,14 @@ pub fn main() !void { control.update(); - const stat = try shader_file.stat(); - if (stat.mtime > last_stat.mtime or control.reload_requested) { - try reloadShader(&config, &main_program, config.fragment, stat.size); - try cache.refresh(); - last_stat = stat; - - control.reload_requested = false; - } + // const stat = try shader_file.stat(); + // if (stat.mtime > last_stat.mtime or control.reload_requested) { + // try main_program.loadFile(config.fragment, stat.size); + // try cache.refresh(); + // last_stat = stat; + // + // control.reload_requested = false; + // } fbo.bind(); c.glClear(c.GL_COLOR_BUFFER_BIT); @@ -161,103 +155,3 @@ pub fn main() !void { c.glfwPollEvents(); } } - -fn escape_dquote(str: []const u8) []const u8 { - const state = struct { - var buf: [1024]u8 = undefined; - }; - - const len = std.mem.replacementSize(u8, str, "\"", "\\\""); - _ = std.mem.replace(u8, str, "\"", "\\\"", state.buf[0..len]); - return state.buf[0..len]; -} - -// given a file and the directory it is in, resolve references -fn loadFile(config: *const cfg.Config, writer: anytype, dir: []const u8, filename: []const u8) !void { - const file_dir = try fs.path.resolve(c_allocator, &[_][]const u8{ - dir, - fs.path.dirname(filename) orelse "", - }); - defer c_allocator.free(file_dir); - - const file_path = try fs.path.resolve(c_allocator, &[_][]const u8{ dir, filename }); - defer c_allocator.free(file_path); - - var file = try config.project_root.openFile(file_path, .{}); - defer file.close(); - var reader = file.reader(); - - var line_number: u32 = 1; - var buf: [1024]u8 = undefined; - var wrote_line = false; - while (try reader.readUntilDelimiterOrEof(&buf, '\n')) |line| { - if (std.mem.startsWith(u8, line, "#pragma ")) { - var parts = std.mem.splitScalar(u8, line, ' '); - _ = parts.next(); - const pragma = parts.next().?; - - if (std.mem.eql(u8, pragma, "include")) { - var include_path = parts.next().?; - if (include_path[0] != '"' or include_path[include_path.len - 1] != '"') { - debug.print("{s}:{}: Invalid #pragma include\n", .{ file_path, line_number }); - debug.print("{s}:{}: {s}\n", .{ file_path, line_number, line }); - continue; - } - include_path = include_path[1 .. include_path.len - 1]; - - loadFile(config, writer, file_dir, include_path) catch unreachable; - _ = try writer.print("#line {d} \"{s}\"\n", .{ line_number, escape_dquote(filename) }); - - continue; - } else { - debug.print("{s}:{}: Unknown #pragma directive '{s}'\n", .{ file_path, line_number, pragma }); - debug.print("{s}:{}: {s}\n", .{ file_path, line_number, line }); - } - } - - if (!std.mem.startsWith(u8, line, "#version") and !wrote_line) { - _ = try writer.print("#line {d} \"{s}\"\n", .{ line_number - 1, escape_dquote(filename) }); - wrote_line = true; - } - - _ = try writer.write(line); - _ = try writer.write("\n"); - - line_number += 1; - } -} - -fn reloadShader(config: *const cfg.Config, current: *gl.ShaderProgram, frag_filename: []const u8, size: u64) !void { - var buffer = try std.ArrayList(u8).initCapacity(c_allocator, @as(usize, @intCast(size))); - errdefer buffer.deinit(); - - const writer = buffer.writer(); - _ = try writer.write( - \\#version 330 core - \\#extension GL_ARB_shading_language_include : require - \\ - ); - try loadFile(config, writer, "", frag_filename); - - const frag_source = try buffer.toOwnedSlice(); - defer c_allocator.free(frag_source); - - const vert_source = - \\#version 330 core - \\ - \\layout(location = 0) in vec2 position; - \\out vec2 uv; - \\ - \\void main() { - \\ uv = position / 2.0 + 0.5; - \\ gl_Position = vec4(position, 0, 1); - \\} - ; - - if (gl.ShaderProgram.create(vert_source, frag_source)) |new_program| { - current.destroy(); - current.* = new_program; - } else |err| { - debug.print("Error while reloading shader: {}\n", .{err}); - } -} diff --git a/src/output.zig b/src/output.zig index df219d2..b5c6885 100644 --- a/src/output.zig +++ b/src/output.zig @@ -2,7 +2,6 @@ const c = @import("c.zig"); const std = @import("std"); const cfg = @import("config.zig"); const gl = @import("gl.zig"); -const yaml = @import("yaml.zig"); pub const Output = struct { update_fn: *const fn (output: *Output, texture_id: c.GLuint) bool, @@ -35,38 +34,6 @@ pub const TSVOutput = struct { pub const default: Config = .{ .name = "glsl-view", }; - - pub fn parse(parser: *c.yaml_parser_t, allocator: std.mem.Allocator) !Config { - var self: Config = .default; - - while (true) { - var event: c.yaml_event_t = undefined; - if (c.yaml_parser_parse(parser, &event) != 1) - return error.YAMLParserError; - defer c.yaml_event_delete(&event); - - switch (event.type) { - c.YAML_MAPPING_END_EVENT => break, - c.YAML_SCALAR_EVENT => { - const data = event.data.scalar; - const key: []const u8 = data.value[0..data.length]; - - if (std.mem.eql(u8, key, "name")) { - self.name = try yaml.parseStringZ(parser, allocator); - } else { - std.debug.print("unknown key: '{s}'\n", .{key}); - try yaml.skipAny(parser); - } - }, - else => { - std.debug.print("unexpected event: {}\n", .{event.type}); - return error.InvalidConfiguration; - }, - } - } - - return self; - } }; output: Output, @@ -154,46 +121,6 @@ pub const WindowOutput = struct { .monitor = "", .fullscreen = false, }; - - pub fn parse(parser: *c.yaml_parser_t, allocator: std.mem.Allocator) !Config { - var self: Config = .default; - - while (true) { - var event: c.yaml_event_t = undefined; - if (c.yaml_parser_parse(parser, &event) != 1) - return error.YAMLParserError; - defer c.yaml_event_delete(&event); - - switch (event.type) { - c.YAML_MAPPING_END_EVENT => break, - c.YAML_SCALAR_EVENT => { - const data = event.data.scalar; - const key: []const u8 = data.value[0..data.length]; - - if (std.mem.eql(u8, key, "width")) { - self.width = try yaml.parseInt(parser, i32); - } else if (std.mem.eql(u8, key, "height")) { - self.height = try yaml.parseInt(parser, i32); - } else if (std.mem.eql(u8, key, "filter")) { - self.filter = try yaml.parseEnum(parser, FilterMode); - } else if (std.mem.eql(u8, key, "monitor")) { - self.monitor = try yaml.parseString(parser, allocator); - } else if (std.mem.eql(u8, key, "fullscreen")) { - self.fullscreen = try yaml.parseBool(parser); - } else { - std.debug.print("unknown key: '{s}'\n", .{key}); - try yaml.skipAny(parser); - } - }, - else => { - std.debug.print("unexpected event: {}\n", .{event.type}); - return error.InvalidConfiguration; - }, - } - } - - return self; - } }; output: Output, |
