stevee

My wayland statusbar
git clone git://gtms.dev/stevee
Log | Files | Refs | Submodules | README | LICENSE

commit 86231de2e6d8017596e443f8402ded96745c9a89
parent b03a9523463068f7f8aaf8ac63f7efc873e2877e
Author: Andrea Feletto <andrea@andreafeletto.com>
Date:   Wed, 19 Jan 2022 23:24:41 +0100

render clock

Diffstat:
Msrc/config.zig | 2++
Msrc/event.zig | 31++++++++++++++++++-------------
Msrc/render.zig | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/wayland.zig | 33++++++++++++++++++++++++++++-----
4 files changed, 129 insertions(+), 18 deletions(-)

diff --git a/src/config.zig b/src/config.zig @@ -7,6 +7,7 @@ pub const Config = struct { foregroundColor: pixman.Color, border: u15, font: *fcft.Font, + clockFormat: [*:0]const u8, pub fn init() !Config { var font_names = [_][*:0]const u8{"monospace:size=14"}; @@ -27,6 +28,7 @@ pub const Config = struct { }, .border = 2, .font = try fcft.Font.fromName(&font_names, null), + .clockFormat = "%d %b %Y - %R", }; } }; diff --git a/src/event.zig b/src/event.zig @@ -5,19 +5,23 @@ const ArrayList = std.ArrayList; const wl = @import("wayland").client.wl; +const render = @import("render.zig"); const State = @import("main.zig").State; pub const Loop = struct { state: *State, - fds: [2]os.pollfd, - timers: ArrayList(*Timer), pub fn init(state: *State) !Loop { const tfd = os.linux.timerfd_create( os.CLOCK.MONOTONIC, os.linux.TFD.CLOEXEC, ); + const interval: os.linux.itimerspec = .{ + .it_interval = .{ .tv_sec = 1, .tv_nsec = 0 }, + .it_value = .{ .tv_sec = 1, .tv_nsec = 0 }, + }; + _ = os.linux.timerfd_settime(@intCast(i32, tfd), 0, &interval, null); return Loop{ .state = state, @@ -33,12 +37,12 @@ pub const Loop = struct { .revents = 0, }, }, - .timers = ArrayList(*Timer).init(state.allocator), }; } pub fn run(self: *Loop) !void { const display = self.state.wayland.display; + const tfd = self.fds[1].fd; while (true) loop: { while (true) { @@ -67,18 +71,19 @@ pub const Loop = struct { // timer if (self.fds[1].revents & os.POLL.IN != 0) { - for (self.timers.items) |timer, i| { - const callback = timer.callback; - const payload = timer.payload; - _ = self.timers.swapRemove(i); - callback(payload); + var expirations = mem.zeroes([8]u8); + _ = try os.read(tfd, &expirations); + + for (self.state.wayland.outputs.items) |output| { + if (output.surface) |surface| { + if (surface.configured) { + render.renderClock(surface) catch continue; + surface.clockSurface.commit(); + surface.backgroundSurface.commit(); + } + } } } } } }; - -pub const Timer = struct { - callback: fn (*anyopaque) void, - payload: *anyopaque, -}; diff --git a/src/render.zig b/src/render.zig @@ -1,3 +1,7 @@ +const std = @import("std"); +const mem = std.mem; + +const cTime = @cImport(@cInclude("time.h")); const fcft = @import("fcft"); const pixman = @import("pixman"); @@ -53,6 +57,70 @@ pub fn renderTags(surface: *Surface) !void { wlSurface.attach(buffer.buffer, 0, 0); } +pub fn renderClock(surface: *Surface) !void { + const state = surface.output.state; + const wlSurface = surface.clockSurface; + + const buffer = try Buffer.nextBuffer( + &surface.clockBuffers, + surface.output.state.wayland.shm, + surface.width, + surface.height, + ); + buffer.busy = true; + + // clear the buffer + const bg_area = [_]pixman.Rectangle16{ + .{ .x = 0, .y = 0, .width = surface.width, .height = surface.height }, + }; + const bg_color = mem.zeroes(pixman.Color); + _ = pixman.Image.fillRectangles(.src, buffer.pix.?, &bg_color, 1, &bg_area); + + // get formatted datetime + const str = try formatDatetime(state); + defer state.allocator.free(str); + + // convert chars to ints for fcft + const cint = try state.allocator.alloc(c_int, str.len); + defer state.allocator.free(cint); + for (str) |char, i| cint[i] = char; + + const run = try fcft.TextRun.rasterize(state.config.font, cint, .default); + defer run.destroy(); + + var i: usize = 0; + var text_width: u32 = 0; + while (i < run.count) : (i += 1) { + text_width += @intCast(u32, run.glyphs[i].advance.x); + } + + const font_height = @intCast(u32, state.config.font.height); + var x_offset = @intCast(i32, @divFloor(surface.width - text_width, 2)); + var y_offset = @intCast(i32, @divFloor(surface.height - font_height, 2)); + + i = 0; + var color = pixman.Image.createSolidFill(&state.config.foregroundColor).?; + while (i < run.count) : (i += 1) { + const glyph = run.glyphs[i]; + const x = x_offset + @intCast(i32, glyph.x); + const y = y_offset + state.config.font.ascent - @intCast(i32, glyph.y); + pixman.Image.composite32( + .over, + color, + glyph.pix, + buffer.pix.?, + 0, 0, 0, 0, + x, y, + glyph.width, glyph.height, + ); + x_offset += glyph.advance.x; + } + + wlSurface.setBufferScale(surface.output.scale); + wlSurface.damageBuffer(0, 0, surface.width, surface.height); + wlSurface.attach(buffer.buffer, 0, 0); +} + fn renderTag( pix: *pixman.Image, tag: *const Tag, @@ -106,3 +174,16 @@ fn renderTag( glyph.width, glyph.height, ); } + +fn formatDatetime(state: *State) ![]const u8 { + var buf = try state.allocator.alloc(u8, 256); + const now = cTime.time(null); + const local = cTime.localtime(&now); + const len = cTime.strftime( + buf.ptr, + buf.len, + state.config.clockFormat, + local, + ); + return state.allocator.resize(buf, len).?; +} diff --git a/src/wayland.zig b/src/wayland.zig @@ -154,6 +154,10 @@ pub const Surface = struct { tagsSubsurface: *wl.Subsurface, tagsBuffers: [2]Buffer, + clockSurface: *wl.Surface, + clockSubsurface: *wl.Subsurface, + clockBuffers: [2]Buffer, + configured: bool, width: u16, height: u16, @@ -181,6 +185,13 @@ pub const Surface = struct { ); self.tagsBuffers = mem.zeroes([2]Buffer); + self.clockSurface = try state.wayland.compositor.createSurface(); + self.clockSubsurface = try state.wayland.subcompositor.getSubsurface( + self.clockSurface, + self.backgroundSurface, + ); + self.clockBuffers = mem.zeroes([2]Buffer); + // setup layer surface self.layerSurface.setSize(0, state.config.height); self.layerSurface.setAnchor( @@ -192,11 +203,13 @@ pub const Surface = struct { // setup subsurfaces self.tagsSubsurface.setPosition(0, 0); - const region = try state.wayland.compositor.createRegion(); - self.tagsSurface.setInputRegion(region); - region.destroy(); + self.clockSubsurface.setPosition(0, 0); + // const region = try state.wayland.compositor.createRegion(); + // self.tagsSurface.setInputRegion(region); + // region.destroy(); self.tagsSurface.commit(); + self.clockSurface.commit(); self.backgroundSurface.commit(); return self; @@ -204,14 +217,22 @@ pub const Surface = struct { pub fn destroy(self: *Surface) void { self.output.surface = null; + self.backgroundSurface.destroy(); self.layerSurface.destroy(); - self.tagsSurface.destroy(); - self.tagsSubsurface.destroy(); self.backgroundBuffers[0].deinit(); self.backgroundBuffers[1].deinit(); + + self.tagsSurface.destroy(); + self.tagsSubsurface.destroy(); self.tagsBuffers[0].deinit(); self.tagsBuffers[1].deinit(); + + self.clockSurface.destroy(); + self.clockSubsurface.destroy(); + self.clockBuffers[0].deinit(); + self.clockBuffers[1].deinit(); + self.output.state.allocator.destroy(self); } @@ -230,8 +251,10 @@ pub const Surface = struct { render.renderBackground(surface) catch return; render.renderTags(surface) catch return; + render.renderClock(surface) catch return; surface.tagsSurface.commit(); + surface.clockSurface.commit(); surface.backgroundSurface.commit(); }, .closed => {