1const std = @import("std");
2const log = std.log;
3const mem = std.mem;
4
5const fcft = @import("fcft");
6const wl = @import("wayland").client.wl;
7const wp = @import("wayland").client.wp;
8const zwlr = @import("wayland").client.zwlr;
9
10const Buffer = @import("Buffer.zig");
11const Monitor = @import("Monitor.zig");
12const render = @import("render.zig");
13const Widget = @import("Widget.zig");
14const Bar = @This();
15
16const state = &@import("root").state;
17
18monitor: *Monitor,
19
20layer_surface: *zwlr.LayerSurfaceV1,
21background: struct {
22 surface: *wl.Surface,
23 viewport: *wp.Viewport,
24 buffer: *wl.Buffer,
25},
26
27title: Widget,
28tags: Widget,
29text: Widget,
30
31tags_width: u16,
32text_width: u16,
33
34abbrev_width: u16,
35abbrev_run: *const fcft.TextRun,
36
37text_padding: i32,
38configured: bool,
39width: u16,
40height: u16,
41
42// Convert a pixman u16 color to a 32-bit color with a pre-multiplied
43// alpha channel as used by the "Single-pixel buffer" Wayland protocol.
44fn toRgba(color: u16) u32 {
45 return (@as(u32, color) >> 8) << 24 | 0xffffff;
46}
47
48pub fn create(monitor: *Monitor) !*Bar {
49 const bg_color = &state.config.normalBgColor;
50 const self = try state.gpa.create(Bar);
51 self.monitor = monitor;
52 self.configured = false;
53
54 const compositor = state.wayland.compositor.?;
55 const viewporter = state.wayland.viewporter.?;
56 const spb_manager = state.wayland.single_pixel_buffer_manager.?;
57 const layer_shell = state.wayland.layer_shell.?;
58
59 self.background.surface = try compositor.createSurface();
60 self.background.viewport = try viewporter.getViewport(self.background.surface);
61 self.background.buffer = try spb_manager.createU32RgbaBuffer(toRgba(bg_color.red), toRgba(bg_color.green), toRgba(bg_color.blue), 0xffffffff);
62
63 self.layer_surface = try layer_shell.getLayerSurface(self.background.surface, monitor.output, .top, "creek");
64
65 self.title = try Widget.init(self.background.surface);
66 self.tags = try Widget.init(self.background.surface);
67 self.text = try Widget.init(self.background.surface);
68
69 // calculate right padding for status text
70 const font = state.config.font;
71 const char_run = try font.rasterizeTextRunUtf32(&[_]u32{' '}, .default);
72 self.text_padding = char_run.glyphs[0].advance.x;
73 char_run.destroy();
74
75 // rasterize abbreviation glyphs for window ttile.
76 self.abbrev_run = try font.rasterizeTextRunUtf32(&[_]u32{'…'}, .default);
77 self.abbrev_width = 0;
78 var i: usize = 0;
79 while (i < self.abbrev_run.count) : (i += 1) {
80 self.abbrev_width += @intCast(self.abbrev_run.glyphs[i].advance.x);
81 }
82
83 // setup layer surface
84 self.layer_surface.setSize(0, state.config.height);
85 self.layer_surface.setAnchor(
86 .{ .top = true, .left = true, .right = true, .bottom = false },
87 );
88 self.layer_surface.setExclusiveZone(state.config.height);
89 self.layer_surface.setMargin(0, 0, 0, 0);
90 self.layer_surface.setListener(*Bar, layerSurfaceListener, self);
91
92 self.tags.surface.commit();
93 self.title.surface.commit();
94 self.text.surface.commit();
95 self.background.surface.commit();
96
97 self.tags_width = 0;
98 self.text_width = 0;
99
100 return self;
101}
102
103pub fn destroy(self: *Bar) void {
104 self.abbrev_run.destroy();
105 self.monitor.bar = null;
106
107 self.layer_surface.destroy();
108
109 self.background.buffer.destroy();
110 self.background.viewport.destroy();
111 self.background.surface.destroy();
112
113 self.title.deinit();
114 self.tags.deinit();
115 self.text.deinit();
116 state.gpa.destroy(self);
117}
118
119fn layerSurfaceListener(
120 layerSurface: *zwlr.LayerSurfaceV1,
121 event: zwlr.LayerSurfaceV1.Event,
122 bar: *Bar,
123) void {
124 switch (event) {
125 .configure => |data| {
126 layerSurface.ackConfigure(data.serial);
127
128 const w: u16 = @intCast(data.width);
129 const h: u16 = @intCast(data.height);
130 if (bar.configured and bar.width == w and bar.height == h) {
131 return;
132 }
133
134 bar.configured = true;
135 bar.width = w;
136 bar.height = h;
137
138 const bg = &bar.background;
139 bg.surface.attach(bg.buffer, 0, 0);
140 bg.surface.damageBuffer(0, 0, bar.width, bar.height);
141 bg.viewport.setDestination(bar.width, bar.height);
142
143 render.renderTags(bar) catch |err| {
144 log.err("renderTags failed for monitor {}: {s}", .{ bar.monitor.globalName, @errorName(err) });
145 return;
146 };
147
148 bar.tags.surface.commit();
149 bar.title.surface.commit();
150 bar.text.surface.commit();
151 bar.background.surface.commit();
152 },
153 .closed => bar.destroy(),
154 }
155}