Compare commits
4 Commits
4573d015a1
...
64a3b6c74a
Author | SHA1 | Date |
---|---|---|
LordMZTE | 64a3b6c74a | |
LordMZTE | 105fb94ef7 | |
LordMZTE | 336fe9d864 | |
LordMZTE | 5ba7cc86f6 |
|
@ -0,0 +1 @@
|
|||
/zig-*
|
|
@ -0,0 +1 @@
|
|||
Common library for all my Zig scripts.
|
|
@ -1,15 +1,19 @@
|
|||
//! Shared code for script build scripts
|
||||
const std = @import("std");
|
||||
|
||||
pub const confgen_json_opt = std.json.ParseOptions{ .ignore_unknown_fields = true };
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
_ = b.addModule("common", .{
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: make confgen generate zon and delete
|
||||
/// Retrieve some confgen options given a relative path to the dotfile root and a struct type
|
||||
/// with a field for each option.
|
||||
pub fn confgenGet(comptime T: type, root_path: []const u8, alloc: std.mem.Allocator) !T {
|
||||
const optsjson = try std.fs.path.join(alloc, &.{ root_path, "cgout", "opts.json" });
|
||||
defer alloc.free(optsjson);
|
||||
|
||||
var file = try std.fs.cwd().openFile(optsjson, .{});
|
||||
pub fn confgenGet(comptime T: type, alloc: std.mem.Allocator) !T {
|
||||
const optjson_path = comptime std.fs.path.dirname(@src().file).? ++ "/../../cgout/opts.json";
|
||||
var file = try std.fs.cwd().openFile(optjson_path, .{});
|
||||
defer file.close();
|
||||
var buf_reader = std.io.bufferedReader(file.reader());
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
const std = @import("std");
|
||||
|
||||
var stderr_isatty: ?bool = null;
|
||||
|
||||
pub var log_file: ?std.fs.File = null;
|
||||
|
||||
pub fn logFn(
|
||||
comptime level: std.log.Level,
|
||||
comptime scope: @TypeOf(.enum_literal),
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
const log_pfx: ?[]const u8 = if (@hasDecl(@import("root"), "mztecommon_log_pfx"))
|
||||
@import("root").mztecommon_log_pfx
|
||||
else
|
||||
null;
|
||||
|
||||
const color = log_file == null and stderr_isatty orelse blk: {
|
||||
const isatty = std.os.isatty(std.os.STDERR_FILENO);
|
||||
stderr_isatty = isatty;
|
||||
break :blk isatty;
|
||||
};
|
||||
|
||||
const logfile = log_file orelse std.io.getStdErr();
|
||||
|
||||
const scope_prefix = if (log_pfx) |lpfx|
|
||||
if (scope != .default)
|
||||
"[" ++ lpfx ++ " " ++ @tagName(scope) ++ "] "
|
||||
else
|
||||
"[" ++ lpfx ++ "] "
|
||||
else if (scope != .default)
|
||||
"[" ++ @tagName(scope) ++ "] "
|
||||
else
|
||||
"";
|
||||
|
||||
switch (color) {
|
||||
inline else => |col| {
|
||||
const lvl_prefix = comptime if (col) switch (level) {
|
||||
.debug => "\x1b[1;34mD:\x1b[0m ",
|
||||
.info => "\x1b[1;32mI:\x1b[0m ",
|
||||
.warn => "\x1b[1;33mW:\x1b[0m ",
|
||||
.err => "\x1b[1;31mE:\x1b[0m ",
|
||||
} else switch (level) {
|
||||
.debug => "D: ",
|
||||
.info => "I: ",
|
||||
.warn => "W: ",
|
||||
.err => "E: ",
|
||||
};
|
||||
|
||||
logfile.writer().print(scope_prefix ++ lvl_prefix ++ fmt ++ "\n", args) catch {};
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
const std = @import("std");
|
||||
const common = @import("build_common.zig");
|
||||
const common = @import("common");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
@ -21,7 +21,7 @@ pub fn build(b: *std.Build) !void {
|
|||
|
||||
const cg_opt = try common.confgenGet(struct {
|
||||
term_font: []u8, // TODO: this being non-const is a workaround for an std bug
|
||||
}, "..", b.allocator);
|
||||
}, b.allocator);
|
||||
|
||||
const opts = b.addOptions();
|
||||
opts.addOption([]const u8, "font", cg_opt.term_font);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
.version = "0.0.0",
|
||||
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../lib/common-zig" },
|
||||
.znvim = .{
|
||||
.url = "git+https://git.mzte.de/LordMZTE/znvim.git#8e52c461dc071e6b88c8e77e49aa2805f225e7da",
|
||||
.hash = "122029929d792aa32a71c0b98bb67d344d971d269d6fea5b8f8693e29f0f45924951",
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../build_common.zig
|
|
@ -7,11 +7,11 @@ pub fn build(b: *std.Build) void {
|
|||
const lib = b.addSharedLibrary(.{
|
||||
.name = "mzte-mpv",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.link_libc = true,
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
lib.linkLibC();
|
||||
lib.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
|
||||
const install_step = b.addInstallArtifact(lib, .{
|
||||
// this is not a standard MPV installation path, but instead one that makes sense.
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.{
|
||||
.name = "mzte-mpv",
|
||||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
}
|
||||
}
|
|
@ -6,22 +6,11 @@ const util = @import("util.zig");
|
|||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = struct {
|
||||
fn logFn(
|
||||
comptime message_level: std.log.Level,
|
||||
comptime scope: @TypeOf(.enum_literal),
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
_ = scope;
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
stderr.print("[mzte-mpv {s}] " ++ format ++ "\n", .{@tagName(message_level)} ++ args) catch return;
|
||||
}
|
||||
}.logFn,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub const mztecommon_log_pfx = "mzte-mpv";
|
||||
|
||||
export fn mpv_open_cplugin(handle: *c.mpv_handle) callconv(.C) c_int {
|
||||
tryMain(handle) catch |e| {
|
||||
if (@errorReturnTrace()) |ert|
|
||||
|
|
|
@ -35,7 +35,7 @@ pub fn onEvent(self: *LiveChat, mpv: *c.mpv_handle, ev: *c.mpv_event) !void {
|
|||
errdefer file.close();
|
||||
std.log.info("initializing subtitle transcoder: {s}", .{fname});
|
||||
|
||||
const pipe = try std.os.pipe2(0);
|
||||
const pipe = try std.os.pipe2(.{});
|
||||
|
||||
// This needs to be done here instead of the separate thread. MPV will instantly
|
||||
// give up if there's nothing to be read from the pipe when the command is called.
|
||||
|
|
|
@ -11,6 +11,8 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.{
|
||||
.name = "alecor",
|
||||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
},
|
||||
}
|
|
@ -5,6 +5,7 @@ const util = @import("util.zig");
|
|||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
|
|
|
@ -11,6 +11,8 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.{
|
||||
.name = "hyprtool",
|
||||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
},
|
||||
}
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
|||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
const std = @import("std");
|
||||
const common = @import("build_common.zig");
|
||||
const common = @import("common");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const ansi_term_mod = b.dependency("ansi_term", .{}).module("ansi-term");
|
||||
const common_mod = b.dependency("common", .{}).module("common");
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "mzteinit",
|
||||
|
@ -23,11 +24,12 @@ pub fn build(b: *std.Build) !void {
|
|||
|
||||
inline for (.{ mzteinitctl, exe }) |e| {
|
||||
e.root_module.addImport("ansi-term", ansi_term_mod);
|
||||
e.root_module.addImport("common", common_mod);
|
||||
}
|
||||
|
||||
const cg_opt = try common.confgenGet(struct {
|
||||
gtk_theme: []u8, // TODO: this being non-const is a workaround for an std bug
|
||||
}, "../..", b.allocator);
|
||||
}, b.allocator);
|
||||
|
||||
const opts = b.addOptions();
|
||||
opts.addOption([]const u8, "gtk_theme", cg_opt.gtk_theme);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
.version = "0.0.0",
|
||||
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
.ansi_term = .{
|
||||
.url = "git+https://github.com/LordMZTE/ansi-term.git#73c03175068679685535111dbea72cade075719e",
|
||||
.hash = "1220ea86ace34b38e49c1d737c5f857d88346af10695a992b38e10cb0a73b6a19ef7",
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../build_common.zig
|
|
@ -1,5 +1,7 @@
|
|||
const std = @import("std");
|
||||
const at = @import("ansi-term");
|
||||
const common = @import("common");
|
||||
|
||||
const env = @import("env.zig");
|
||||
const command = @import("command.zig");
|
||||
const util = @import("util.zig");
|
||||
|
@ -11,36 +13,12 @@ const msg = @import("message.zig").msg;
|
|||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = struct {
|
||||
pub fn logFn(
|
||||
comptime msg_level: std.log.Level,
|
||||
comptime scope: @TypeOf(.enum_literal),
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
const logfile = log_file orelse return;
|
||||
|
||||
if (scope != .default) {
|
||||
logfile.writer().print("[{s}] ", .{@tagName(scope)}) catch return;
|
||||
}
|
||||
|
||||
logfile.writer().writeAll(switch (msg_level) {
|
||||
.err => "E: ",
|
||||
.warn => "W: ",
|
||||
.info => "I: ",
|
||||
.debug => "D: ",
|
||||
}) catch return;
|
||||
|
||||
logfile.writer().print(fmt ++ "\n", args) catch return;
|
||||
}
|
||||
}.logFn,
|
||||
.logFn = common.logFn,
|
||||
};
|
||||
|
||||
var log_file: ?std.fs.File = null;
|
||||
|
||||
pub fn main() void {
|
||||
log_file = createLogFile() catch null;
|
||||
defer if (log_file) |lf| lf.close();
|
||||
common.log_file = createLogFile() catch null;
|
||||
defer if (common.log_file) |lf| lf.close();
|
||||
|
||||
tryMain() catch |e| {
|
||||
std.log.err("FATAL ERROR: {}", .{e});
|
||||
|
|
|
@ -4,6 +4,7 @@ const Client = @import("sock/Client.zig");
|
|||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const std = @import("std");
|
||||
const common = @import("build_common.zig");
|
||||
const common = @import("common");
|
||||
|
||||
const Scanner = @import("wayland").Scanner;
|
||||
|
||||
|
@ -7,15 +7,26 @@ pub fn build(b: *std.Build) !void {
|
|||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const cg_opt = try common.confgenGet(struct { nvidia: bool = false, term: struct { command: [:0]const u8 }, commands: struct {
|
||||
file_manager: [:0]const u8,
|
||||
browser: [:0]const u8,
|
||||
}, cursor: struct {
|
||||
theme: [:0]const u8,
|
||||
size: u32,
|
||||
} }, "../..", b.allocator);
|
||||
const cg_opt = try common.confgenGet(struct {
|
||||
catppuccin: struct {
|
||||
red: [:0]const u8,
|
||||
sky: [:0]const u8,
|
||||
},
|
||||
nvidia: bool = false,
|
||||
term: struct { command: [:0]const u8 },
|
||||
commands: struct {
|
||||
file_manager: [:0]const u8,
|
||||
browser: [:0]const u8,
|
||||
},
|
||||
cursor: struct {
|
||||
theme: [:0]const u8,
|
||||
size: u32,
|
||||
},
|
||||
}, b.allocator);
|
||||
|
||||
const opts = b.addOptions();
|
||||
opts.addOption([:0]const u8, "catppuccin_red", cg_opt.catppuccin.red);
|
||||
opts.addOption([:0]const u8, "catppuccin_sky", cg_opt.catppuccin.sky);
|
||||
opts.addOption(bool, "nvidia", cg_opt.nvidia);
|
||||
opts.addOption([:0]const u8, "term_command", cg_opt.term.command);
|
||||
opts.addOption([:0]const u8, "file_manager_command", cg_opt.commands.file_manager);
|
||||
|
@ -32,6 +43,7 @@ pub fn build(b: *std.Build) !void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
exe.root_module.addImport("opts", opts.createModule());
|
||||
exe.root_module.addImport("wayland", scanner.mod);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
.version = "0.0.0",
|
||||
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
.wayland = .{
|
||||
.url = "git+https://git.mzte.de/LordMZTE/zig-wayland#4de9f2d6d5fddae1fbb29e21fd1dae69e646ab7d",
|
||||
.hash = "1220d6448c277e5c41348aa95ce2ba2fc92a92cb7a9e9783edf0f816cd0260122d31",
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../build_common.zig
|
|
@ -1,11 +1,11 @@
|
|||
const std = @import("std");
|
||||
const opts = @import("opts");
|
||||
|
||||
const log = std.log.scoped(.mzteriver);
|
||||
const log = std.log.scoped(.init);
|
||||
|
||||
const Connection = @import("Connection.zig");
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) !void {
|
||||
pub fn init(alloc: std.mem.Allocator, initial: bool) !void {
|
||||
const con = try Connection.init();
|
||||
defer con.deinit();
|
||||
|
||||
|
@ -145,8 +145,8 @@ pub fn init(alloc: std.mem.Allocator) !void {
|
|||
|
||||
try con.runCommand(&.{ "set-repeat", "50", "300" });
|
||||
|
||||
try con.runCommand(&.{ "border-color-focused", "0x880000" });
|
||||
try con.runCommand(&.{ "border-color-unfocused", "0x660000" });
|
||||
try con.runCommand(&.{ "border-color-focused", "0x" ++ opts.catppuccin_red });
|
||||
try con.runCommand(&.{ "border-color-unfocused", "0x" ++ opts.catppuccin_sky });
|
||||
|
||||
try con.runCommand(&.{ "hide-cursor", "when-typing", "enabled" });
|
||||
|
||||
|
@ -170,7 +170,10 @@ pub fn init(alloc: std.mem.Allocator) !void {
|
|||
);
|
||||
defer alloc.free(init_path);
|
||||
|
||||
var init_child = std.process.Child.init(&.{init_path}, alloc);
|
||||
var init_child = std.process.Child.init(
|
||||
&.{ init_path, if (initial) "init" else "reinit" },
|
||||
alloc,
|
||||
);
|
||||
const term = init_child.spawnAndWait() catch |e| switch (e) {
|
||||
error.FileNotFound => b: {
|
||||
log.info("no river_init", .{});
|
||||
|
@ -184,7 +187,7 @@ pub fn init(alloc: std.mem.Allocator) !void {
|
|||
return error.InitBorked;
|
||||
}
|
||||
|
||||
log.info("configuration finished, spawning processes", .{});
|
||||
log.info("configuration finished, initial: {}", .{initial});
|
||||
|
||||
// tell confgenfs we're now using river
|
||||
confgenfs: {
|
||||
|
@ -195,7 +198,7 @@ pub fn init(alloc: std.mem.Allocator) !void {
|
|||
defer alloc.free(cgfs_eval_path);
|
||||
|
||||
const evalf = std.fs.cwd().openFile(cgfs_eval_path, .{ .mode = .write_only }) catch {
|
||||
std.log.warn("unable to open confgenfs eval file", .{});
|
||||
log.warn("unable to open confgenfs eval file", .{});
|
||||
break :confgenfs;
|
||||
};
|
||||
defer evalf.close();
|
||||
|
@ -205,21 +208,25 @@ pub fn init(alloc: std.mem.Allocator) !void {
|
|||
);
|
||||
}
|
||||
|
||||
var child_arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer child_arena.deinit();
|
||||
if (initial) {
|
||||
log.info("spawning processes", .{});
|
||||
|
||||
// spawn background processes
|
||||
inline for (.{
|
||||
.{"wlbg"},
|
||||
.{"waybar"},
|
||||
.{ "dbus-update-activation-environment", "DISPLAY", "XAUTHORITY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP" },
|
||||
.{ "systemctl", "--user", "import-environment", "DISPLAY", "XAUTHORITY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP" },
|
||||
.{ "rivertile", "-view-padding", "6", "-outer-padding", "6" },
|
||||
}) |argv| {
|
||||
// TODO: wonk
|
||||
// We use an arena here to prevent leaks because process.Child apparently doesn't support
|
||||
// detaching.
|
||||
var child = std.process.Child.init(&argv, child_arena.allocator());
|
||||
try child.spawn();
|
||||
var child_arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer child_arena.deinit();
|
||||
|
||||
// spawn background processes
|
||||
inline for (.{
|
||||
.{"wlbg"},
|
||||
.{"waybar"},
|
||||
.{ "dbus-update-activation-environment", "DISPLAY", "XAUTHORITY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP" },
|
||||
.{ "systemctl", "--user", "import-environment", "DISPLAY", "XAUTHORITY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP" },
|
||||
.{ "rivertile", "-view-padding", "6", "-outer-padding", "6" },
|
||||
}) |argv| {
|
||||
// TODO: wonk
|
||||
// We use an arena here to prevent leaks because process.Child apparently doesn't support
|
||||
// detaching.
|
||||
var child = std.process.Child.init(&argv, child_arena.allocator());
|
||||
try child.spawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
const std = @import("std");
|
||||
const opts = @import("opts");
|
||||
|
||||
const log = std.log.scoped(.mzteriver);
|
||||
const init = @import("init.zig").init;
|
||||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = switch (@import("builtin").mode) {
|
||||
.Debug => .debug,
|
||||
else => .info,
|
||||
},
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub const mztecommon_log_pfx = "mzteriver";
|
||||
|
||||
pub fn main() !void {
|
||||
var dbg_gpa = if (@import("builtin").mode == .Debug) std.heap.GeneralPurposeAllocator(.{}){} else {};
|
||||
defer if (@TypeOf(dbg_gpa) != void) {
|
||||
|
@ -20,10 +23,15 @@ pub fn main() !void {
|
|||
if (std.mem.endsWith(u8, std.mem.span(std.os.argv[0]), "init") or
|
||||
(std.os.argv.len >= 2 and std.mem.orderZ(u8, std.os.argv[1], "init") == .eq))
|
||||
{
|
||||
log.info("running in init mode", .{});
|
||||
try @import("init.zig").init(alloc);
|
||||
std.log.info("running in init mode", .{});
|
||||
try init(alloc, true);
|
||||
} else if (std.mem.endsWith(u8, std.mem.span(std.os.argv[0]), "reinit") or
|
||||
(std.os.argv.len >= 2 and std.mem.orderZ(u8, std.os.argv[1], "reinit") == .eq))
|
||||
{
|
||||
std.log.info("running in reinit mode", .{});
|
||||
try init(alloc, false);
|
||||
} else {
|
||||
log.info("running in launch mode", .{});
|
||||
std.log.info("running in launch mode", .{});
|
||||
|
||||
const logfd = logf: {
|
||||
const logf_path = try std.fmt.allocPrintZ(
|
||||
|
@ -33,7 +41,7 @@ pub fn main() !void {
|
|||
);
|
||||
defer alloc.free(logf_path);
|
||||
|
||||
log.info("river log file: {s}", .{logf_path});
|
||||
std.log.info("river log file: {s}", .{logf_path});
|
||||
|
||||
break :logf try std.os.openatZ(
|
||||
std.os.AT.FDCWD,
|
||||
|
|
|
@ -10,6 +10,9 @@ pub fn build(b: *std.Build) void {
|
|||
.target = target,
|
||||
.optimize = mode,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const desktop_install_step = b.addInstallFile(
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.{
|
||||
.name = "openbrowser",
|
||||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
},
|
||||
}
|
|
@ -3,6 +3,7 @@ const info = @import("info.zig");
|
|||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
const browsers = &[_][]const u8{
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
zig-cache/
|
||||
zig-out/
|
||||
deps.zig
|
||||
gyro.lock
|
||||
.gyro
|
|
@ -1,6 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Name=Playtwitch
|
||||
Comment=Launch a twitch stream
|
||||
Type=Application
|
||||
Exec=playtwitch
|
||||
Icon=playtwitch
|
|
@ -1,79 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 135.46666 135.46667"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14, custom)"
|
||||
sodipodi:docname="logo.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.1250517"
|
||||
inkscape:cx="230.21164"
|
||||
inkscape:cy="251.09957"
|
||||
inkscape:window-width="1876"
|
||||
inkscape:window-height="1018"
|
||||
inkscape:window-x="1940"
|
||||
inkscape:window-y="40"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2011">
|
||||
<stop
|
||||
style="stop-color:#9146ff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2007" />
|
||||
<stop
|
||||
style="stop-color:#d146ff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2009" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2011"
|
||||
id="linearGradient2013"
|
||||
x1="8.4666662"
|
||||
y1="67.733333"
|
||||
x2="127"
|
||||
y2="67.733333"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:url(#linearGradient2013);fill-opacity:1;stroke:none;stroke-width:6.83395;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect1982"
|
||||
width="118.53333"
|
||||
height="118.53333"
|
||||
x="8.4666662"
|
||||
y="8.4666662"
|
||||
rx="2.1166666"
|
||||
ry="2.1166666" />
|
||||
<path
|
||||
id="rect131"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.55387;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 15.897361,15.897361 V 119.56931 H 119.56931 V 15.897361 Z M 41.6205,36.946187 93.846169,67.733336 41.6205,98.520488 Z" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
|
@ -1,42 +0,0 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "playtwitch",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.linkLibC();
|
||||
exe.linkSystemLibrary("cimgui");
|
||||
exe.linkSystemLibrary("glfw3");
|
||||
exe.linkSystemLibrary("glew");
|
||||
exe.linkSystemLibrary("curl");
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
var logo_install_step = b.addInstallFile(
|
||||
.{ .path = "assets/playtwitch.svg" },
|
||||
"share/icons/hicolor/scalable/apps/playtwitch.svg",
|
||||
);
|
||||
b.getInstallStep().dependOn(&logo_install_step.step);
|
||||
|
||||
var desktop_entry_install_step = b.addInstallFile(
|
||||
.{ .path = "assets/playtwitch.desktop" },
|
||||
"share/applications/playtwitch.desktop",
|
||||
);
|
||||
b.getInstallStep().dependOn(&desktop_entry_install_step.step);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
const config = @import("config.zig");
|
||||
const log = std.log.scoped(.state);
|
||||
|
||||
pub const Entry = union(enum) {
|
||||
channel: ChannelEntry,
|
||||
|
||||
/// a seperator in the channel list, the optional string is a heading.
|
||||
separator: ?[]const u8,
|
||||
};
|
||||
|
||||
pub const ChannelEntry = struct {
|
||||
name: []const u8,
|
||||
comment: ?[]const u8,
|
||||
live: Live = .loading,
|
||||
};
|
||||
|
||||
pub const Live = enum {
|
||||
live,
|
||||
offline,
|
||||
loading,
|
||||
err,
|
||||
};
|
||||
|
||||
mutex: std.Thread.Mutex,
|
||||
win: *c.GLFWwindow,
|
||||
|
||||
/// start chatty if true
|
||||
chatty: bool,
|
||||
chatty_alive: bool,
|
||||
|
||||
/// an array of channels, composed of slices into `channels_file_data`
|
||||
channels: ?[]Entry,
|
||||
|
||||
/// the data of the channels configuration file
|
||||
channels_file_data: ?[]u8,
|
||||
|
||||
quality_buf: [64]u8,
|
||||
channel_name_buf: [64]u8,
|
||||
|
||||
streamlink_memfd: ?std.fs.File,
|
||||
streamlink_out: ?[]align(std.mem.page_size) u8,
|
||||
|
||||
/// If the status of the channels is being loaded currently
|
||||
live_status_loading: bool,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(win: *c.GLFWwindow) !*Self {
|
||||
log.info("creating state", .{});
|
||||
|
||||
// on the heap so this thing doesn't move.
|
||||
const self = try std.heap.c_allocator.create(Self);
|
||||
self.* = .{
|
||||
.mutex = .{},
|
||||
.win = win,
|
||||
|
||||
.chatty = true,
|
||||
.chatty_alive = false,
|
||||
|
||||
// initialized by config loader thread
|
||||
.channels = null,
|
||||
.channels_file_data = null,
|
||||
|
||||
.quality_buf = std.mem.zeroes([64]u8),
|
||||
.channel_name_buf = std.mem.zeroes([64]u8),
|
||||
|
||||
.streamlink_memfd = null,
|
||||
.streamlink_out = null,
|
||||
|
||||
.live_status_loading = true,
|
||||
};
|
||||
|
||||
@memcpy(self.quality_buf[0..4], "best");
|
||||
|
||||
const thread = try std.Thread.spawn(.{}, config.configLoaderThread, .{self});
|
||||
thread.detach();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn freeStreamlinkMemfd(self: *Self) void {
|
||||
if (self.streamlink_out) |mem| {
|
||||
log.info("unmapping streamlink output", .{});
|
||||
std.os.munmap(mem);
|
||||
self.streamlink_out = null;
|
||||
}
|
||||
|
||||
if (self.streamlink_memfd) |fd| {
|
||||
log.info("closing streamlink output", .{});
|
||||
fd.close();
|
||||
self.streamlink_memfd = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.freeStreamlinkMemfd();
|
||||
|
||||
if (self.channels) |ch| {
|
||||
std.heap.c_allocator.free(ch);
|
||||
}
|
||||
|
||||
if (self.channels_file_data) |d| {
|
||||
std.heap.c_allocator.free(d);
|
||||
}
|
||||
|
||||
self.* = undefined;
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
const State = @import("State.zig");
|
||||
const log = std.log.scoped(.config);
|
||||
|
||||
pub fn configLoaderThread(state: *State) !void {
|
||||
const home = std.os.getenv("HOME") orelse return error.HomeNotSet;
|
||||
const channels_path = try std.fs.path.join(
|
||||
std.heap.c_allocator,
|
||||
&.{ home, ".config", "playtwitch", "channels.cfg" },
|
||||
);
|
||||
defer std.heap.c_allocator.free(channels_path);
|
||||
|
||||
log.info("reading config from '{s}'", .{channels_path});
|
||||
const start_time = std.time.milliTimestamp();
|
||||
|
||||
const file = std.fs.cwd().openFile(channels_path, .{}) catch |e| {
|
||||
switch (e) {
|
||||
error.FileNotFound => {
|
||||
log.warn("channels config file not found at {s}, skipping.", .{channels_path});
|
||||
return;
|
||||
},
|
||||
else => return e,
|
||||
}
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
const channels_data = try file.readToEndAlloc(std.heap.c_allocator, std.math.maxInt(usize));
|
||||
var channels = std.ArrayList(State.Entry).init(std.heap.c_allocator);
|
||||
|
||||
var channels_iter = std.mem.tokenize(u8, channels_data, "\n");
|
||||
while (channels_iter.next()) |line| {
|
||||
var line_iter = std.mem.tokenize(u8, line, ":");
|
||||
|
||||
const channel = line_iter.next() orelse continue;
|
||||
const channel_trimmed = std.mem.trim(u8, channel, " \n\r");
|
||||
|
||||
if (channel_trimmed.len <= 0 or channel_trimmed[0] == '#')
|
||||
continue;
|
||||
|
||||
const comment_trimmed = blk: {
|
||||
const comment = line_iter.next() orelse break :blk null;
|
||||
|
||||
const comment_trimmed = std.mem.trim(u8, comment, " \n\r");
|
||||
|
||||
if (comment_trimmed.len == 0)
|
||||
break :blk null;
|
||||
|
||||
break :blk comment_trimmed;
|
||||
};
|
||||
|
||||
// dashes act as separator
|
||||
if (std.mem.allEqual(u8, channel_trimmed, '-')) {
|
||||
// separators can have comments to act as headings
|
||||
try channels.append(.{ .separator = comment_trimmed });
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try channels.append(.{ .channel = .{
|
||||
.name = channel_trimmed,
|
||||
.comment = comment_trimmed,
|
||||
} });
|
||||
}
|
||||
|
||||
const end_time = std.time.milliTimestamp();
|
||||
|
||||
log.info(
|
||||
"Loaded {d} channel items in {d}ms",
|
||||
.{ channels.items.len, end_time - start_time },
|
||||
);
|
||||
|
||||
{
|
||||
state.mutex.lock();
|
||||
defer state.mutex.unlock();
|
||||
|
||||
state.channels_file_data = channels_data;
|
||||
state.channels = try channels.toOwnedSlice();
|
||||
}
|
||||
|
||||
@import("live.zig").tryFetchChannelsLive(state);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
pub const c = @cImport({
|
||||
@cInclude("curl/curl.h");
|
||||
|
||||
@cInclude("GL/glew.h");
|
||||
@cInclude("GLFW/glfw3.h");
|
||||
|
||||
@cDefine("CIMGUI_DEFINE_ENUMS_AND_STRUCTS", "");
|
||||
@cInclude("cimgui.h");
|
||||
|
||||
@cDefine("CIMGUI_USE_GLFW", "");
|
||||
@cDefine("CIMGUI_USE_OPENGL3", "");
|
||||
@cInclude("cimgui_impl.h");
|
||||
});
|
|
@ -1,291 +0,0 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
const igu = @import("ig_util.zig");
|
||||
const launch = @import("launch.zig");
|
||||
const State = @import("State.zig");
|
||||
|
||||
const StartType = union(enum) {
|
||||
none,
|
||||
channel_bar,
|
||||
channels_idx: usize,
|
||||
};
|
||||
|
||||
pub fn winContent(state: *State) !void {
|
||||
state.mutex.lock();
|
||||
defer state.mutex.unlock();
|
||||
|
||||
var start: StartType = .none;
|
||||
|
||||
if (c.igBeginTable(
|
||||
"##text_inputs",
|
||||
2,
|
||||
0,
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
0.0,
|
||||
)) {
|
||||
defer c.igEndTable();
|
||||
|
||||
c.igTableSetupColumn("##label", c.ImGuiTableColumnFlags_WidthFixed, 85.0, 0);
|
||||
c.igTableSetupColumn("##input", 0, 0.0, 0);
|
||||
|
||||
_ = c.igTableNextRow(0, 0.0);
|
||||
|
||||
// Quality input
|
||||
_ = c.igTableSetColumnIndex(0);
|
||||
igu.sliceText("Quality");
|
||||
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
if (c.igInputText(
|
||||
"##quality_input",
|
||||
&state.quality_buf,
|
||||
state.quality_buf.len,
|
||||
c.ImGuiInputTextFlags_EnterReturnsTrue,
|
||||
null,
|
||||
null,
|
||||
)) {
|
||||
start = .channel_bar;
|
||||
}
|
||||
|
||||
var quality_popup_pos: c.ImVec2 = undefined;
|
||||
c.igGetItemRectMin(&quality_popup_pos);
|
||||
var quality_popup_size: c.ImVec2 = undefined;
|
||||
c.igGetItemRectSize(&quality_popup_size);
|
||||
|
||||
c.igSameLine(0.0, 0.0);
|
||||
if (c.igArrowButton("##open_quality_popup", c.ImGuiDir_Down)) {
|
||||
c.igOpenPopup_Str("quality_popup", 0);
|
||||
}
|
||||
// open popup on arrow button click
|
||||
c.igOpenPopupOnItemClick("quality_popup", 0);
|
||||
|
||||
var btn_size: c.ImVec2 = undefined;
|
||||
c.igGetItemRectSize(&btn_size);
|
||||
|
||||
const preset_qualities = [_][:0]const u8{
|
||||
"best",
|
||||
"1080p60",
|
||||
"720p60",
|
||||
"480p",
|
||||
"360p",
|
||||
"worst",
|
||||
"audio_only",
|
||||
};
|
||||
|
||||
quality_popup_pos.y += quality_popup_size.y;
|
||||
quality_popup_size.x += btn_size.x;
|
||||
quality_popup_size.y += 5 + (quality_popup_size.y * @as(f32, @floatFromInt(preset_qualities.len)));
|
||||
|
||||
c.igSetNextWindowPos(quality_popup_pos, c.ImGuiCond_Always, .{ .x = 0.0, .y = 0.0 });
|
||||
c.igSetNextWindowSize(quality_popup_size, c.ImGuiCond_Always);
|
||||
|
||||
if (c.igBeginPopup("quality_popup", c.ImGuiWindowFlags_NoMove)) {
|
||||
defer c.igEndPopup();
|
||||
|
||||
for (preset_qualities) |quality| {
|
||||
if (c.igSelectable_Bool(quality.ptr, false, 0, .{ .x = 0.0, .y = 0.0 })) {
|
||||
@memcpy(state.quality_buf[0..quality.len], quality);
|
||||
state.quality_buf[quality.len] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = c.igTableNextRow(0, 0.0);
|
||||
_ = c.igTableSetColumnIndex(0);
|
||||
igu.sliceText("Play Channel");
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
if (c.igInputText(
|
||||
"##play_channel_input",
|
||||
&state.channel_name_buf,
|
||||
state.channel_name_buf.len,
|
||||
c.ImGuiInputTextFlags_EnterReturnsTrue,
|
||||
null,
|
||||
null,
|
||||
)) {
|
||||
start = .channel_bar;
|
||||
}
|
||||
c.igSameLine(0.0, 0.0);
|
||||
if (c.igButton("Play!", .{ .x = 0.0, .y = 0.0 })) {
|
||||
start = .channel_bar;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.channels != null) {
|
||||
c.igBeginDisabled(state.live_status_loading);
|
||||
defer c.igEndDisabled();
|
||||
if (c.igButton("Refresh Status", .{ .x = 0.0, .y = 0.0 })) {
|
||||
(try std.Thread.spawn(.{}, @import("live.zig").reloadLiveThread, .{state}))
|
||||
.detach();
|
||||
}
|
||||
|
||||
c.igSameLine(0, 5.0);
|
||||
}
|
||||
|
||||
// Chatty checkbox
|
||||
_ = c.igCheckbox("Start Chatty", &state.chatty);
|
||||
|
||||
if (state.channels != null and c.igBeginChild_Str(
|
||||
"Quick Pick",
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
true,
|
||||
0,
|
||||
)) {
|
||||
_ = c.igBeginTable(
|
||||
"##qp_table",
|
||||
3,
|
||||
c.ImGuiTableFlags_Resizable,
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
0.0,
|
||||
);
|
||||
defer c.igEndTable();
|
||||
|
||||
c.igTableSetupColumn("Channel", 0, 0.0, 0);
|
||||
c.igTableSetupColumn("Comment", 0, 0.0, 0);
|
||||
c.igTableSetupColumn("Live?", c.ImGuiTableColumnFlags_WidthFixed, 80.0, 0);
|
||||
|
||||
c.igTableHeadersRow();
|
||||
_ = c.igTableSetColumnIndex(0);
|
||||
c.igTableHeader("Channel");
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
c.igTableHeader("Comment");
|
||||
_ = c.igTableSetColumnIndex(2);
|
||||
c.igTableHeader("Live?");
|
||||
|
||||
for (state.channels.?, 0..) |entry, i| {
|
||||
c.igPushID_Int(@intCast(i));
|
||||
defer c.igPopID();
|
||||
|
||||
_ = c.igTableNextRow(0, 0.0);
|
||||
_ = c.igTableSetColumnIndex(0);
|
||||
|
||||
switch (entry) {
|
||||
.channel => |ch| {
|
||||
var ch_buf: [256]u8 = undefined;
|
||||
const formatted = try std.fmt.bufPrintZ(
|
||||
&ch_buf,
|
||||
"{s}",
|
||||
.{ch.name},
|
||||
);
|
||||
|
||||
if (c.igSelectable_Bool(
|
||||
formatted.ptr,
|
||||
false,
|
||||
c.ImGuiSelectableFlags_SpanAllColumns,
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
)) {
|
||||
start = .{ .channels_idx = i };
|
||||
}
|
||||
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
|
||||
if (ch.comment) |comment| {
|
||||
igu.sliceText(comment);
|
||||
}
|
||||
|
||||
_ = c.igTableSetColumnIndex(2);
|
||||
|
||||
const live_color = switch (ch.live) {
|
||||
.loading => c.ImVec4{ .x = 1.0, .y = 1.0, .z = 0.0, .w = 1.0 },
|
||||
.live => c.ImVec4{ .x = 0.0, .y = 1.0, .z = 0.0, .w = 1.0 },
|
||||
.offline => c.ImVec4{ .x = 1.0, .y = 0.0, .z = 0.0, .w = 1.0 },
|
||||
.err => c.ImVec4{ .x = 0.8, .y = 0.0, .z = 0.0, .w = 1.0 },
|
||||
};
|
||||
const live_label = switch (ch.live) {
|
||||
.loading => "Loading...",
|
||||
.live => "Live",
|
||||
.offline => "Offline",
|
||||
.err => "Error",
|
||||
};
|
||||
|
||||
const prev_col = c.igGetStyle().*.Colors[c.ImGuiCol_Text];
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = live_color;
|
||||
igu.sliceText(live_label);
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = prev_col;
|
||||
},
|
||||
.separator => |heading| {
|
||||
if (heading) |h| {
|
||||
const spacer_size = c.ImVec2{ .x = 0.0, .y = 2.0 };
|
||||
|
||||
c.igDummy(spacer_size);
|
||||
const prev_col = c.igGetStyle().*.Colors[c.ImGuiCol_Text];
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = c.ImVec4{
|
||||
.x = 0.7,
|
||||
.y = 0.2,
|
||||
.z = 0.9,
|
||||
.w = 1.0,
|
||||
};
|
||||
igu.sliceText(h);
|
||||
c.igGetStyle().*.Colors[c.ImGuiCol_Text] = prev_col;
|
||||
|
||||
// TODO: is this the best way to do the alignment?
|
||||
c.igSeparator();
|
||||
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
c.igDummy(spacer_size);
|
||||
c.igDummy(c.ImVec2{ .x = 0.0, .y = c.igGetTextLineHeight() });
|
||||
c.igSeparator();
|
||||
|
||||
_ = c.igTableSetColumnIndex(2);
|
||||
c.igDummy(spacer_size);
|
||||
c.igDummy(c.ImVec2{ .x = 0.0, .y = c.igGetTextLineHeight() });
|
||||
c.igSeparator();
|
||||
} else {
|
||||
c.igSeparator();
|
||||
_ = c.igTableSetColumnIndex(1);
|
||||
c.igSeparator();
|
||||
_ = c.igTableSetColumnIndex(2);
|
||||
c.igSeparator();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.channels != null)
|
||||
c.igEndChild(); // END THE CHILD MWAAHAHA
|
||||
|
||||
if (state.streamlink_out) |out| {
|
||||
c.igSetNextWindowSize(.{ .x = 400.0, .y = 150.0 }, c.ImGuiCond_Appearing);
|
||||
var open = true;
|
||||
if (c.igBeginPopupModal(
|
||||
"Streamlink Crashed!",
|
||||
&open,
|
||||
c.ImGuiWindowFlags_Modal,
|
||||
)) {
|
||||
defer c.igEndPopup();
|
||||
if (c.igBeginChild_Str(
|
||||
"##output",
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
true,
|
||||
c.ImGuiWindowFlags_HorizontalScrollbar,
|
||||
))
|
||||
igu.sliceText(out);
|
||||
c.igEndChild();
|
||||
} else {
|
||||
c.igOpenPopup_Str("Streamlink Crashed!", 0);
|
||||
}
|
||||
|
||||
if (!open) {
|
||||
state.freeStreamlinkMemfd();
|
||||
}
|
||||
}
|
||||
|
||||
if (start == .channel_bar and state.channel_name_buf[0] == 0) {
|
||||
std.log.warn("Tried to start an empty stream!", .{});
|
||||
start = .none;
|
||||
}
|
||||
|
||||
switch (start) {
|
||||
.none => {},
|
||||
.channel_bar => {
|
||||
c.glfwHideWindow(state.win);
|
||||
try launch.launchChildren(
|
||||
state,
|
||||
std.mem.sliceTo(&state.channel_name_buf, 0),
|
||||
);
|
||||
},
|
||||
.channels_idx => |idx| {
|
||||
c.glfwHideWindow(state.win);
|
||||
try launch.launchChildren(state, state.channels.?[idx].channel.name);
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
|
||||
pub fn sliceText(text: []const u8) void {
|
||||
c.igTextUnformatted(text.ptr, text.ptr + text.len);
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
const State = @import("State.zig");
|
||||
const log = std.log.scoped(.launch);
|
||||
|
||||
pub fn launchChildren(state: *State, channel: []const u8) !void {
|
||||
log.info(
|
||||
"starting for channel {s} with quality {s} (chatty: {})",
|
||||
.{ channel, std.mem.sliceTo(&state.quality_buf, 0), state.chatty },
|
||||
);
|
||||
|
||||
// just to be safe...
|
||||
state.freeStreamlinkMemfd();
|
||||
|
||||
if (state.chatty and !state.chatty_alive) {
|
||||
var chatty_arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
|
||||
const channel_d = try std.ascii.allocLowerString(chatty_arena.allocator(), channel);
|
||||
const chatty_argv = try chatty_arena.allocator().dupe(
|
||||
[]const u8,
|
||||
&.{ "chatty", "-connect", "-channel", channel_d },
|
||||
);
|
||||
const chatty_child = std.ChildProcess.init(chatty_argv, std.heap.c_allocator);
|
||||
|
||||
const chatty_thread = try std.Thread.spawn(
|
||||
.{},
|
||||
chattyThread,
|
||||
.{ state, chatty_child, chatty_arena },
|
||||
);
|
||||
chatty_thread.detach();
|
||||
}
|
||||
|
||||
const channel_d = try std.heap.c_allocator.dupe(u8, channel);
|
||||
const streamlink_thread = try std.Thread.spawn(
|
||||
.{},
|
||||
streamlinkThread,
|
||||
.{ state, channel_d },
|
||||
);
|
||||
streamlink_thread.detach();
|
||||
}
|
||||
|
||||
fn streamlinkThread(state: *State, channel: []const u8) !void {
|
||||
defer std.heap.c_allocator.free(channel);
|
||||
errdefer {
|
||||
state.mutex.lock();
|
||||
defer state.mutex.unlock();
|
||||
|
||||
c.glfwShowWindow(state.win);
|
||||
}
|
||||
|
||||
const memfd = try std.os.memfd_create("streamlink_out", 0);
|
||||
errdefer std.os.close(memfd);
|
||||
const memfile = std.fs.File{ .handle = memfd };
|
||||
|
||||
var arg_arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
|
||||
defer arg_arena.deinit();
|
||||
const pid = spawn: {
|
||||
state.mutex.lock();
|
||||
defer state.mutex.unlock();
|
||||
|
||||
var ch_buf: [128]u8 = undefined;
|
||||
const lower_channel = std.ascii.lowerString(&ch_buf, channel);
|
||||
|
||||
const url = try std.fmt.allocPrintZ(arg_arena.allocator(), "https://twitch.tv/{s}", .{lower_channel});
|
||||
const quality = try arg_arena.allocator().dupeZ(u8, std.mem.sliceTo(&state.quality_buf, 0));
|
||||
|
||||
const streamlink_argv = try arg_arena.allocator().allocSentinel(
|
||||
?[*:0]const u8,
|
||||
3,
|
||||
null,
|
||||
);
|
||||
|
||||
streamlink_argv[0] = "streamlink";
|
||||
streamlink_argv[1] = url;
|
||||
streamlink_argv[2] = quality;
|
||||
|
||||
// Doing it the C way because zig's ChildProcess ain't got this
|
||||
const pid = try std.os.fork();
|
||||
if (pid == 0) {
|
||||
try std.os.dup2(memfd, 1);
|
||||
try std.os.dup2(memfd, 2);
|
||||
return std.os.execvpeZ(streamlink_argv[0].?, streamlink_argv, std.c.environ);
|
||||
}
|
||||
|
||||
break :spawn pid;
|
||||
};
|
||||
|
||||
var success = std.os.waitpid(pid, 0).status == 0;
|
||||
|
||||
var size = (try memfile.stat()).size;
|
||||
if (size == 0) {
|
||||
try memfile.writeAll("<no output>");
|
||||
size = (try memfile.stat()).size;
|
||||
}
|
||||
|
||||
const mem = try std.os.mmap(
|
||||
null,
|
||||
size,
|
||||
std.os.PROT.READ,
|
||||
.{ .TYPE = .PRIVATE },
|
||||
memfd,
|
||||
0,
|
||||
);
|
||||
|
||||
// If the stream ends, this silly program still exits with a non-zero status.
|
||||
success = success or std.mem.containsAtLeast(u8, mem, 1, "Stream ended");
|
||||
|
||||
state.mutex.lock();
|
||||
defer state.mutex.unlock();
|
||||
|
||||
if (success) {
|
||||
std.os.munmap(mem);
|
||||
log.info("streamlink exited successfully, closing.", .{});
|
||||
c.glfwSetWindowShouldClose(state.win, 1);
|
||||
} else {
|
||||
state.streamlink_memfd = memfile;
|
||||
state.streamlink_out = mem;
|
||||
c.glfwShowWindow(state.win);
|
||||
}
|
||||
}
|
||||
|
||||
fn chattyThread(state: *State, child: std.ChildProcess, arena: std.heap.ArenaAllocator) !void {
|
||||
// no need to get the mutex here, chatty_alive is atomic
|
||||
@atomicStore(bool, &state.chatty_alive, true, .Unordered);
|
||||
defer @atomicStore(bool, &state.chatty_alive, false, .Unordered);
|
||||
|
||||
var ch = child;
|
||||
defer arena.deinit();
|
||||
_ = ch.spawnAndWait() catch |e| {
|
||||
std.log.err("Spawning Chatty: {!}", .{e});
|
||||
};
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
const std = @import("std");
|
||||
const State = @import("State.zig");
|
||||
const c = @import("ffi.zig").c;
|
||||
const log = std.log.scoped(.live);
|
||||
|
||||
pub fn reloadLiveThread(s: *State) !void {
|
||||
{
|
||||
s.mutex.lock();
|
||||
defer s.mutex.unlock();
|
||||
|
||||
for (s.channels.?) |*chan| {
|
||||
switch (chan.*) {
|
||||
.channel => |*ch| ch.live = .loading,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tryFetchChannelsLive(s);
|
||||
}
|
||||
|
||||
pub fn tryFetchChannelsLive(s: *State) void {
|
||||
fetchChannelsLive(s) catch |e| {
|
||||
log.err("fetching status: {}", .{e});
|
||||
|
||||
s.mutex.lock();
|
||||
defer s.mutex.unlock();
|
||||
|
||||
for (s.channels.?) |*chan| {
|
||||
switch (chan.*) {
|
||||
.channel => |*ch| ch.live = .err,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn fetchChannelsLive(s: *State) !void {
|
||||
@atomicStore(bool, &s.live_status_loading, true, .Unordered);
|
||||
defer @atomicStore(bool, &s.live_status_loading, false, .Unordered);
|
||||
log.info("initiaizing cURL", .{});
|
||||
const curl = c.curl_easy_init();
|
||||
if (curl == null)
|
||||
return error.CurlInitError;
|
||||
defer c.curl_easy_cleanup(curl);
|
||||
|
||||
try handleCurlErr(c.curl_easy_setopt(
|
||||
curl,
|
||||
c.CURLOPT_WRITEFUNCTION,
|
||||
&curlWriteCb,
|
||||
));
|
||||
try handleCurlErr(c.curl_easy_setopt(curl, c.CURLOPT_NOPROGRESS, @as(c_long, 1)));
|
||||
try handleCurlErr(c.curl_easy_setopt(curl, c.CURLOPT_FOLLOWLOCATION, @as(c_long, 1)));
|
||||
|
||||
// the twitch info grabbinator works by downloading the web page
|
||||
// and checking if it contains a string. this is the bufffer for the page.
|
||||
//
|
||||
// Fuck you, twitch! amazing API design!
|
||||
var page_buf = std.ArrayList(u8).init(std.heap.c_allocator);
|
||||
defer page_buf.deinit();
|
||||
|
||||
try handleCurlErr(c.curl_easy_setopt(curl, c.CURLOPT_WRITEDATA, &page_buf));
|
||||
|
||||
// we shouldn't need to aquire the mutex here, this data isnt being read and we're
|
||||
// only doing atomic writes.
|
||||
var fmt_buf: [512]u8 = undefined;
|
||||
for (s.channels.?) |*entry| {
|
||||
const chan = if (entry.* == .channel) &entry.channel else continue;
|
||||
|
||||
log.info("requesting live state for channel {s}", .{chan.name});
|
||||
|
||||
const url = try std.fmt.bufPrintZ(
|
||||
&fmt_buf,
|
||||
"https://www.twitch.tv/{s}",
|
||||
.{chan.name},
|
||||
);
|
||||
try handleCurlErr(c.curl_easy_setopt(curl, c.CURLOPT_URL, url.ptr));
|
||||
|
||||
var tries: u8 = 3;
|
||||
while (tries > 0) : (tries -= 1) {
|
||||
page_buf.clearRetainingCapacity();
|
||||
|
||||
try handleCurlErr(c.curl_easy_perform(curl));
|
||||
|
||||
var response: c_long = 0;
|
||||
try handleCurlErr(c.curl_easy_getinfo(curl, c.CURLINFO_RESPONSE_CODE, &response));
|
||||
|
||||
if (response != 200) {
|
||||
log.warn(
|
||||
"got error response {}, retrying ({} tries left)",
|
||||
.{ response, tries },
|
||||
);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (tries == 0) {
|
||||
@atomicStore(State.Live, &chan.live, .err, .Unordered);
|
||||
}
|
||||
if (std.mem.containsAtLeast(u8, page_buf.items, 1, "live_user")) {
|
||||
@atomicStore(State.Live, &chan.live, .live, .Unordered);
|
||||
} else {
|
||||
@atomicStore(State.Live, &chan.live, .offline, .Unordered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn curlWriteCb(
|
||||
data: [*]const u8,
|
||||
size: usize,
|
||||
nmemb: usize,
|
||||
out: *std.ArrayList(u8),
|
||||
) callconv(.C) usize {
|
||||
const realsize = size * nmemb;
|
||||
out.writer().writeAll(data[0..realsize]) catch return 0;
|
||||
return realsize;
|
||||
}
|
||||
|
||||
fn handleCurlErr(code: c.CURLcode) !void {
|
||||
if (code != c.CURLE_OK) {
|
||||
log.err("Curl error: {s}", .{c.curl_easy_strerror(code)});
|
||||
return error.CurlError;
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
const gui = @import("gui.zig");
|
||||
const State = @import("State.zig");
|
||||
const log = std.log.scoped(.main);
|
||||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
log.info("initializing GLFW", .{});
|
||||
_ = c.glfwSetErrorCallback(&glfwErrorCb);
|
||||
if (c.glfwInit() == 0) {
|
||||
return error.GlfwInit;
|
||||
}
|
||||
|
||||
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
c.glfwWindowHint(c.GLFW_TRANSPARENT_FRAMEBUFFER, c.GLFW_TRUE);
|
||||
|
||||
log.info("creating window", .{});
|
||||
const win = c.glfwCreateWindow(500, 500, "playtwitch", null, null);
|
||||
defer c.glfwTerminate();
|
||||
|
||||
c.glfwMakeContextCurrent(win);
|
||||
c.glfwSwapInterval(1);
|
||||
|
||||
log.info("initializing GLEW", .{});
|
||||
const glew_err = c.glewInit();
|
||||
if (glew_err != c.GLEW_OK) {
|
||||
std.log.err("GLEW init error: {s}", .{c.glewGetErrorString(glew_err)});
|
||||
return error.GlewInit;
|
||||
}
|
||||
|
||||
log.info("initializing ImGui", .{});
|
||||
const ctx = c.igCreateContext(null);
|
||||
defer c.igDestroyContext(ctx);
|
||||
|
||||
const io = c.igGetIO();
|
||||
io.*.ConfigFlags |= c.ImGuiConfigFlags_NavEnableKeyboard;
|
||||
io.*.IniFilename = null;
|
||||
io.*.LogFilename = null;
|
||||
|
||||
_ = c.ImGui_ImplGlfw_InitForOpenGL(win, true);
|
||||
defer c.ImGui_ImplGlfw_Shutdown();
|
||||
|
||||
_ = c.ImGui_ImplOpenGL3_Init("#version 330 core");
|
||||
defer c.ImGui_ImplOpenGL3_Shutdown();
|
||||
|
||||
c.igStyleColorsDark(null);
|
||||
@import("theme.zig").loadTheme(&c.igGetStyle().*.Colors);
|
||||
const font = try @import("theme.zig").loadFont();
|
||||
|
||||
const state = try State.init(win.?);
|
||||
defer state.deinit();
|
||||
|
||||
while (c.glfwWindowShouldClose(win) == 0) {
|
||||
if (c.glfwGetWindowAttrib(win, c.GLFW_VISIBLE) == 0)
|
||||
continue;
|
||||
|
||||
c.glfwPollEvents();
|
||||
|
||||
var win_width: c_int = 0;
|
||||
var win_height: c_int = 0;
|
||||
c.glfwGetWindowSize(win, &win_width, &win_height);
|
||||
|
||||
c.ImGui_ImplOpenGL3_NewFrame();
|
||||
c.ImGui_ImplGlfw_NewFrame();
|
||||
c.igNewFrame();
|
||||
if (font) |f|
|
||||
c.igPushFont(f);
|
||||
|
||||
const win_visible = c.igBegin(
|
||||
"##main_win",
|
||||
null,
|
||||
c.ImGuiWindowFlags_NoMove |
|
||||
c.ImGuiWindowFlags_NoResize |
|
||||
c.ImGuiWindowFlags_NoDecoration |
|
||||
c.ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
c.ImGuiWindowFlags_NoNavFocus,
|
||||
);
|
||||
|
||||
c.igSetWindowPos_Vec2(
|
||||
.{ .x = 0.0, .y = 0.0 },
|
||||
c.ImGuiCond_Always,
|
||||
);
|
||||
c.igSetWindowSize_Vec2(
|
||||
.{ .x = @floatFromInt(win_width), .y = @floatFromInt(win_height) },
|
||||
c.ImGuiCond_Always,
|
||||
);
|
||||
|
||||
if (win_visible) {
|
||||
try gui.winContent(state);
|
||||
}
|
||||
|
||||
if (font != null)
|
||||
c.igPopFont();
|
||||
|
||||
c.igEnd();
|
||||
|
||||
c.igEndFrame();
|
||||
|
||||
c.glViewport(0, 0, win_width, win_height);
|
||||
c.glClear(c.GL_COLOR_BUFFER_BIT);
|
||||
c.glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
c.igRender();
|
||||
c.ImGui_ImplOpenGL3_RenderDrawData(c.igGetDrawData());
|
||||
c.glfwSwapBuffers(win);
|
||||
}
|
||||
}
|
||||
|
||||
fn glfwErrorCb(e: c_int, d: [*c]const u8) callconv(.C) void {
|
||||
log.err("GLFW error {d}: {s}", .{ e, d });
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
const log = std.log.scoped(.theme);
|
||||
|
||||
pub fn loadTheme(colors: [*]c.ImVec4) void {
|
||||
log.info("loading theme", .{});
|
||||
|
||||
colors[c.ImGuiCol_ButtonHovered] = c.ImVec4{ .x = 0.7, .y = 0.49, .z = 0.9, .w = 1.0 };
|
||||
colors[c.ImGuiCol_Button] = c.ImVec4{ .x = 0.33, .y = 0.14, .z = 0.51, .w = 1.0 };
|
||||
colors[c.ImGuiCol_ChildBg] = c.ImVec4{ .x = 0.1, .y = 0.0, .z = 0.2, .w = 0.85 };
|
||||
colors[c.ImGuiCol_FrameBg] = c.ImVec4{ .x = 0.45, .y = 0.2, .z = 0.69, .w = 1.0 };
|
||||
colors[c.ImGuiCol_HeaderHovered] = c.ImVec4{ .x = 0.45, .y = 0.2, .z = 0.69, .w = 1.0 };
|
||||
colors[c.ImGuiCol_Header] = c.ImVec4{ .x = 0.26, .y = 0.1, .z = 0.43, .w = 1.0 };
|
||||
colors[c.ImGuiCol_TableHeaderBg] = c.ImVec4{ .x = 0.45, .y = 0.2, .z = 0.69, .w = 0.8 };
|
||||
colors[c.ImGuiCol_TitleBgActive] = c.ImVec4{ .x = 0.33, .y = 0.14, .z = 0.51, .w = 1.0 };
|
||||
colors[c.ImGuiCol_WindowBg] = c.ImVec4{ .x = 0.12, .y = 0.0, .z = 0.23, .w = 0.8 };
|
||||
}
|
||||
|
||||
pub fn loadFont() !?*c.ImFont {
|
||||
log.info("loading fonts", .{});
|
||||
|
||||
const fonts = [_][:0]const u8{
|
||||
"/usr/share/fonts/TTF/IosevkaNerdFont-Regular.ttf",
|
||||
"/usr/share/fonts/noto/NotoSans-Regular.ttf",
|
||||
};
|
||||
|
||||
for (fonts) |font| {
|
||||
const found = if (std.fs.accessAbsolute(font, .{})) |_|
|
||||
true
|
||||
else |e| if (e == error.FileNotFound)
|
||||
false
|
||||
else
|
||||
return e;
|
||||
|
||||
if (found) {
|
||||
log.info("using font {s}", .{font});
|
||||
return c.ImFontAtlas_AddFontFromFileTTF(
|
||||
c.igGetIO().*.Fonts,
|
||||
font.ptr,
|
||||
16,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
|
@ -11,6 +11,8 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.{
|
||||
.name = "playvid",
|
||||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
},
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub fn main() !u8 {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
|
|
|
@ -14,6 +14,7 @@ pub fn build(b: *std.Build) void {
|
|||
exe.linkLibC();
|
||||
exe.linkSystemLibrary("libgit2");
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
exe.root_module.addImport("ansi-term", b.dependency("ansi_term", .{}).module("ansi-term"));
|
||||
exe.root_module.addImport("known-folders", b.dependency("known_folders", .{}).module("known-folders"));
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
.{
|
||||
.name = "promt",
|
||||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
.ansi_term = .{
|
||||
.url = "git+https://github.com/LordMZTE/ansi-term.git#73c03175068679685535111dbea72cade075719e",
|
||||
.hash = "1220ea86ace34b38e49c1d737c5f857d88346af10695a992b38e10cb0a73b6a19ef7",
|
||||
|
@ -12,5 +14,4 @@
|
|||
.hash = "12209925016f4b5486a713828ead3bcc900fa4f039c93de1894aa7d5253f7633b92c",
|
||||
},
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
|
|
|
@ -13,6 +13,11 @@ const fish_code =
|
|||
\\end
|
||||
;
|
||||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
if (std.os.argv.len < 2)
|
||||
return error.NotEnoughArguments;
|
||||
|
|
|
@ -15,6 +15,8 @@ pub fn build(b: *std.Build) void {
|
|||
exe.linkSystemLibrary("xcb");
|
||||
exe.linkSystemLibrary("xcb-xinerama");
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.{
|
||||
.name = "randomwallpaper",
|
||||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
},
|
||||
}
|
|
@ -4,6 +4,7 @@ const Walker = @import("Walker.zig");
|
|||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub fn main() !u8 {
|
||||
|
|
|
@ -16,6 +16,7 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = mode,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
exe.root_module.addImport("wayland", wayland_mod);
|
||||
|
||||
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
.wayland = .{
|
||||
.url = "git+https://git.mzte.de/LordMZTE/zig-wayland#4de9f2d6d5fddae1fbb29e21fd1dae69e646ab7d",
|
||||
.hash = "1220d6448c277e5c41348aa95ce2ba2fc92a92cb7a9e9783edf0f816cd0260122d31",
|
||||
|
|
|
@ -4,6 +4,7 @@ const ClipboardConnection = @import("ClipboardConnection.zig");
|
|||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
|
|
|
@ -11,6 +11,8 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.{
|
||||
.name = "withjava",
|
||||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
},
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub fn main() !u8 {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
|
|
|
@ -16,6 +16,7 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("common", b.dependency("common", .{}).module("common"));
|
||||
exe.root_module.addImport("xev", b.dependency("xev", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.common = .{ .path = "../../lib/common-zig" },
|
||||
.wayland = .{
|
||||
.url = "git+https://git.mzte.de/LordMZTE/zig-wayland#4de9f2d6d5fddae1fbb29e21fd1dae69e646ab7d",
|
||||
.hash = "1220d6448c277e5c41348aa95ce2ba2fc92a92cb7a9e9783edf0f816cd0260122d31",
|
||||
},
|
||||
.xev = .{
|
||||
.url = "git+https://github.com/mitchellh/libxev#2494978bedec01a856e8f3c47a33148ad1755fab",
|
||||
.hash = "12203de3dbf9c235903c71122448ef031622ba8d64b250205934e2a65018b92b9717",
|
||||
.url = "git+https://github.com/mitchellh/libxev#418cac65473668b02cc5a85194042bdaf04acd00",
|
||||
.hash = "12203116ff408eb48f81c947dfeb06f7feebf6a9efa962a560ac69463098b2c04a96",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ const Collector = col: {
|
|||
}};
|
||||
}
|
||||
break :col @Type(.{ .Struct = .{
|
||||
.layout = .Auto,
|
||||
.layout = .auto,
|
||||
.fields = fields,
|
||||
.decls = &.{},
|
||||
.is_tuple = false,
|
||||
|
|
|
@ -18,6 +18,7 @@ const zxdg = wayland.client.zxdg;
|
|||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .debug,
|
||||
.logFn = @import("common").logFn,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
(install-zig "scripts/mzteinit")
|
||||
(install-zig "scripts/mzteriver")
|
||||
(install-zig "scripts/openbrowser")
|
||||
(install-zig "scripts/playtwitch")
|
||||
(install-zig "scripts/playvid")
|
||||
(install-zig "scripts/prompt")
|
||||
(install-zig "scripts/randomwallpaper")
|
||||
|
|
Loading…
Reference in New Issue