~andreafeletto/levee

aae2679e7485e558b4271760e11f75176bb3e644 — Andrea Feletto 4 months ago 4db1168
add stdin widget with utf8 support
4 files changed, 131 insertions(+), 39 deletions(-)

M src/event.zig
M src/render.zig
M src/tags.zig
M src/wayland.zig
M src/event.zig => src/event.zig +23 -1
@@ 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();
                        }
                    }
                }
            }
        }
    }
};

M src/render.zig => src/render.zig +78 -24
@@ 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 {

M src/tags.zig => src/tags.zig +1 -1
@@ 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);


M src/wayland.zig => src/wayland.zig +29 -13
@@ 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 => {