1const std = @import("std");
2const log = std.log;
3
4const zriver = @import("wayland").client.zriver;
5const pixman = @import("pixman");
6
7const Monitor = @import("Monitor.zig");
8const render = @import("render.zig");
9const Input = @import("Input.zig");
10const Tags = @This();
11
12const state = &@import("root").state;
13
14monitor: *Monitor,
15output_status: *zriver.OutputStatusV1,
16tags: [9]Tag,
17
18pub const Tag = struct {
19 label: u8,
20 focused: bool = false,
21 occupied: bool = false,
22 urgent: bool = false,
23
24 pub fn bgColor(self: *const Tag) *pixman.Color {
25 if (self.focused) {
26 return &state.config.focusBgColor;
27 } else if (self.urgent) {
28 return &state.config.normalFgColor;
29 } else {
30 return &state.config.normalBgColor;
31 }
32 }
33
34 pub fn fgColor(self: *const Tag) *pixman.Color {
35 if (self.focused) {
36 return &state.config.focusFgColor;
37 } else if (self.urgent) {
38 return &state.config.normalBgColor;
39 } else {
40 return &state.config.normalFgColor;
41 }
42 }
43};
44
45pub fn create(monitor: *Monitor) !*Tags {
46 const self = try state.gpa.create(Tags);
47 const manager = state.wayland.status_manager.?;
48
49 self.monitor = monitor;
50 self.output_status = try manager.getRiverOutputStatus(monitor.output);
51 for (&self.tags, 0..) |*tag, i| {
52 tag.label = '1' + @as(u8, @intCast(i));
53 }
54
55 self.output_status.setListener(*Tags, outputStatusListener, self);
56 return self;
57}
58
59pub fn destroy(self: *Tags) void {
60 self.output_status.destroy();
61 state.gpa.destroy(self);
62}
63
64fn outputStatusListener(
65 _: *zriver.OutputStatusV1,
66 event: zriver.OutputStatusV1.Event,
67 tags: *Tags,
68) void {
69 switch (event) {
70 .focused_tags => |data| {
71 for (&tags.tags, 0..) |*tag, i| {
72 const mask = @as(u32, 1) << @as(u5, @intCast(i));
73 tag.focused = data.tags & mask != 0;
74 }
75 },
76 .urgent_tags => |data| {
77 for (&tags.tags, 0..) |*tag, i| {
78 const mask = @as(u32, 1) << @as(u5, @intCast(i));
79 tag.urgent = data.tags & mask != 0;
80 }
81 },
82 .view_tags => |data| {
83 for (&tags.tags) |*tag| {
84 tag.occupied = false;
85 }
86 for (data.tags.slice(u32)) |view| {
87 for (&tags.tags, 0..) |*tag, i| {
88 const mask = @as(u32, 1) << @as(u5, @intCast(i));
89 if (view & mask != 0) tag.occupied = true;
90 }
91 }
92 },
93 }
94 if (tags.monitor.confBar()) |bar| {
95 render.renderTags(bar) catch |err| {
96 log.err("renderTags failed for monitor {}: {s}", .{ tags.monitor.globalName, @errorName(err) });
97 return;
98 };
99
100 bar.tags.surface.commit();
101 bar.background.surface.commit();
102 }
103}
104
105pub fn handleClick(self: *Tags, x: u32) !void {
106 const control = state.wayland.control.?;
107
108 if (self.monitor.bar) |bar| {
109 const index = x / bar.height;
110 const payload = try std.fmt.allocPrintZ(
111 state.gpa,
112 "{d}",
113 .{@as(u32, 1) << @as(u5, @intCast(index))},
114 );
115 defer state.gpa.free(payload);
116
117 control.addArgument("set-focused-tags");
118 control.addArgument(payload);
119 const callback = try control.runCommand(state.wayland.seat.?);
120 _ = callback;
121 }
122}