Wire up TagOverlay into Config

The tag overlay still isn't actually created anywhere, but now it can
be configured.
This commit is contained in:
Ben Buhse 2026-02-16 09:37:33 -06:00
commit 2bef233d8f
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
5 changed files with 335 additions and 11 deletions

View file

@ -44,7 +44,7 @@ wallpaper_image_path "~/Pictures/wallpaper.png"
|------------------------------|--------|---------|-----------------------------------------------------| |------------------------------|--------|---------|-----------------------------------------------------|
| `attach_mode` | enum | `top` | Where new windows go in the stack (`top` or `bottom`) | | `attach_mode` | enum | `top` | Where new windows go in the stack (`top` or `bottom`) |
| `primary_count` | u8 | `1` | Number of windows in the primary stack (0+) | | `primary_count` | u8 | `1` | Number of windows in the primary stack (0+) |
| `primary_ratio` | float | `0.55` | Proportion of output width for the primary stack (0.100.90) | | `primary_ratio` | float | `0.55` | Proportion of output width for the primary stack (0.10-0.90) |
| `focus_follows_pointer` | bool | `#true` | Focus follows the pointer between windows | | `focus_follows_pointer` | bool | `#true` | Focus follows the pointer between windows |
| `pointer_warp_on_focus_change` | bool | `#true` | Warp pointer to center of newly-focused windows | | `pointer_warp_on_focus_change` | bool | `#true` | Warp pointer to center of newly-focused windows |
| `wallpaper_image_path` | string | none | Path to wallpaper image | | `wallpaper_image_path` | string | none | Path to wallpaper image |
@ -71,6 +71,62 @@ borders {
Colors are specified in `0xRRGGBB` or `0xRRGGBBAA` hex format. Colors are specified in `0xRRGGBB` or `0xRRGGBBAA` hex format.
## Tag Overlay
The tag overlay is an optional widget that briefly shows your tag state when switching tags.
It is only created when a `tag_overlay` block is present in the config. All settings have
defaults, with the color based on the Catppuccin Mocha theme. An empty block can be used
to enable the widget with all defaults:
```kdl
tag_overlay {
}
```
### Tag Overlay Settings
| Setting | Type | Default | Description |
|-------------------------------------|-------|--------------|-------------------------------------------|
| `border_width` | u8 | `2` | Widget border width in pixels |
| `tag_amount` | u8 | `9` | Number of displayed tags (1-32) |
| `tags_per_row` | u8 | `32` | Tags per row (1-32) |
| `square_size` | u8 | `40` | Size of tag squares in pixels |
| `square_inner_padding` | u8 | `10` | Padding around occupied indicator |
| `square_padding` | u8 | `15` | Padding around tag squares |
| `square_border_width` | u8 | `1` | Border width of tag squares |
| `timeout` | u32 | `500` | Display duration in milliseconds |
| `background_color` | color | `0x1e1e2e` | Widget background color |
| `border_color` | color | `0x6c7086` | Widget border color |
| `square_active_background_color` | color | `0x89b4fa` | Active tag square background |
| `square_active_border_color` | color | `0x6c7086` | Active tag square border |
| `square_active_occupied_color` | color | `0xcdd6f4` | Active tag occupied indicator |
| `square_inactive_background_color` | color | `0x585b70` | Inactive tag square background |
| `square_inactive_border_color` | color | `0x6c7086` | Inactive tag square border |
| `square_inactive_occupied_color` | color | `0xcdd6f4` | Inactive tag occupied indicator |
### Anchors
The `anchors` child block controls which edge(s) of the screen the overlay
attaches to. Each direction is a boolean (`#true` / `#false`). Default: none, i.e. centered on output.
| Setting | Type | Default |
|----------|------|----------|
| `top` | bool | `#false` |
| `right` | bool | `#false` |
| `bottom` | bool | `#false` |
| `left` | bool | `#false` |
### Margins
The `margins` child block sets pixel offsets from the anchored edge(s).
| Setting | Type | Default |
|----------|------|---------|
| `top` | i32 | `0` |
| `right` | i32 | `0` |
| `bottom` | i32 | `0` |
| `left` | i32 | `0` |
## Keybinds ## Keybinds
Keyboard bindings are placed inside a `keybinds` block. Each binding has the Keyboard bindings are placed inside a `keybinds` block. Each binding has the

View file

@ -2,8 +2,7 @@
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.
- [ ] Implement a river-tag-overlay clone - [ ] Add options to the bar
- [ ] Add options to the bar and river-tag-overlay
- [ ] Make a Rect struct to combine x, y, width, and height - [ ] Make a Rect struct to combine x, y, width, and height
- [ ] Support window rules (float/tags/SSD by app-id/title) - [ ] Support window rules (float/tags/SSD by app-id/title)
- [ ] Support overriding config location - [ ] Support overriding config location
@ -29,3 +28,5 @@ These are in rough order of my priority, though no promises I do them in this or
- [x] Implement primary count/ratio per tagmask - [x] Implement primary count/ratio per tagmask
- [x] Add primary_count and primary_ratio to Config - [x] Add primary_count and primary_ratio to Config
- [x] Implement an optional clock bar - [x] Implement an optional clock bar
- [x] Implement a river-tag-overlay clone
- [x] Add options to the tag overlay

View file

@ -18,6 +18,19 @@ borders {
color_focused "0x89b4fa" color_focused "0x89b4fa"
color_unfocused "0x1e1e2e" color_unfocused "0x1e1e2e"
} }
// Tag overlay widget — shown briefly when switching tags
// Remove this block to disable the overlay entirely
tag_overlay {
tag_amount 10
background_color "0x1e1e2e"
border_color "0x6c7086"
square_active_background_color "0x89b4fa"
square_active_border_color "0x6c7086"
square_active_occupied_color "0xcdd6f4"
square_inactive_background_color "0x585b70"
square_inactive_border_color "0x6c7086"
square_inactive_occupied_color "0xcdd6f4"
}
keybinds { keybinds {
// Swap a window // Swap a window
spawn Mod4 T foot spawn Mod4 T foot

View file

@ -31,6 +31,9 @@ pointer_warp_on_focus_change: bool = true,
/// Path to the wallpaper image /// Path to the wallpaper image
wallpaper_image_path: ?[]const u8 = null, wallpaper_image_path: ?[]const u8 = null,
/// Tag overlay configuration. If null, no overlay is created.
tag_overlay: ?TagOverlayConfig = null,
/// Tag bind entries parsed from config (tag_bind nodes in keybinds block) /// Tag bind entries parsed from config (tag_bind nodes in keybinds block)
tag_binds: std.ArrayList(Keybind) = .{}, tag_binds: std.ArrayList(Keybind) = .{},
keybinds: std.ArrayList(Keybind) = .{}, keybinds: std.ArrayList(Keybind) = .{},
@ -85,6 +88,66 @@ pub const AttachMode = enum {
bottom, bottom,
}; };
pub const TagOverlayConfig = struct {
border_width: u8 = 2,
tag_amount: u8 = 9,
tags_per_row: u8 = 32,
square_size: u8 = 40,
square_inner_padding: u8 = 10,
square_padding: u8 = 15,
square_border_width: u8 = 1,
background_color: pixman.Color = utils.parseRgbaPixmanComptime("0x1e1e2e"),
border_color: pixman.Color = utils.parseRgbaPixmanComptime("0x6c7086"),
square_active_background_color: pixman.Color = utils.parseRgbaPixmanComptime("0x89b4fa"),
square_active_border_color: pixman.Color = utils.parseRgbaPixmanComptime("0x6c7086"),
square_active_occupied_color: pixman.Color = utils.parseRgbaPixmanComptime("0xcdd6f4"),
square_inactive_background_color: pixman.Color = utils.parseRgbaPixmanComptime("0x585b70"),
square_inactive_border_color: pixman.Color = utils.parseRgbaPixmanComptime("0x6c7086"),
square_inactive_occupied_color: pixman.Color = utils.parseRgbaPixmanComptime("0xcdd6f4"),
timeout: u32 = 500,
anchor_top: bool = false,
anchor_right: bool = false,
anchor_bottom: bool = false,
anchor_left: bool = false,
margin_top: i32 = 0,
margin_right: i32 = 0,
margin_bottom: i32 = 0,
margin_left: i32 = 0,
pub fn toTagOverlayOptions(self: TagOverlayConfig) TagOverlay.Options {
return .{
.border_width = self.border_width,
.tag_amount = @intCast(std.math.clamp(@as(u32, self.tag_amount), 1, 32) - 1),
.tags_per_row = @intCast(std.math.clamp(@as(u32, self.tags_per_row), 1, 32) - 1),
.square_size = self.square_size,
.square_inner_padding = self.square_inner_padding,
.square_padding = self.square_padding,
.square_border_width = self.square_border_width,
.background_color = self.background_color,
.border_color = self.border_color,
.square_active_background_color = self.square_active_background_color,
.square_active_border_color = self.square_active_border_color,
.square_active_occupied_color = self.square_active_occupied_color,
.square_inactive_background_color = self.square_inactive_background_color,
.square_inactive_border_color = self.square_inactive_border_color,
.square_inactive_occupied_color = self.square_inactive_occupied_color,
.anchors = .{
.top = self.anchor_top,
.right = self.anchor_right,
.bottom = self.anchor_bottom,
.left = self.anchor_left,
},
.margins = .{
.top = self.margin_top,
.right = self.margin_right,
.bottom = self.margin_bottom,
.left = self.margin_left,
},
.timeout = self.timeout,
};
}
};
const NodeName = enum { const NodeName = enum {
attach_mode, attach_mode,
primary_count, primary_count,
@ -97,6 +160,7 @@ const NodeName = enum {
keybinds, keybinds,
pointer_binds, pointer_binds,
input, input,
tag_overlay,
}; };
const BorderNodeName = enum { const BorderNodeName = enum {
@ -105,6 +169,30 @@ const BorderNodeName = enum {
color_unfocused, color_unfocused,
}; };
const TagOverlayNodeName = enum {
border_width,
tag_amount,
tags_per_row,
square_size,
square_inner_padding,
square_padding,
square_border_width,
background_color,
border_color,
square_active_background_color,
square_active_border_color,
square_active_occupied_color,
square_inactive_background_color,
square_inactive_border_color,
square_inactive_occupied_color,
timeout,
anchors,
margins,
};
const TagOverlayAnchorsNodeName = enum { top, right, bottom, left };
const TagOverlayMarginsNodeName = enum { top, right, bottom, left };
const PointerBindNodeName = enum { const PointerBindNodeName = enum {
move_window, move_window,
resize_window, resize_window,
@ -315,6 +403,9 @@ fn load(config: *Config, reader: *Io.Reader) !void {
null; null;
next_child_block = .input; next_child_block = .input;
}, },
.tag_overlay => {
next_child_block = .tag_overlay;
},
} }
} else { } else {
logWarnInvalidNode(node.name); logWarnInvalidNode(node.name);
@ -330,6 +421,7 @@ fn load(config: *Config, reader: *Io.Reader) !void {
try config.loadInputChildBlock(&parser, pending_input_name, hostname); try config.loadInputChildBlock(&parser, pending_input_name, hostname);
pending_input_name = null; // ownership transferred pending_input_name = null; // ownership transferred
}, },
.tag_overlay => try config.loadTagOverlayChildBlock(&parser, hostname),
else => { else => {
// Nothing else should ever be marked as a next_child_block // Nothing else should ever be marked as a next_child_block
unreachable; unreachable;
@ -398,16 +490,162 @@ fn loadBordersChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]cons
} }
} }
fn loadTagOverlayChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
config.tag_overlay = .{}; // Presence of block = enabled; initialize with defaults
const TagOverlayChild = enum { anchors, margins };
var next_child_block: ?TagOverlayChild = null;
while (try parser.next()) |event| {
switch (event) {
.node => |node| {
if (next_child_block) |child| {
log.warn("Expected child block for tag_overlay.{s}, got node instead. Ignoring", .{@tagName(child)});
next_child_block = null;
}
const node_name = std.meta.stringToEnum(TagOverlayNodeName, node.name);
if (node_name) |name| {
if (!hostMatches(node, parser, hostname)) {
logDebugHostMismatch(name);
continue;
}
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
switch (name) {
.anchors => next_child_block = .anchors,
.margins => next_child_block = .margins,
// These are all u8s, so we can inline the branch
inline .border_width,
.tag_amount,
.tags_per_row,
.square_size,
.square_inner_padding,
.square_padding,
.square_border_width,
=> |tag| {
const val = fmt.parseInt(u8, val_str, 10) catch {
logWarnInvalidNodeArg(name, val_str);
continue;
};
@field(config.tag_overlay.?, @tagName(tag)) = val;
logDebugSettingNode(name, val_str);
},
.timeout => {
config.tag_overlay.?.timeout = fmt.parseInt(u32, val_str, 10) catch {
logWarnInvalidNodeArg(name, val_str);
continue;
};
logDebugSettingNode(name, val_str);
},
inline .background_color,
.border_color,
.square_active_background_color,
.square_active_border_color,
.square_active_occupied_color,
.square_inactive_background_color,
.square_inactive_border_color,
.square_inactive_occupied_color,
=> |tag| {
@field(config.tag_overlay.?, @tagName(tag)) = utils.parseRgbaPixman(val_str) catch {
logWarnInvalidNodeArg(name, val_str);
continue;
};
logDebugSettingNode(name, val_str);
},
}
} else {
logWarnInvalidNode(node.name);
}
},
.child_block_begin => {
if (next_child_block) |child| {
switch (child) {
.anchors => try config.loadTagOverlayAnchorsBlock(parser, hostname),
.margins => try config.loadTagOverlayMarginsBlock(parser, hostname),
}
next_child_block = null;
} else {
try config.skipChildBlock(parser);
}
},
.child_block_end => return,
}
}
}
fn loadTagOverlayAnchorsBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
while (try parser.next()) |event| {
switch (event) {
.node => |node| {
const node_name = std.meta.stringToEnum(TagOverlayAnchorsNodeName, node.name);
if (node_name) |name| {
if (!hostMatches(node, parser, hostname)) {
logDebugHostMismatch(name);
continue;
}
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
if (boolFromKdlStr(val_str)) |val| {
switch (name) {
.top => config.tag_overlay.?.anchor_top = val,
.right => config.tag_overlay.?.anchor_right = val,
.bottom => config.tag_overlay.?.anchor_bottom = val,
.left => config.tag_overlay.?.anchor_left = val,
}
logDebugSettingNode(name, val_str);
} else {
logWarnInvalidNodeArg(name, val_str);
}
} else {
logWarnInvalidNode(node.name);
}
},
.child_block_begin => try config.skipChildBlock(parser),
.child_block_end => return,
}
}
}
fn loadTagOverlayMarginsBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
while (try parser.next()) |event| {
switch (event) {
.node => |node| {
const node_name = std.meta.stringToEnum(TagOverlayMarginsNodeName, node.name);
if (node_name) |name| {
if (!hostMatches(node, parser, hostname)) {
logDebugHostMismatch(name);
continue;
}
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
const val = fmt.parseInt(i32, val_str, 10) catch {
logWarnInvalidNodeArg(name, val_str);
continue;
};
switch (name) {
.top => config.tag_overlay.?.margin_top = val,
.right => config.tag_overlay.?.margin_right = val,
.bottom => config.tag_overlay.?.margin_bottom = val,
.left => config.tag_overlay.?.margin_left = val,
}
logDebugSettingNode(name, val_str);
} else {
logWarnInvalidNode(node.name);
}
},
.child_block_begin => try config.skipChildBlock(parser),
.child_block_end => return,
}
}
}
fn loadKeybindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !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)) { // tag_bind is a special case node name not in KeybindNodeName
logDebugHostMismatch(node.name);
continue;
}
// tag_bind is a special case node name
if (mem.eql(u8, node.name, "tag_bind")) { if (mem.eql(u8, node.name, "tag_bind")) {
if (!hostMatches(node, parser, hostname)) {
log.debug("Skipping \"keybind.tag_bind\" (host mismatch)", .{});
continue;
}
const mod_str = utils.stripQuotes(node.arg(parser, 0) orelse { const mod_str = utils.stripQuotes(node.arg(parser, 0) orelse {
log.warn("tag_bind: missing modifier argument. Ignoring", .{}); log.warn("tag_bind: missing modifier argument. Ignoring", .{});
continue; continue;
@ -443,13 +681,16 @@ fn loadKeybindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]con
.keysym = null, // Tag binds don't need a keysym (automatically 1-9) .keysym = null, // Tag binds don't need a keysym (automatically 1-9)
}); });
// TODO: Make a logger for keybind settings
log.debug("tag_bind: {s} {s}", .{ mod_str, cmd_str }); log.debug("tag_bind: {s} {s}", .{ mod_str, cmd_str });
continue; continue;
} }
// Handle the rest of the possiblities like all the other types of block // Handle the rest of the possibilities like all the other types of block
const node_name = std.meta.stringToEnum(KeybindNodeName, node.name); const node_name = std.meta.stringToEnum(KeybindNodeName, node.name);
if (node_name) |name| { if (node_name) |name| {
if (!hostMatches(node, parser, hostname)) {
logDebugHostMismatch(name);
continue;
}
// All nodes should have at least a command, modifiers, and a keysym // All nodes should have at least a command, modifiers, and a keysym
// Some may have more arguments handled in the switch // Some may have more arguments handled in the switch
const mod_str = utils.stripQuotes(node.arg(parser, 0) orelse { const mod_str = utils.stripQuotes(node.arg(parser, 0) orelse {
@ -787,6 +1028,9 @@ fn logWarnInvalidNodeArg(node_name: anytype, node_value: []const u8) void {
KeybindNodeName => log.warn("Invalid \"keybind.{s}\" ({s}). Ignoring", .{ @tagName(node_name), node_value }), KeybindNodeName => log.warn("Invalid \"keybind.{s}\" ({s}). Ignoring", .{ @tagName(node_name), node_value }),
PointerBindNodeName => log.warn("Invalid \"pointer_binds.{s}\" ({s}). Ignoring", .{ @tagName(node_name), node_value }), PointerBindNodeName => log.warn("Invalid \"pointer_binds.{s}\" ({s}). Ignoring", .{ @tagName(node_name), node_value }),
InputConfigNodeName => log.warn("Invalid \"input.{s}\" ({s}). Ignoring", .{ @tagName(node_name), node_value }), InputConfigNodeName => log.warn("Invalid \"input.{s}\" ({s}). Ignoring", .{ @tagName(node_name), node_value }),
TagOverlayNodeName => log.warn("Invalid \"tag_overlay.{s}\" ({s}). Using default value", .{ @tagName(node_name), node_value }),
TagOverlayAnchorsNodeName => log.warn("Invalid \"tag_overlay.anchors.{s}\" ({s}). Using default value", .{ @tagName(node_name), node_value }),
TagOverlayMarginsNodeName => log.warn("Invalid \"tag_overlay.margins.{s}\" ({s}). Using default value", .{ @tagName(node_name), node_value }),
else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""), else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""),
} }
} }
@ -798,6 +1042,7 @@ fn logWarnMissingNodeArg(node_name: anytype, comptime arg: []const u8) void {
KeybindNodeName => log.warn("\"keybind.{s}\" missing " ++ arg ++ " argument. Ignoring", .{@tagName(node_name)}), KeybindNodeName => log.warn("\"keybind.{s}\" missing " ++ arg ++ " argument. Ignoring", .{@tagName(node_name)}),
PointerBindNodeName => log.warn("\"pointer_binds.{s}\" missing " ++ arg ++ " argument. Ignoring", .{@tagName(node_name)}), PointerBindNodeName => log.warn("\"pointer_binds.{s}\" missing " ++ arg ++ " argument. Ignoring", .{@tagName(node_name)}),
InputConfigNodeName => log.warn("\"input.{s}\" missing " ++ arg ++ " argument. Ignoring", .{@tagName(node_name)}), InputConfigNodeName => log.warn("\"input.{s}\" missing " ++ arg ++ " argument. Ignoring", .{@tagName(node_name)}),
TagOverlayNodeName => log.warn("\"tag_overlay.{s}\" missing " ++ arg ++ " argument. Ignoring", .{@tagName(node_name)}),
else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""), else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""),
} }
} }
@ -821,7 +1066,10 @@ fn logDebugHostMismatch(node_name: anytype) void {
BorderNodeName => log.debug("Skipping \"border.{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)}), PointerBindNodeName => log.debug("Skipping \"pointer_binds.{s}\" (host mismatch)", .{@tagName(node_name)}),
InputConfigNodeName => log.debug("Skipping \"input.{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}), TagOverlayNodeName => log.debug("Skipping \"tag_overlay.{s}\" (host mismatch)", .{@tagName(node_name)}),
TagOverlayAnchorsNodeName => log.debug("Skipping \"tag_overlay.anchors.{s}\" (host mismatch)", .{@tagName(node_name)}),
TagOverlayMarginsNodeName => log.debug("Skipping \"tag_overlay.margins.{s}\" (host mismatch)", .{@tagName(node_name)}),
KeybindNodeName => log.debug("Skipping \"keybind.{s}\" (host mismatch)", .{@tagName(node_name)}),
else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""), else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""),
} }
} }
@ -831,6 +1079,9 @@ fn logDebugSettingNode(node_name: anytype, node_value: []const u8) void {
switch (node_name_type) { switch (node_name_type) {
NodeName => log.debug("Setting {s} to {s}", .{ @tagName(node_name), node_value }), NodeName => log.debug("Setting {s} to {s}", .{ @tagName(node_name), node_value }),
BorderNodeName => log.debug("Setting border.{s} to {s}", .{ @tagName(node_name), node_value }), BorderNodeName => log.debug("Setting border.{s} to {s}", .{ @tagName(node_name), node_value }),
TagOverlayNodeName => log.debug("Setting tag_overlay.{s} to {s}", .{ @tagName(node_name), node_value }),
TagOverlayAnchorsNodeName => log.debug("Setting tag_overlay.anchors.{s} to {s}", .{ @tagName(node_name), node_value }),
TagOverlayMarginsNodeName => log.debug("Setting tag_overlay.margins.{s} to {s}", .{ @tagName(node_name), node_value }),
else => @compileError("This function does not (yet) support type \"" ++ @typeName(@TypeOf(node_name)) ++ "\""), else => @compileError("This function does not (yet) support type \"" ++ @typeName(@TypeOf(node_name)) ++ "\""),
} }
} }
@ -879,10 +1130,12 @@ const ThreeFingerDragState = river.LibinputDeviceV1.ThreeFingerDragState;
const kdl = @import("kdl"); const kdl = @import("kdl");
const known_folders = @import("known_folders"); const known_folders = @import("known_folders");
const pixman = @import("pixman");
const xkbcommon = @import("xkbcommon"); const xkbcommon = @import("xkbcommon");
const utils = @import("utils.zig"); const utils = @import("utils.zig");
const RiverColor = utils.RiverColor; const RiverColor = utils.RiverColor;
const TagOverlay = @import("TagOverlay.zig");
const XkbBindings = @import("XkbBindings.zig"); const XkbBindings = @import("XkbBindings.zig");
const log = std.log.scoped(.Config); const log = std.log.scoped(.Config);

View file

@ -64,6 +64,7 @@ pub fn parseRgbaPixman(s: []const u8) !pixman.Color {
/// Parse a color in the format 0xRRGGBB or 0xRRGGBBAA and convert it to /// Parse a color in the format 0xRRGGBB or 0xRRGGBBAA and convert it to
/// 16-bit color values at comptime. /// 16-bit color values at comptime.
pub fn parseRgbaPixmanComptime(comptime s: []const u8) pixman.Color { pub fn parseRgbaPixmanComptime(comptime s: []const u8) pixman.Color {
@setEvalBranchQuota(2000);
if (s.len != 8 and s.len != 10) @compileError("Invalid RGBA"); if (s.len != 8 and s.len != 10) @compileError("Invalid RGBA");
if (s[0] != '0' or s[1] != 'x') @compileError("Invalid RGBA"); if (s[0] != '0' or s[1] != 'x') @compileError("Invalid RGBA");