stevee

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

commit aae2679e7485e558b4271760e11f75176bb3e644
parent 4db11685b9299e8ea857c6e508d7a6d8ddd001d9
Author: Andrea Feletto <andrea@andreafeletto.com>
Date:   Thu, 20 Jan 2022 12:45:31 +0100

add stdin widget with utf8 support

Diffstat:
Msrc/event.zig | 24+++++++++++++++++++++++-
Msrc/render.zig | 102++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/tags.zig | 2+-
Msrc/wayland.zig | 42+++++++++++++++++++++++++++++-------------
4 files changed, 131 insertions(+), 39 deletions(-)

diff --git a/src/event.zig b/src/event.zig @@ -10,7 +10,7 @@ const State = @import("main.zig").State; pub const Loop = struct { state: *State, - fds: [2]os.pollfd, + fds: [3]os.pollfd, pub fn init(state: *State) !Loop { const tfd = os.linux.timerfd_create( @@ -36,6 +36,11 @@ pub const Loop = struct { .events = os.POLL.IN, .revents = 0, }, + .{ + .fd = os.linux.STDIN_FILENO, + .events = os.POLL.IN, + .revents = 0, + }, }, }; } @@ -84,6 +89,23 @@ pub const Loop = struct { } } } + + // stdin + if (self.fds[2].revents & os.POLL.IN != 0) { + var buffer = [_]u8{0} ** 256; + const len = try os.read(self.fds[2].fd, &buffer); + if (len == 0) continue; + + for (self.state.wayland.outputs.items) |output| { + if (output.surface) |surface| { + if (surface.configured) { + render.renderCustom(surface, buffer[0 .. len - 1]) catch continue; + surface.customSurface.commit(); + surface.backgroundSurface.commit(); + } + } + } + } } } }; diff --git a/src/render.zig b/src/render.zig @@ -80,15 +80,23 @@ pub fn renderClock(surface: *Surface) !void { 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; + // ut8 encoding + const utf8 = try std.unicode.Utf8View.init(str); + var utf8_iter = utf8.iterator(); - const run = try fcft.TextRun.rasterize(state.config.font, cint, .default); - defer run.destroy(); + var runes = try state.allocator.alloc(c_int, str.len); + defer state.allocator.free(runes); var i: usize = 0; + while (utf8_iter.nextCodepoint()) |rune| : (i += 1) { + runes[i] = rune; + } + runes = state.allocator.resize(runes, i).?; + + const run = try fcft.TextRun.rasterize(state.config.font, runes, .default); + defer run.destroy(); + + i = 0; var text_width: u32 = 0; while (i < run.count) : (i += 1) { text_width += @intCast(u32, run.glyphs[i].advance.x); @@ -104,15 +112,69 @@ pub fn renderClock(surface: *Surface) !void { 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, - ); + 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); +} + +pub fn renderCustom(surface: *Surface, str: []const u8) !void { + const state = surface.output.state; + const wlSurface = surface.customSurface; + + const buffer = try Buffer.nextBuffer( + &surface.customBuffers, + 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); + + // ut8 encoding + const utf8 = try std.unicode.Utf8View.init(str); + var utf8_iter = utf8.iterator(); + + var runes = try state.allocator.alloc(c_int, str.len); + defer state.allocator.free(runes); + + var i: usize = 0; + while (utf8_iter.nextCodepoint()) |rune| : (i += 1) { + runes[i] = rune; + } + runes = state.allocator.resize(runes, i).?; + + const run = try fcft.TextRun.rasterize(state.config.font, runes, .default); + defer run.destroy(); + + // compute offsets + i = 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, surface.width - text_width); + var y_offset = @intCast(i32, @divFloor(surface.height - font_height, 2)); + + // resterize + 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; } @@ -164,15 +226,7 @@ fn renderTag( const glyph = try fcft.Glyph.rasterize(font, tag.label, .default); const x = offset + @divFloor(size - glyph.width, 2); const y = @divFloor(size - glyph.height, 2); - pixman.Image.composite32( - .over, - char, - glyph.pix, - pix, - 0, 0, 0, 0, - x, y, - glyph.width, glyph.height, - ); + pixman.Image.composite32(.over, char, glyph.pix, pix, 0, 0, 0, 0, x, y, glyph.width, glyph.height); } fn formatDatetime(state: *State) ![]const u8 { diff --git a/src/tags.zig b/src/tags.zig @@ -80,7 +80,7 @@ pub const Tags = struct { const payload = try std.fmt.allocPrintZ( state.allocator, "{d}", - .{ @as(u32, 1) << @intCast(u5, index) }, + .{@as(u32, 1) << @intCast(u5, index)}, ); defer state.allocator.free(payload); diff --git a/src/wayland.zig b/src/wayland.zig @@ -116,11 +116,11 @@ pub const Wayland = struct { } for (self.outputs.items) |output| { if (output.surface) |surface| { - if ( - surface.backgroundSurface == wlSurface or + if (surface.backgroundSurface == wlSurface or surface.tagsSurface == wlSurface or - surface.clockSurface == wlSurface - ) { + surface.clockSurface == wlSurface or + surface.customSurface == wlSurface) + { return surface; } } @@ -281,19 +281,24 @@ pub const Surface = struct { clockSubsurface: *wl.Subsurface, clockBuffers: [2]Buffer, + customSurface: *wl.Surface, + customSubsurface: *wl.Subsurface, + customBuffers: [2]Buffer, + configured: bool, width: u16, height: u16, pub fn create(output: *Output) !*Surface { const state = output.state; + const wayland = state.wayland; const self = try state.allocator.create(Surface); self.output = output; self.configured = false; - self.backgroundSurface = try state.wayland.compositor.createSurface(); - self.layerSurface = try state.wayland.layerShell.getLayerSurface( + self.backgroundSurface = try wayland.compositor.createSurface(); + self.layerSurface = try wayland.layerShell.getLayerSurface( self.backgroundSurface, output.wlOutput, .overlay, @@ -301,20 +306,27 @@ pub const Surface = struct { ); self.backgroundBuffers = mem.zeroes([2]Buffer); - self.tagsSurface = try state.wayland.compositor.createSurface(); - self.tagsSubsurface = try state.wayland.subcompositor.getSubsurface( + self.tagsSurface = try wayland.compositor.createSurface(); + self.tagsSubsurface = try wayland.subcompositor.getSubsurface( self.tagsSurface, self.backgroundSurface, ); self.tagsBuffers = mem.zeroes([2]Buffer); - self.clockSurface = try state.wayland.compositor.createSurface(); - self.clockSubsurface = try state.wayland.subcompositor.getSubsurface( + self.clockSurface = try wayland.compositor.createSurface(); + self.clockSubsurface = try wayland.subcompositor.getSubsurface( self.clockSurface, self.backgroundSurface, ); self.clockBuffers = mem.zeroes([2]Buffer); + self.customSurface = try wayland.compositor.createSurface(); + self.customSubsurface = try wayland.subcompositor.getSubsurface( + self.customSurface, + self.backgroundSurface, + ); + self.customBuffers = mem.zeroes([2]Buffer); + // setup layer surface self.layerSurface.setSize(0, state.config.height); self.layerSurface.setAnchor( @@ -327,9 +339,7 @@ pub const Surface = struct { // setup subsurfaces self.tagsSubsurface.setPosition(0, 0); self.clockSubsurface.setPosition(0, 0); - // const region = try state.wayland.compositor.createRegion(); - // self.tagsSurface.setInputRegion(region); - // region.destroy(); + self.customSubsurface.setPosition(0, 0); self.tagsSurface.commit(); self.clockSurface.commit(); @@ -356,6 +366,11 @@ pub const Surface = struct { self.clockBuffers[0].deinit(); self.clockBuffers[1].deinit(); + self.customSurface.destroy(); + self.customSubsurface.destroy(); + self.customBuffers[0].deinit(); + self.customBuffers[1].deinit(); + self.output.state.allocator.destroy(self); } @@ -378,6 +393,7 @@ pub const Surface = struct { surface.tagsSurface.commit(); surface.clockSurface.commit(); + surface.customSurface.commit(); surface.backgroundSurface.commit(); }, .closed => {