const c = @import("c.zig"); const gl = @import("gl.zig"); const std = @import("std"); const cfg = @import("config.zig"); const param_prefix = "/param/"; pub const ControlServer = struct { server: c.lo_server, shader: gl.ShaderProgram, pub fn init(allocator: *std.mem.Allocator, config: cfg.OSCConfig) !*ControlServer { var self: *ControlServer = try allocator.create(ControlServer); switch (config) { .Manual => |conf| { var port = [_]u8{0} ** 6; _ = std.fmt.formatIntBuf(port[0..], conf.port, 10, false, std.fmt.FormatOptions{}); const proto: c_int = switch (conf.protocol) { .udp => c.LO_UDP, .tcp => c.LO_TCP, .unix => c.LO_UNIX, else => unreachable, }; std.debug.warn("listening for OSC messages on {} port {}\n", .{ conf.protocol, conf.port }); self.server = c.lo_server_new_with_proto(port[0..].ptr, proto, handle_error); }, .URL => |url| { std.debug.warn("listening for OSC messages at {s}\n", .{url}); self.server = c.lo_server_new_from_url(url[0..].ptr, handle_error); }, else => unreachable, } if (self.server == null) return error.serverInitializationError; _ = c.lo_server_add_method(self.server, null, null, handle_method, @ptrCast(*c_void, self)); return self; } pub fn update(self: *ControlServer, shader: gl.ShaderProgram) void { self.shader = shader; while (c.lo_server_recv_noblock(self.server, 0) > 0) {} } pub fn destroy(self: ControlServer) void { c.lo_server_free(self.server); } fn handle_error(num: c_int, msg: [*c]const u8, where: [*c]const u8) callconv(.C) void { std.debug.warn("OSC error {} @ {s}: {s}\n", .{ num, @ptrCast([*:0]const u8, where), @ptrCast([*:0]const u8, msg) }); } fn handle_method(_path: [*c]const u8, _types: [*c]const u8, argv: [*c][*c]c.lo_arg, argc: c_int, msg: c.lo_message, userdata: ?*c_void) callconv(.C) c_int { const self = @ptrCast(*ControlServer, @alignCast(@alignOf(ControlServer), userdata.?)); const path = _path[0..c.strlen(_path)]; const types = _types[0..@intCast(u32, argc)]; if (std.mem.startsWith(u8, path, "/param/")) { if (!std.mem.endsWith(u8, path, "/set")) { std.debug.warn("unhandled OSC message {s} ({s})\n", .{ path, types }); return 1; } const name_slice = path["/param/".len .. path.len - "/set".len]; // const uniform = self.shader.uniformLocation(name) catch { // std.debug.warn("param {s} doesn't exist\n", .{uniform_name}); // return 0; // }; // if (std.mem.eql(u8, types, "f")) { // self.shader.setUniform1f(uniform, argv[0].*.f); // } else if (std.mem.eql(u8, types, "ff")) { // self.shader.setUniform2f(uniform, argv[0].*.f, argv[1].*.f); // } else if (std.mem.eql(u8, types, "fff")) { // self.shader.setUniform3f(uniform, argv[0].*.f, argv[1].*.f, argv[2].*.f); // } else if (std.mem.eql(u8, types, "ffff")) { // self.shader.setUniform4f(uniform, argv[0].*.f, argv[1].*.f, argv[2].*.f, argv[3].*.f); // } else if (std.mem.eql(u8, types, "i")) { // self.shader.setUniform1i(uniform, argv[0].*.i); // } else if (std.mem.eql(u8, types, "ii")) { // self.shader.setUniform2i(uniform, argv[0].*.i, argv[1].*.i); // } else if (std.mem.eql(u8, types, "iii")) { // self.shader.setUniform3i(uniform, argv[0].*.i, argv[1].*.i, argv[2].*.i); // } else if (std.mem.eql(u8, types, "iiii")) { // self.shader.setUniform4i(uniform, argv[0].*.i, argv[1].*.i, argv[2].*.i, argv[3].*.i); // } else { // std.debug.warn("unsupported types: {s}\n", .{types}); // } return 0; } std.debug.warn("unhandled OSC message {s} ({s})\n", .{ path, types }); return 1; } fn handle_bundle_start(time: c.lo_timetag, userdata: ?*c_void) callconv(.C) c_int { return 1; } fn handle_bundle_end(userdata: ?*c_void) callconv(.C) c_int { return 1; } };