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.
|
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 an optional clock bar
|
||||||
- [ ] Implement a rivertile clone
|
- [ ] Implement a rivertile clone
|
||||||
- [ ] Support overriding config location
|
- [ ] 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] Implement runtime log levels
|
||||||
- [x] Add input configuration, i.e. pointer acceleration and that type of thing
|
- [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 `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",
|
.hash = "xkbcommon-0.4.0-dev-VDqIe0y2AgCNeWLthDZ3MUcUYzhyKXjK85ISm_zxk9Nk",
|
||||||
},
|
},
|
||||||
.kdl = .{
|
.kdl = .{
|
||||||
.url = "https://codeberg.org/desttinghim/zig-kdl/archive/9a92d2cc6bb25031778d321c6c1d87e9e4052eab.tar.gz",
|
.url = "https://codeberg.org/bwbuhse/zig-kdl/archive/13d9d247324f79b854187d6becc47fffdf7fea3b.tar.gz",
|
||||||
.hash = "kdl-0.0.0-8rilEMFEAQCYVNhFIcJZWp8HLrjYaEIZGov6CSH05Dsv",
|
.hash = "kdl-0.0.0-8rilEKdHAQC_NOLDNu3Ts6kJT8uqqJvrPduFScEjSm_g",
|
||||||
},
|
},
|
||||||
.known_folders = .{
|
.known_folders = .{
|
||||||
.url = "https://github.com/ziglibs/known-folders/archive/83d39161eac2ed6f37ad3cb4d9dd518696ce90bb.tar.gz",
|
.url = "https://github.com/ziglibs/known-folders/archive/83d39161eac2ed6f37ad3cb4d9dd518696ce90bb.tar.gz",
|
||||||
|
|
|
||||||
|
|
@ -91,4 +91,8 @@ input "PIXA3854:00 093A:0274 Touchpad" {
|
||||||
natural_scroll "enabled"
|
natural_scroll "enabled"
|
||||||
tap "disabled"
|
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);
|
utils.allocator.destroy(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support kdl properties to specific the hostname the config should affect
|
|
||||||
fn load(config: *Config, reader: *Io.Reader) !void {
|
fn load(config: *Config, reader: *Io.Reader) !void {
|
||||||
var parser = try kdl.Parser.init(utils.allocator, reader, .{});
|
var parser = try kdl.Parser.init(utils.allocator, reader, .{});
|
||||||
defer parser.deinit(utils.allocator);
|
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 next_child_block: ?NodeName = null;
|
||||||
var pending_input_name: ?[]const u8 = null;
|
var pending_input_name: ?[]const u8 = null;
|
||||||
defer if (pending_input_name) |n| utils.allocator.free(n);
|
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
|
// If it's a node, we check if it's a valid NodeName
|
||||||
const node_name = std.meta.stringToEnum(NodeName, node.name);
|
const node_name = std.meta.stringToEnum(NodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
|
if (!hostMatches(node, &parser, hostname)) {
|
||||||
|
logDebugHostMismatch(name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Next, we have to check the specifics for the NodeName
|
// Next, we have to check the specifics for the NodeName
|
||||||
switch (name) {
|
switch (name) {
|
||||||
.attach_mode => {
|
.attach_mode => {
|
||||||
|
|
@ -286,11 +296,11 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
||||||
.child_block_begin => {
|
.child_block_begin => {
|
||||||
if (next_child_block) |child_block| {
|
if (next_child_block) |child_block| {
|
||||||
switch (child_block) {
|
switch (child_block) {
|
||||||
.borders => try config.loadBordersChildBlock(&parser),
|
.borders => try config.loadBordersChildBlock(&parser, hostname),
|
||||||
.keybinds => try config.loadKeybindsChildBlock(&parser),
|
.keybinds => try config.loadKeybindsChildBlock(&parser, hostname),
|
||||||
.pointer_binds => try config.loadPointerBindsChildBlock(&parser),
|
.pointer_binds => try config.loadPointerBindsChildBlock(&parser, hostname),
|
||||||
.input => {
|
.input => {
|
||||||
try config.loadInputChildBlock(&parser, pending_input_name);
|
try config.loadInputChildBlock(&parser, pending_input_name, hostname);
|
||||||
pending_input_name = null; // ownership transferred
|
pending_input_name = null; // ownership transferred
|
||||||
},
|
},
|
||||||
else => {
|
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| {
|
while (try parser.next()) |event| {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.node => |node| {
|
.node => |node| {
|
||||||
// If it's a node, we check if it's a valid NodeName
|
// If it's a node, we check if it's a valid NodeName
|
||||||
const node_name = std.meta.stringToEnum(BorderNodeName, node.name);
|
const node_name = std.meta.stringToEnum(BorderNodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
|
if (!hostMatches(node, parser, hostname)) {
|
||||||
|
logDebugHostMismatch(name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
switch (name) {
|
switch (name) {
|
||||||
.width => {
|
.width => {
|
||||||
const width_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
|
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| {
|
while (try parser.next()) |event| {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.node => |node| {
|
.node => |node| {
|
||||||
|
if (!hostMatches(node, parser, hostname)) {
|
||||||
|
logDebugHostMismatch(node.name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// tag_bind is a special case node name
|
// tag_bind is a special case node name
|
||||||
if (mem.eql(u8, node.name, "tag_bind")) {
|
if (mem.eql(u8, node.name, "tag_bind")) {
|
||||||
const mod_str = utils.stripQuotes(node.arg(parser, 0) orelse {
|
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| {
|
while (try parser.next()) |event| {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.node => |node| {
|
.node => |node| {
|
||||||
const node_name = std.meta.stringToEnum(PointerBindNodeName, node.name);
|
const node_name = std.meta.stringToEnum(PointerBindNodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
|
if (!hostMatches(node, parser, hostname)) {
|
||||||
|
logDebugHostMismatch(name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Parse modifiers (arg 0)
|
// Parse modifiers (arg 0)
|
||||||
const mod_str = utils.stripQuotes(node.arg(parser, 0) orelse {
|
const mod_str = utils.stripQuotes(node.arg(parser, 0) orelse {
|
||||||
logWarnMissingNodeArg(name, "modifier(s)");
|
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 };
|
var input_config: InputConfig = .{ .name = name };
|
||||||
errdefer if (input_config.name) |n| utils.allocator.free(n);
|
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| {
|
.node => |node| {
|
||||||
const node_name = std.meta.stringToEnum(InputConfigNodeName, node.name);
|
const node_name = std.meta.stringToEnum(InputConfigNodeName, node.name);
|
||||||
if (node_name) |tag| {
|
if (node_name) |tag| {
|
||||||
|
if (!hostMatches(node, parser, hostname)) {
|
||||||
|
logDebugHostMismatch(tag);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse {
|
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse {
|
||||||
logWarnMissingNodeArg(tag, "value");
|
logWarnMissingNodeArg(tag, "value");
|
||||||
continue;
|
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 {
|
fn logDebugSettingNode(node_name: anytype, node_value: []const u8) void {
|
||||||
const node_name_type = @TypeOf(node_name);
|
const node_name_type = @TypeOf(node_name);
|
||||||
switch (node_name_type) {
|
switch (node_name_type) {
|
||||||
|
|
@ -762,6 +800,15 @@ fn expandTilde(path: []const u8) ![]const u8 {
|
||||||
return utils.allocator.dupe(u8, path);
|
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 std = @import("std");
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue