commit 86231de2e6d8017596e443f8402ded96745c9a89
parent b03a9523463068f7f8aaf8ac63f7efc873e2877e
Author: Andrea Feletto <andrea@andreafeletto.com>
Date: Wed, 19 Jan 2022 23:24:41 +0100
render clock
Diffstat:
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 => {