Add support for per-host user configuration
This uses KDL properties, i.e. "host=<hostname>" and can be applied to
any config type. An example is includes in examples/config.kdl.
```kdl
wallpaper_image_path "~/Pictures/desktop.png" host="desktop"
wallpaper_image_path "~/Pictures/laptop.png" host="laptop"
```
This commit is contained in:
parent
e29c4d01e1
commit
0b7e15d7ed
4 changed files with 63 additions and 12 deletions
|
|
@ -10,7 +10,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
||||
These are in rough order of my priority, though no promises I do them in this order.
|
||||
|
||||
- [ ] Support per-host config using properties
|
||||
- [ ] Implement an optional clock bar
|
||||
- [ ] Implement a rivertile clone
|
||||
- [ ] Support overriding config location
|
||||
|
|
@ -31,3 +30,4 @@ These are in rough order of my priority, though no promises I do them in this or
|
|||
- [x] Implement runtime log levels
|
||||
- [x] Add input configuration, i.e. pointer acceleration and that type of thing
|
||||
- [x] Support `None` modifier for keybinds (needed for media/brightness keys)
|
||||
- [x] Support per-host config using properties
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
.hash = "xkbcommon-0.4.0-dev-VDqIe0y2AgCNeWLthDZ3MUcUYzhyKXjK85ISm_zxk9Nk",
|
||||
},
|
||||
.kdl = .{
|
||||
.url = "https://codeberg.org/desttinghim/zig-kdl/archive/9a92d2cc6bb25031778d321c6c1d87e9e4052eab.tar.gz",
|
||||
.hash = "kdl-0.0.0-8rilEMFEAQCYVNhFIcJZWp8HLrjYaEIZGov6CSH05Dsv",
|
||||
.url = "https://codeberg.org/bwbuhse/zig-kdl/archive/13d9d247324f79b854187d6becc47fffdf7fea3b.tar.gz",
|
||||
.hash = "kdl-0.0.0-8rilEKdHAQC_NOLDNu3Ts6kJT8uqqJvrPduFScEjSm_g",
|
||||
},
|
||||
.known_folders = .{
|
||||
.url = "https://github.com/ziglibs/known-folders/archive/83d39161eac2ed6f37ad3cb4d9dd518696ce90bb.tar.gz",
|
||||
|
|
|
|||
|
|
@ -91,4 +91,8 @@ input "PIXA3854:00 093A:0274 Touchpad" {
|
|||
natural_scroll "enabled"
|
||||
tap "disabled"
|
||||
}
|
||||
// Per-host config using the host= property
|
||||
// Nodes with a host property are only applied when the hostname matches
|
||||
wallpaper_image_path "~/Pictures/desktop.png" host="desktop"
|
||||
wallpaper_image_path "~/Pictures/laptop.png" host="laptop"
|
||||
|
||||
|
|
|
|||
|
|
@ -194,11 +194,17 @@ pub fn destroy(config: *Config) void {
|
|||
utils.allocator.destroy(config);
|
||||
}
|
||||
|
||||
// TODO: Support kdl properties to specific the hostname the config should affect
|
||||
fn load(config: *Config, reader: *Io.Reader) !void {
|
||||
var parser = try kdl.Parser.init(utils.allocator, reader, .{});
|
||||
defer parser.deinit(utils.allocator);
|
||||
|
||||
const hostname = blk: {
|
||||
var uname = std.posix.uname();
|
||||
const hostname = mem.sliceTo(&uname.nodename, 0);
|
||||
if (hostname.len == 0) break :blk null;
|
||||
break :blk hostname;
|
||||
};
|
||||
|
||||
var next_child_block: ?NodeName = null;
|
||||
var pending_input_name: ?[]const u8 = null;
|
||||
defer if (pending_input_name) |n| utils.allocator.free(n);
|
||||
|
|
@ -217,6 +223,10 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
|||
// If it's a node, we check if it's a valid NodeName
|
||||
const node_name = std.meta.stringToEnum(NodeName, node.name);
|
||||
if (node_name) |name| {
|
||||
if (!hostMatches(node, &parser, hostname)) {
|
||||
logDebugHostMismatch(name);
|
||||
continue;
|
||||
}
|
||||
// Next, we have to check the specifics for the NodeName
|
||||
switch (name) {
|
||||
.attach_mode => {
|
||||
|
|
@ -286,11 +296,11 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
|||
.child_block_begin => {
|
||||
if (next_child_block) |child_block| {
|
||||
switch (child_block) {
|
||||
.borders => try config.loadBordersChildBlock(&parser),
|
||||
.keybinds => try config.loadKeybindsChildBlock(&parser),
|
||||
.pointer_binds => try config.loadPointerBindsChildBlock(&parser),
|
||||
.borders => try config.loadBordersChildBlock(&parser, hostname),
|
||||
.keybinds => try config.loadKeybindsChildBlock(&parser, hostname),
|
||||
.pointer_binds => try config.loadPointerBindsChildBlock(&parser, hostname),
|
||||
.input => {
|
||||
try config.loadInputChildBlock(&parser, pending_input_name);
|
||||
try config.loadInputChildBlock(&parser, pending_input_name, hostname);
|
||||
pending_input_name = null; // ownership transferred
|
||||
},
|
||||
else => {
|
||||
|
|
@ -308,13 +318,17 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
|||
}
|
||||
}
|
||||
|
||||
fn loadBordersChildBlock(config: *Config, parser: *kdl.Parser) !void {
|
||||
fn loadBordersChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
||||
while (try parser.next()) |event| {
|
||||
switch (event) {
|
||||
.node => |node| {
|
||||
// If it's a node, we check if it's a valid NodeName
|
||||
const node_name = std.meta.stringToEnum(BorderNodeName, node.name);
|
||||
if (node_name) |name| {
|
||||
if (!hostMatches(node, parser, hostname)) {
|
||||
logDebugHostMismatch(name);
|
||||
continue;
|
||||
}
|
||||
switch (name) {
|
||||
.width => {
|
||||
const width_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
|
||||
|
|
@ -357,10 +371,14 @@ fn loadBordersChildBlock(config: *Config, parser: *kdl.Parser) !void {
|
|||
}
|
||||
}
|
||||
|
||||
fn loadKeybindsChildBlock(config: *Config, parser: *kdl.Parser) !void {
|
||||
fn loadKeybindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
||||
while (try parser.next()) |event| {
|
||||
switch (event) {
|
||||
.node => |node| {
|
||||
if (!hostMatches(node, parser, hostname)) {
|
||||
logDebugHostMismatch(node.name);
|
||||
continue;
|
||||
}
|
||||
// tag_bind is a special case node name
|
||||
if (mem.eql(u8, node.name, "tag_bind")) {
|
||||
const mod_str = utils.stripQuotes(node.arg(parser, 0) orelse {
|
||||
|
|
@ -516,12 +534,16 @@ fn loadKeybindsChildBlock(config: *Config, parser: *kdl.Parser) !void {
|
|||
}
|
||||
}
|
||||
|
||||
fn loadPointerBindsChildBlock(config: *Config, parser: *kdl.Parser) !void {
|
||||
fn loadPointerBindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
||||
while (try parser.next()) |event| {
|
||||
switch (event) {
|
||||
.node => |node| {
|
||||
const node_name = std.meta.stringToEnum(PointerBindNodeName, node.name);
|
||||
if (node_name) |name| {
|
||||
if (!hostMatches(node, parser, hostname)) {
|
||||
logDebugHostMismatch(name);
|
||||
continue;
|
||||
}
|
||||
// Parse modifiers (arg 0)
|
||||
const mod_str = utils.stripQuotes(node.arg(parser, 0) orelse {
|
||||
logWarnMissingNodeArg(name, "modifier(s)");
|
||||
|
|
@ -568,7 +590,7 @@ fn loadPointerBindsChildBlock(config: *Config, parser: *kdl.Parser) !void {
|
|||
}
|
||||
}
|
||||
|
||||
fn loadInputChildBlock(config: *Config, parser: *kdl.Parser, name: ?[]const u8) !void {
|
||||
fn loadInputChildBlock(config: *Config, parser: *kdl.Parser, name: ?[]const u8, hostname: ?[]const u8) !void {
|
||||
var input_config: InputConfig = .{ .name = name };
|
||||
errdefer if (input_config.name) |n| utils.allocator.free(n);
|
||||
|
||||
|
|
@ -577,6 +599,10 @@ fn loadInputChildBlock(config: *Config, parser: *kdl.Parser, name: ?[]const u8)
|
|||
.node => |node| {
|
||||
const node_name = std.meta.stringToEnum(InputConfigNodeName, node.name);
|
||||
if (node_name) |tag| {
|
||||
if (!hostMatches(node, parser, hostname)) {
|
||||
logDebugHostMismatch(tag);
|
||||
continue;
|
||||
}
|
||||
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse {
|
||||
logWarnMissingNodeArg(tag, "value");
|
||||
continue;
|
||||
|
|
@ -745,6 +771,18 @@ fn logWarnMissingChildBlock(child_block: anytype) void {
|
|||
}
|
||||
}
|
||||
|
||||
fn logDebugHostMismatch(node_name: anytype) void {
|
||||
const node_name_type = @TypeOf(node_name);
|
||||
switch (node_name_type) {
|
||||
NodeName => log.debug("Skipping \"{s}\" (host mismatch)", .{@tagName(node_name)}),
|
||||
BorderNodeName => log.debug("Skipping \"border.{s}\" (host mismatch)", .{@tagName(node_name)}),
|
||||
PointerBindNodeName => log.debug("Skipping \"pointer_binds.{s}\" (host mismatch)", .{@tagName(node_name)}),
|
||||
InputConfigNodeName => log.debug("Skipping \"input.{s}\" (host mismatch)", .{@tagName(node_name)}),
|
||||
[]const u8 => log.debug("Skipping \"keybind.{s}\" (host mismatch)", .{node_name}),
|
||||
else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""),
|
||||
}
|
||||
}
|
||||
|
||||
fn logDebugSettingNode(node_name: anytype, node_value: []const u8) void {
|
||||
const node_name_type = @TypeOf(node_name);
|
||||
switch (node_name_type) {
|
||||
|
|
@ -762,6 +800,15 @@ fn expandTilde(path: []const u8) ![]const u8 {
|
|||
return utils.allocator.dupe(u8, path);
|
||||
}
|
||||
|
||||
/// Check whether this machine's hostname matches the hostname property
|
||||
/// Always returns true if the "host" property is missing (no host = config applies to
|
||||
/// all hosts). Returns false if the hostname argument is null or does not match.
|
||||
fn hostMatches(node: kdl.Parser.Node, parser: *kdl.Parser, hostname: ?[]const u8) bool {
|
||||
const host_property = utils.stripQuotes(node.prop(parser, "host") orelse return true);
|
||||
const hostname_str = hostname orelse return false;
|
||||
return mem.eql(u8, host_property, hostname_str);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const fmt = std.fmt;
|
||||
const fs = std.fs;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue