forked from mirrors/zig-glsl-view
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
14 KiB
Zig
415 lines
14 KiB
Zig
const c = @import("c.zig");
|
|
const fs = @import("std").fs;
|
|
const std = @import("std");
|
|
const debug = @import("std").debug;
|
|
|
|
pub const OutputConfig = struct {
|
|
const Type = enum {
|
|
window,
|
|
};
|
|
|
|
type: Type,
|
|
width: i32,
|
|
height: i32,
|
|
|
|
// only for 'window'
|
|
monitor: []const u8,
|
|
transparent: bool,
|
|
fullscreen: bool,
|
|
};
|
|
|
|
pub const ParameterConfig = struct {
|
|
name: []const u8,
|
|
type: []const u8,
|
|
};
|
|
|
|
pub const OSCConfig = union(enum) {
|
|
const Protocol = enum {
|
|
udp,
|
|
tcp,
|
|
unix,
|
|
};
|
|
|
|
Manual: struct {
|
|
protocol: Protocol = Protocol.udp,
|
|
port: u16 = 9000,
|
|
},
|
|
URL: []const u8,
|
|
};
|
|
|
|
const defaultOutput = OutputConfig{
|
|
.type = .window,
|
|
.width = 800,
|
|
.height = 600,
|
|
.monitor = "",
|
|
.transparent = false,
|
|
.fullscreen = false,
|
|
};
|
|
|
|
pub const Config = struct {
|
|
width: i32,
|
|
height: i32,
|
|
outputs: []const OutputConfig,
|
|
fragment: []const u8,
|
|
parameters: []const ParameterConfig,
|
|
osc: OSCConfig,
|
|
|
|
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);
|
|
|
|
var config: Config = .{
|
|
.width = 1920,
|
|
.height = 1080,
|
|
.outputs = ([_]OutputConfig{defaultOutput})[0..],
|
|
.fragment = "",
|
|
.parameters = ([0]ParameterConfig{})[0..],
|
|
.osc = .{ .URL = "osc.udp://:9000" },
|
|
};
|
|
|
|
try expectEvent(&parser, c.YAML_STREAM_START_EVENT);
|
|
try expectEvent(&parser, c.YAML_DOCUMENT_START_EVENT);
|
|
try expectEvent(&parser, c.YAML_MAPPING_START_EVENT);
|
|
|
|
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")) {
|
|
config.width = try parseInt(&parser, i32);
|
|
} else if (std.mem.eql(u8, key, "height")) {
|
|
config.height = try parseInt(&parser, i32);
|
|
} else if (std.mem.eql(u8, key, "fragment")) {
|
|
config.fragment = try 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, "osc")) {
|
|
config.osc = try parseOSC(&parser, allocator);
|
|
} else {
|
|
debug.print("unknown config key: '{s}'\n", .{key});
|
|
try skipAny(&parser);
|
|
}
|
|
},
|
|
else => {
|
|
debug.print("unexpected config event: {}\n", .{event.type});
|
|
return error.InvalidConfiguration;
|
|
},
|
|
}
|
|
}
|
|
|
|
try expectEvent(&parser, c.YAML_DOCUMENT_END_EVENT);
|
|
try expectEvent(&parser, c.YAML_STREAM_END_EVENT);
|
|
|
|
return config;
|
|
}
|
|
};
|
|
|
|
fn expectEvent(parser: *c.yaml_parser_t, expectedType: c.yaml_event_type_t) !void {
|
|
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 != expectedType) {
|
|
debug.print("unexpected event: {}\n", .{event.type});
|
|
return error.InvalidConfiguration;
|
|
}
|
|
}
|
|
|
|
fn parseStringEvent(event: *c.yaml_event_t, allocator: std.mem.Allocator) ![]const u8 {
|
|
if (event.type != c.YAML_SCALAR_EVENT)
|
|
return error.InvalidType;
|
|
|
|
const data = event.data.scalar;
|
|
const value: []const u8 = data.value[0..data.length];
|
|
return try allocator.dupe(u8, value);
|
|
}
|
|
|
|
fn parseString(parser: *c.yaml_parser_t, allocator: std.mem.Allocator) ![]const u8 {
|
|
var event: c.yaml_event_t = undefined;
|
|
if (c.yaml_parser_parse(parser, &event) != 1)
|
|
return error.YAMLParserError;
|
|
defer c.yaml_event_delete(&event);
|
|
|
|
return parseStringEvent(&event, allocator);
|
|
}
|
|
|
|
fn parseInt(parser: *c.yaml_parser_t, comptime T: type) !T {
|
|
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)
|
|
return error.InvalidType;
|
|
|
|
const data = event.data.scalar;
|
|
const value: []const u8 = data.value[0..data.length];
|
|
return try std.fmt.parseInt(T, value, 10);
|
|
}
|
|
|
|
fn parseEnum(parser: *c.yaml_parser_t, comptime T: type) !T {
|
|
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)
|
|
return error.InvalidType;
|
|
|
|
const data = event.data.scalar;
|
|
const value: []const u8 = data.value[0..data.length];
|
|
|
|
inline for (@typeInfo(T).Enum.fields) |field| {
|
|
if (std.mem.eql(u8, value, field.name))
|
|
return @field(T, field.name);
|
|
}
|
|
|
|
return error.InvalidValue;
|
|
}
|
|
|
|
fn parseBool(parser: *c.yaml_parser_t) !bool {
|
|
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)
|
|
return error.InvalidType;
|
|
|
|
const data = event.data.scalar;
|
|
const value: []const u8 = data.value[0..data.length];
|
|
|
|
if (data.style != c.YAML_PLAIN_SCALAR_STYLE)
|
|
return false;
|
|
return std.mem.eql(u8, value, "true") or std.mem.eql(u8, value, "1");
|
|
}
|
|
|
|
fn parseOutputs(parser: *c.yaml_parser_t, allocator: std.mem.Allocator) ![]OutputConfig {
|
|
try expectEvent(parser, c.YAML_SEQUENCE_START_EVENT);
|
|
|
|
var outputs = try allocator.alloc(OutputConfig, 1024);
|
|
var output_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 => {
|
|
var 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")) {
|
|
output.*.type = try parseEnum(parser, OutputConfig.Type);
|
|
} else if (std.mem.eql(u8, key, "width")) {
|
|
output.*.width = try parseInt(parser, i32);
|
|
} else if (std.mem.eql(u8, key, "height")) {
|
|
output.*.height = try parseInt(parser, i32);
|
|
} else if (std.mem.eql(u8, key, "monitor")) {
|
|
output.*.monitor = try parseString(parser, allocator);
|
|
} else if (std.mem.eql(u8, key, "fullscreen")) {
|
|
output.*.fullscreen = try parseBool(parser);
|
|
} else if (std.mem.eql(u8, key, "transparent")) {
|
|
output.*.transparent = try parseBool(parser);
|
|
} else {
|
|
debug.print("unknown config key: '{s}'\n", .{key});
|
|
try skipAny(parser);
|
|
}
|
|
},
|
|
else => {
|
|
debug.print("unexpected config event: {}\n", .{event.type});
|
|
return error.InvalidConfiguration;
|
|
},
|
|
}
|
|
}
|
|
|
|
output_count += 1;
|
|
},
|
|
else => {
|
|
debug.print("unexpected config 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 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 => {
|
|
var 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 parseString(parser, allocator);
|
|
have_name = true;
|
|
} else if (std.mem.eql(u8, key, "type")) {
|
|
param.*.type = try parseString(parser, allocator);
|
|
have_type = true;
|
|
} else {
|
|
debug.print("unknown config key: '{s}'\n", .{key});
|
|
try skipAny(parser);
|
|
}
|
|
},
|
|
else => {
|
|
debug.print("unexpected config 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 config 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 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 parseEnum(parser, OSCConfig.Protocol);
|
|
} else if (std.mem.eql(u8, key, "port")) {
|
|
config.Manual.port = try parseInt(parser, u16);
|
|
} else {
|
|
debug.print("unknown key: '{s}'\n", .{key});
|
|
try skipAny(parser);
|
|
}
|
|
},
|
|
else => {
|
|
debug.print("unexpected event: {}\n", .{event.type});
|
|
return error.InvalidConfiguration;
|
|
},
|
|
}
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
fn skipAny(parser: *c.yaml_parser_t) !void {
|
|
var event: c.yaml_event_t = undefined;
|
|
var depth: u32 = 0;
|
|
while (true) {
|
|
if (c.yaml_parser_parse(parser, &event) != 1)
|
|
return error.YAMLParserError;
|
|
defer c.yaml_event_delete(&event);
|
|
|
|
switch (event.type) {
|
|
c.YAML_SCALAR_EVENT => {
|
|
if (depth == 0)
|
|
break;
|
|
},
|
|
|
|
c.YAML_SEQUENCE_START_EVENT, c.YAML_MAPPING_START_EVENT => depth += 1,
|
|
|
|
c.YAML_SEQUENCE_END_EVENT, c.YAML_MAPPING_END_EVENT => {
|
|
depth -= 1;
|
|
|
|
if (depth == 0)
|
|
break;
|
|
},
|
|
|
|
else => {
|
|
debug.print("unexpected event: {}\n", .{event.type});
|
|
return error.InvalidConfiguration;
|
|
},
|
|
}
|
|
}
|
|
}
|