render.zig (7359B)
1 const std = @import("std"); 2 const mem = std.mem; 3 4 const fcft = @import("fcft"); 5 const pixman = @import("pixman"); 6 const time = @cImport(@cInclude("time.h")); 7 8 const Buffer = @import("Buffer.zig"); 9 const Bar = @import("Bar.zig"); 10 const Tag = @import("Tags.zig").Tag; 11 const utils = @import("utils.zig"); 12 13 const Backlight = @import("modules/Backlight.zig"); 14 const Battery = @import("modules/Battery.zig"); 15 const Pulse = @import("modules/Pulse.zig"); 16 17 const state = &@import("root").state; 18 19 pub const RenderFn = fn (*Bar) anyerror!void; 20 21 pub fn renderTags(bar: *Bar) !void { 22 const surface = bar.tags.surface; 23 const tags = bar.monitor.tags.tags; 24 25 const buffers = &bar.tags.buffers; 26 const shm = state.wayland.shm.?; 27 28 const width = bar.height * @as(u16, @intCast(bar.monitor.tags.tags.len)); 29 const buffer = try Buffer.nextBuffer(buffers, shm, width, bar.height); 30 if (buffer.buffer == null) return; 31 buffer.busy = true; 32 33 for (&tags, 0..) |*tag, i| { 34 const offset = bar.height * i; 35 try renderTag(buffer.pix.?, tag, bar.height, @intCast(offset)); 36 } 37 38 surface.setBufferScale(1); 39 surface.damageBuffer(0, 0, width, bar.height); 40 surface.attach(buffer.buffer, 0, 0); 41 } 42 43 pub fn renderClock(bar: *Bar) !void { 44 const surface = bar.clock.surface; 45 const shm = state.wayland.shm.?; 46 47 // utf8 datetime 48 const str = try formatDatetime(); 49 defer state.gpa.free(str); 50 const runes = try utils.toUtf8(state.gpa, str); 51 defer state.gpa.free(runes); 52 53 // resterize 54 const font = state.config.font; 55 const run = try font.rasterizeTextRunUtf32(runes, .default); 56 defer run.destroy(); 57 58 // compute total width 59 var i: usize = 0; 60 var width: u16 = 0; 61 while (i < run.count) : (i += 1) { 62 width += @as(u16, @intCast(run.glyphs[i].advance.x)); 63 } 64 65 // set subsurface offset 66 const font_height = @as(u32, @intCast(font.height)); 67 const x_offset = @as(i32, @intCast((bar.width - width) / 2)); 68 const y_offset = @as(i32, @intCast((bar.height - font_height) / 2)); 69 bar.clock.subsurface.setPosition(x_offset, y_offset); 70 71 const buffers = &bar.clock.buffers; 72 const buffer = try Buffer.nextBuffer(buffers, shm, width, bar.height); 73 if (buffer.buffer == null) return; 74 buffer.busy = true; 75 76 const bg_area = [_]pixman.Rectangle16{ 77 .{ .x = 0, .y = 0, .width = width, .height = bar.height }, 78 }; 79 const bg_color = mem.zeroes(pixman.Color); 80 _ = pixman.Image.fillRectangles(.src, buffer.pix.?, &bg_color, 1, &bg_area); 81 82 var x: i32 = 0; 83 i = 0; 84 const color = pixman.Image.createSolidFill(&state.config.foregroundColor).?; 85 while (i < run.count) : (i += 1) { 86 const glyph = run.glyphs[i]; 87 x += @as(i32, @intCast(glyph.x)); 88 const y = state.config.font.ascent - @as(i32, @intCast(glyph.y)); 89 pixman.Image.composite32(.over, color, glyph.pix, buffer.pix.?, 0, 0, 0, 0, x, y, glyph.width, glyph.height); 90 x += glyph.advance.x - @as(i32, @intCast(glyph.x)); 91 } 92 93 surface.setBufferScale(1); 94 surface.damageBuffer(0, 0, width, bar.height); 95 surface.attach(buffer.buffer, 0, 0); 96 } 97 98 pub fn renderModules(bar: *Bar) !void { 99 const surface = bar.modules.surface; 100 const shm = state.wayland.shm.?; 101 102 // compose string 103 var string = std.ArrayList(u8).init(state.gpa); 104 defer string.deinit(); 105 106 const writer = string.writer(); 107 for (state.modules.order.items) |tag| { 108 try writer.print(" | ", .{}); 109 switch (tag) { 110 .backlight => try state.modules.backlight.?.print(writer), 111 .battery => try state.modules.battery.?.print(writer), 112 .pulse => try state.modules.pulse.?.print(writer), 113 } 114 } 115 try writer.print(" ", .{}); 116 117 // ut8 encoding 118 const runes = try utils.toUtf8(state.gpa, string.items); 119 defer state.gpa.free(runes); 120 121 // rasterize 122 const font = state.config.font; 123 const run = try font.rasterizeTextRunUtf32(runes, .default); 124 defer run.destroy(); 125 126 // compute total width 127 var i: usize = 0; 128 var width: u16 = 0; 129 while (i < run.count) : (i += 1) { 130 width += @as(u16, @intCast(run.glyphs[i].advance.x)); 131 } 132 133 // set subsurface offset 134 const font_height = @as(u32, @intCast(state.config.font.height)); 135 const x_offset = @as(i32, @intCast(bar.width - width)); 136 const y_offset = @as(i32, @intCast(@divFloor(bar.height - font_height, 2))); 137 bar.modules.subsurface.setPosition(x_offset, y_offset); 138 139 const buffers = &bar.modules.buffers; 140 const buffer = try Buffer.nextBuffer(buffers, shm, width, bar.height); 141 if (buffer.buffer == null) return; 142 buffer.busy = true; 143 144 const bg_area = [_]pixman.Rectangle16{ 145 .{ .x = 0, .y = 0, .width = width, .height = bar.height }, 146 }; 147 const bg_color = mem.zeroes(pixman.Color); 148 _ = pixman.Image.fillRectangles(.src, buffer.pix.?, &bg_color, 1, &bg_area); 149 150 var x: i32 = 0; 151 i = 0; 152 const color = pixman.Image.createSolidFill(&state.config.foregroundColor).?; 153 while (i < run.count) : (i += 1) { 154 const glyph = run.glyphs[i]; 155 x += @as(i32, @intCast(glyph.x)); 156 const y = state.config.font.ascent - @as(i32, @intCast(glyph.y)); 157 pixman.Image.composite32(.over, color, glyph.pix, buffer.pix.?, 0, 0, 0, 0, x, y, glyph.width, glyph.height); 158 x += glyph.advance.x - @as(i32, @intCast(glyph.x)); 159 } 160 161 surface.setBufferScale(1); 162 surface.damageBuffer(0, 0, width, bar.height); 163 surface.attach(buffer.buffer, 0, 0); 164 } 165 166 fn renderTag( 167 pix: *pixman.Image, 168 tag: *const Tag, 169 height: u16, 170 offset: i16, 171 ) !void { 172 const size = height; 173 174 const outer = [_]pixman.Rectangle16{ 175 .{ .x = offset, .y = 0, .width = size, .height = size }, 176 }; 177 const outer_color = if (tag.focused or tag.occupied) blk: { 178 break :blk &state.config.foregroundColor; 179 } else blk: { 180 break :blk &state.config.backgroundColor; 181 }; 182 _ = pixman.Image.fillRectangles(.over, pix, outer_color, 1, &outer); 183 184 const border = state.config.border; 185 const inner = [_]pixman.Rectangle16{ 186 .{ 187 .x = offset + border, 188 .y = border, 189 .width = size - 2 * border, 190 .height = size - 2 * border, 191 }, 192 }; 193 const inner_color = &state.config.backgroundColor; 194 if (!tag.focused and tag.occupied) { 195 _ = pixman.Image.fillRectangles(.over, pix, inner_color, 1, &inner); 196 } 197 198 const glyph_color = if (tag.focused) blk: { 199 break :blk &state.config.backgroundColor; 200 } else blk: { 201 break :blk &state.config.foregroundColor; 202 }; 203 const font = state.config.font; 204 const char = pixman.Image.createSolidFill(glyph_color).?; 205 const glyph = try font.rasterizeCharUtf32(tag.label, .default); 206 const x = offset + @divFloor(size - glyph.width, 2); 207 const y = @divFloor(size - glyph.height, 2); 208 pixman.Image.composite32(.over, char, glyph.pix, pix, 0, 0, 0, 0, x, y, glyph.width, glyph.height); 209 } 210 211 fn formatDatetime() ![]const u8 { 212 const buf = try state.gpa.alloc(u8, 256); 213 const now = time.time(null); 214 const local = time.localtime(&now); 215 const len = time.strftime( 216 buf.ptr, 217 buf.len, 218 state.config.clockFormat, 219 local, 220 ); 221 return state.gpa.realloc(buf, len); 222 }