1const std = @import("std");2const log = std.log;3const mem = std.mem;45const fcft = @import("fcft");6const wl = @import("wayland").client.wl;7const wp = @import("wayland").client.wp;8const zwlr = @import("wayland").client.zwlr;910const Buffer = @import("Buffer.zig");11const Monitor = @import("Monitor.zig");12const render = @import("render.zig");13const Widget = @import("Widget.zig");14const Bar = @This();1516const state = &@import("root").state;1718monitor: *Monitor,1920layer_surface: *zwlr.LayerSurfaceV1,21background: struct {22 surface: *wl.Surface,23 viewport: *wp.Viewport,24 buffer: *wl.Buffer,25},2627title: Widget,28tags: Widget,29text: Widget,3031tags_width: u16,32text_width: u16,3334abbrev_width: u16,35abbrev_run: *const fcft.TextRun,3637text_padding: i32,38configured: bool,39width: u16,40height: u16,4142// Convert a pixman u16 color to a 32-bit color with a pre-multiplied43// 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}4748pub 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;5354 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.?;5859 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);6263 self.layer_surface = try layer_shell.getLayerSurface(self.background.surface, monitor.output, .top, "creek");6465 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);6869 // calculate right padding for status text70 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();7475 // 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 }8283 // setup layer surface84 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);9192 self.tags.surface.commit();93 self.title.surface.commit();94 self.text.surface.commit();95 self.background.surface.commit();9697 self.tags_width = 0;98 self.text_width = 0;99100 return self;101}102103pub fn destroy(self: *Bar) void {104 self.abbrev_run.destroy();105 self.monitor.bar = null;106107 self.layer_surface.destroy();108109 self.background.buffer.destroy();110 self.background.viewport.destroy();111 self.background.surface.destroy();112113 self.title.deinit();114 self.tags.deinit();115 self.text.deinit();116 state.gpa.destroy(self);117}118119fn 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);127128 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 }133134 bar.configured = true;135 bar.width = w;136 bar.height = h;137138 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);142143 render.renderTags(bar) catch |err| {144 log.err("renderTags failed for monitor {}: {s}", .{ bar.monitor.globalName, @errorName(err) });145 return;146 };147148 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}