dotfiles/scripts/randomwallpaper/src/main.zig

159 lines
4.5 KiB
Zig

const std = @import("std");
const xinerama = @import("xinerama.zig");
const Walker = @import("Walker.zig");
pub const std_options = std.Options{
.log_level = .debug,
.logFn = @import("common").logFn,
};
pub fn main() !u8 {
const alloc = std.heap.c_allocator;
const home_s = std.os.getenv("HOME") orelse return error.HomeNotSet;
const runtime_dir = std.os.getenv("XDG_RUNTIME_DIR") orelse return error.MissingRuntimeDir;
var walker = Walker.init(alloc);
defer walker.deinit();
try walker.walk(
try std.fs.openDirAbsolute(
"/usr/share/backgrounds/",
.{ .iterate = true },
),
);
try walkLocalWps(&walker, home_s);
const swww_socket_path = try std.fs.path.join(alloc, &.{ runtime_dir, "swww.socket" });
defer alloc.free(swww_socket_path);
const has_swww = if (std.fs.cwd().statFile(swww_socket_path)) |_| true else |e| switch (e) {
error.FileNotFound => false,
else => return e,
};
if (has_swww) {
std.log.info("found running swww daemon, using swww backend", .{});
return try setWallpapersSwww(alloc, walker.files.items);
} else {
std.log.info("using X/feh backend", .{});
return try setWallpapersX(alloc, walker.files.items);
}
}
fn walkLocalWps(walker: *Walker, home_s: []const u8) !void {
const wp_path = try std.fs.path.join(walker.files.allocator, &.{ home_s, ".local/share/backgrounds" });
defer walker.files.allocator.free(wp_path);
var local_wp = std.fs.cwd().openDir(wp_path, .{ .iterate = true }) catch |e| switch (e) {
error.FileNotFound => {
std.log.warn(
"No local wallpaper directory @ {s}, skipping local wallpapers",
.{wp_path},
);
return;
},
else => return e,
};
defer local_wp.close();
try walker.walk(local_wp);
}
fn setWallpapersSwww(alloc: std.mem.Allocator, wps: []const []const u8) !u8 {
const exec_res = try std.process.Child.run(.{
.allocator = alloc,
.argv = &.{ "swww", "query" },
});
defer alloc.free(exec_res.stdout);
defer alloc.free(exec_res.stderr);
if (!std.meta.eql(exec_res.term, .{ .Exited = 0 }))
return error.SwwwQuery;
var prng = std.rand.DefaultPrng.init(std.crypto.random.int(u64));
const rand = prng.random();
var output_iter = std.mem.tokenizeScalar(u8, exec_res.stdout, '\n');
while (output_iter.next()) |line| {
if (line.len == 0) continue;
const output = std.mem.sliceTo(line, ':');
const argv = [_][]const u8{
"swww",
"img",
wps[rand.uintAtMost(usize, wps.len - 1)],
"--outputs",
output,
"--transition-type",
"wipe",
"--transition-angle",
"30",
"--transition-bezier",
"0.8,0.0,0.2,1.0",
"--transition-fps",
"60",
"--transition-duration",
"2",
};
var child = std.process.Child.init(&argv, alloc);
const term = try child.spawnAndWait();
const code = switch (term) {
.Exited => |n| n,
.Signal,
.Stopped,
.Unknown,
=> |n| b: {
std.log.err("Child borked with code {}", .{n});
break :b 1;
},
};
if (code != 0) return code;
}
return 0;
}
fn setWallpapersX(alloc: std.mem.Allocator, wps: []const []const u8) !u8 {
const screens = try xinerama.getHeadCount();
const feh_baseargs = [_][]const u8{
"feh",
"--no-fehbg",
"--bg-fill",
};
var feh_argv = try alloc.alloc(
[]const u8,
feh_baseargs.len + @as(usize, @intCast(screens)),
);
defer alloc.free(feh_argv);
@memcpy(feh_argv[0..feh_baseargs.len], &feh_baseargs);
var prng = std.rand.DefaultPrng.init(std.crypto.random.int(u64));
const rand = prng.random();
var i: u31 = 0;
while (i < screens) : (i += 1) {
const idx = rand.uintAtMost(usize, wps.len - 1);
feh_argv[feh_baseargs.len + i] = wps[idx];
}
std.log.info("feh argv: {s}", .{feh_argv});
var child = std.process.Child.init(feh_argv, alloc);
const term = try child.spawnAndWait();
return switch (term) {
.Exited => |n| n,
.Signal,
.Stopped,
.Unknown,
=> |n| b: {
std.log.err("Child borked with code {}", .{n});
break :b 1;
},
};
}