~andreafeletto/levee

dadf6bdd225f5efe54f4364db9fa89e496a05c2b — Andrea Feletto 11 months ago 4fa3228
update to zig 0.11
M .build.yml => .build.yml +3 -3
@@ 12,9 12,9 @@ secrets:
  - 8a791f75-2c57-4cd6-8cae-710da7d992cc
tasks:
  - zig: |
      curl -O https://ziglang.org/download/0.10.0/zig-linux-x86_64-0.10.0.tar.xz
      tar xf zig-linux-x86_64-0.10.0.tar.xz
      mv zig-linux-x86_64-0.10.0 zig
      curl -O https://ziglang.org/download/0.11.0/zig-linux-x86_64-0.11.0.tar.xz
      tar xf zig-linux-x86_64-0.11.0.tar.xz
      mv zig-linux-x86_64-0.11.0 zig
  - submodules: |
      cd levee
      git submodule update --init

M README.md => README.md +1 -1
@@ 25,7 25,7 @@ riverctl spawn "levee pulse backlight battery"

## Dependencies

* [zig] 0.10.0
* [zig] 0.11.0
* [wayland] 1.21.0
* [pixman] 0.42.0
* [fcft] 3.1.5

M build.zig => build.zig +38 -39
@@ 1,23 1,21 @@
const std = @import("std");
const Pkg = std.build.Pkg;
const Builder = std.build.Builder;

const ScanProtocolsStep = @import("deps/zig-wayland/build.zig").ScanProtocolsStep;
const Scanner = @import("deps/zig-wayland/build.zig").Scanner;

pub fn build(b: *std.build.Builder) void {
pub fn build(b: *Builder) void {
    const target = b.standardTargetOptions(.{});
    const mode = b.standardReleaseOptions();
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable("levee", "src/main.zig");
    exe.setTarget(target);
    exe.setBuildMode(mode);
    const scanner = Scanner.create(b, .{});
    const wayland = b.createModule(.{ .source_file = scanner.result });

    const scanner = ScanProtocolsStep.create(b);
    scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
    scanner.addSystemProtocol("stable/viewporter/viewporter.xml");
    scanner.addSystemProtocol("staging/single-pixel-buffer/single-pixel-buffer-v1.xml");
    scanner.addProtocolPath("protocol/wlr-layer-shell-unstable-v1.xml");
    scanner.addProtocolPath("protocol/river-status-unstable-v1.xml");
    scanner.addProtocolPath("protocol/river-control-unstable-v1.xml");
    scanner.addCustomProtocol("protocol/wlr-layer-shell-unstable-v1.xml");
    scanner.addCustomProtocol("protocol/river-status-unstable-v1.xml");
    scanner.addCustomProtocol("protocol/river-control-unstable-v1.xml");

    scanner.generate("wl_compositor", 4);
    scanner.generate("wl_subcompositor", 1);


@@ 30,31 28,30 @@ pub fn build(b: *std.build.Builder) void {
    scanner.generate("zriver_status_manager_v1", 1);
    scanner.generate("zriver_control_v1", 1);

    exe.step.dependOn(&scanner.step);
    scanner.addCSource(exe);
    const pixman = b.createModule(.{
        .source_file = .{ .path = "deps/zig-pixman/pixman.zig" },
    });
    const fcft = b.createModule(.{
        .source_file = .{ .path = "deps/zig-fcft/fcft.zig" },
        .dependencies = &.{
            .{ .name = "pixman", .module = pixman },
        },
    });
    const udev = b.createModule(.{
        .source_file = .{ .path = "deps/zig-udev/udev.zig" },
    });

    const wayland = Pkg{
        .name = "wayland",
        .source = .{ .generated = &scanner.result },
    };
    const pixman = Pkg{
        .name = "pixman",
        .source = .{ .path = "deps/zig-pixman/pixman.zig" },
    };
    const fcft = Pkg{
        .name = "fcft",
        .source = .{ .path = "deps/zig-fcft/fcft.zig" },
        .dependencies = &[_]Pkg{pixman},
    };
    const udev = Pkg{
        .name = "udev",
        .source = .{ .path = "deps/zig-udev/udev.zig" },
    };
    const exe = b.addExecutable(.{
        .name = "levee",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });

    exe.addPackage(fcft);
    exe.addPackage(pixman);
    exe.addPackage(udev);
    exe.addPackage(wayland);
    exe.addModule("fcft", fcft);
    exe.addModule("pixman", pixman);
    exe.addModule("udev", udev);
    exe.addModule("wayland", wayland);

    exe.linkLibC();
    exe.linkSystemLibrary("fcft");


@@ 63,14 60,16 @@ pub fn build(b: *std.build.Builder) void {
    exe.linkSystemLibrary("libpulse");
    exe.linkSystemLibrary("wayland-client");

    exe.install();
    scanner.addCSource(exe);

    b.installArtifact(exe);

    const run_cmd = exe.run();
    run_cmd.step.dependOn(b.getInstallStep());
    const run = b.addRunArtifact(exe);
    run.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
        run.addArgs(args);
    }

    const run_step = b.step("run", "Run levee");
    run_step.dependOn(&run_cmd.step);
    run_step.dependOn(&run.step);
}

M deps/zig-fcft => deps/zig-fcft +1 -1
@@ 1,1 1,1 @@
Subproject commit cf6d6057faa8ec7b149c53005ba6bd247c8b6dff
Subproject commit 1091c57e3427a0dcc9945ba02b94a37221689ff3

M deps/zig-pixman => deps/zig-pixman +1 -1
@@ 1,1 1,1 @@
Subproject commit 4a49ba13eb9ebb0c0f991de924328e3d615bf283
Subproject commit b0a961079d80059ef56565e23c5f7a8b6a911196

M deps/zig-udev => deps/zig-udev +1 -1
@@ 1,1 1,1 @@
Subproject commit 01d0a1aadc67df24a65d7c39efce2fd0f9d0bdfc
Subproject commit 199ed84cc9e797e315be030326908fd25d88cc19

M deps/zig-wayland => deps/zig-wayland +1 -1
@@ 1,1 1,1 @@
Subproject commit cf165f481ed093f182edef39dbea398e937df4dc
Subproject commit b9c6fcb8cab3a85c5583ef371055cb589b1e7b18

M src/Bar.zig => src/Bar.zig +2 -2
@@ 90,8 90,8 @@ fn layerSurfaceListener(
    switch (event) {
        .configure => |data| {
            bar.configured = true;
            bar.width = @intCast(u16, data.width);
            bar.height = @intCast(u16, data.height);
            bar.width = @intCast(data.width);
            bar.height = @intCast(data.height);

            layerSurface.ackConfigure(data.serial);


M src/Input.zig => src/Input.zig +2 -2
@@ 86,9 86,9 @@ fn pointerListener(
                const tagsSurface = bar.tags.surface;
                if (input.pointer.surface != tagsSurface) return;

                const x = @intCast(u32, input.pointer.x);
                const x = input.pointer.x;
                if (x < bar.height * 9) {
                    bar.monitor.tags.handleClick(x, input) catch return;
                    bar.monitor.tags.handleClick(@intCast(x), input) catch return;
                }
            }
        },

M src/Loop.zig => src/Loop.zig +1 -1
@@ 19,7 19,7 @@ pub fn init() !Loop {
    _ = os.linux.sigprocmask(os.linux.SIG.BLOCK, &mask, null);
    const sfd = os.linux.signalfd(-1, &mask, os.linux.SFD.NONBLOCK);

    return Loop{ .sfd = @intCast(os.fd_t, sfd) };
    return Loop{ .sfd = @intCast(sfd) };
}

pub fn run(self: *Loop) !void {

M src/Tags.zig => src/Tags.zig +8 -8
@@ 25,8 25,8 @@ pub fn create(monitor: *Monitor) !*Tags {

    self.monitor = monitor;
    self.output_status = try manager.getRiverOutputStatus(monitor.output);
    for (self.tags) |*tag, i| {
        tag.label = '1' + @intCast(u8, i);
    for (&self.tags, 0..) |*tag, i| {
        tag.label = '1' + @as(u8, @intCast(i));
    }

    self.output_status.setListener(*Tags, outputStatusListener, self);


@@ 45,18 45,18 @@ fn outputStatusListener(
) void {
    switch (event) {
        .focused_tags => |data| {
            for (tags.tags) |*tag, i| {
                const mask = @as(u32, 1) << @intCast(u5, i);
            for (&tags.tags, 0..) |*tag, i| {
                const mask = @as(u32, 1) << @as(u5, @intCast(i));
                tag.focused = data.tags & mask != 0;
            }
        },
        .view_tags => |data| {
            for (tags.tags) |*tag| {
            for (&tags.tags) |*tag| {
                tag.occupied = false;
            }
            for (data.tags.slice(u32)) |view| {
                for (tags.tags) |*tag, i| {
                    const mask = @as(u32, 1) << @intCast(u5, i);
                for (&tags.tags, 0..) |*tag, i| {
                    const mask = @as(u32, 1) << @as(u5, @intCast(i));
                    if (view & mask != 0) tag.occupied = true;
                }
            }


@@ 79,7 79,7 @@ pub fn handleClick(self: *Tags, x: u32, input: *Input) !void {
        const payload = try std.fmt.allocPrintZ(
            state.gpa,
            "{d}",
            .{@as(u32, 1) << @intCast(u5, index)},
            .{@as(u32, 1) << @as(u5, @intCast(index))},
        );
        defer state.gpa.free(payload);


M src/Wayland.zig => src/Wayland.zig +13 -15
@@ 3,7 3,6 @@ const log = std.log;
const mem = std.mem;
const meta = std.meta;
const os = std.os;
const strcmp = std.cstr.cmp;

const wl = @import("wayland").client.wl;
const wp = @import("wayland").client.wp;


@@ 36,11 35,10 @@ inputs: std.ArrayList(*Input),

pub fn init() !Wayland {
    const display = try wl.Display.connect(null);
    const wfd = @intCast(os.fd_t, display.getFd());

    return Wayland{
        .display = display,
        .fd = wfd,
        .fd = @intCast(display.getFd()),
        .monitors = std.ArrayList(*Monitor).init(state.gpa),
        .inputs = std.ArrayList(*Input).init(state.gpa),
    };


@@ 98,13 96,13 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, self: *Way
            self.bindGlobal(registry, g.name, g.interface) catch unreachable;
        },
        .global_remove => |g| {
            for (self.monitors.items) |monitor, i| {
            for (self.monitors.items, 0..) |monitor, i| {
                if (monitor.globalName == g.name) {
                    self.monitors.swapRemove(i).destroy();
                    break;
                }
            }
            for (self.inputs.items) |input, i| {
            for (self.inputs.items, 0..) |input, i| {
                if (input.globalName == g.name) {
                    self.inputs.swapRemove(i).destroy();
                    break;


@@ 115,26 113,26 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, self: *Way
}

fn bindGlobal(self: *Wayland, registry: *wl.Registry, name: u32, iface: [*:0]const u8) !void {
    if (strcmp(iface, wl.Compositor.getInterface().name) == 0) {
    if (mem.orderZ(u8, iface, wl.Compositor.getInterface().name) == .eq) {
        self.compositor = try registry.bind(name, wl.Compositor, 4);
    } else if (strcmp(iface, wl.Subcompositor.getInterface().name) == 0) {
    } else if (mem.orderZ(u8, iface, wl.Subcompositor.getInterface().name) == .eq) {
        self.subcompositor = try registry.bind(name, wl.Subcompositor, 1);
    } else if (strcmp(iface, wl.Shm.getInterface().name) == 0) {
    } else if (mem.orderZ(u8, iface, wl.Shm.getInterface().name) == .eq) {
        self.shm = try registry.bind(name, wl.Shm, 1);
    } else if (strcmp(iface, wp.Viewporter.getInterface().name) == 0) {
    } else if (mem.orderZ(u8, iface, wp.Viewporter.getInterface().name) == .eq) {
        self.viewporter = try registry.bind(name, wp.Viewporter, 1);
    } else if (strcmp(iface, wp.SinglePixelBufferManagerV1.getInterface().name) == 0) {
    } else if (mem.orderZ(u8, iface, wp.SinglePixelBufferManagerV1.getInterface().name) == .eq) {
        self.single_pixel_buffer_manager = try registry.bind(name, wp.SinglePixelBufferManagerV1, 1);
    } else if (strcmp(iface, zwlr.LayerShellV1.getInterface().name) == 0) {
    } else if (mem.orderZ(u8, iface, zwlr.LayerShellV1.getInterface().name) == .eq) {
        self.layer_shell = try registry.bind(name, zwlr.LayerShellV1, 1);
    } else if (strcmp(iface, zriver.StatusManagerV1.getInterface().name) == 0) {
    } else if (mem.orderZ(u8, iface, zriver.StatusManagerV1.getInterface().name) == .eq) {
        self.status_manager = try registry.bind(name, zriver.StatusManagerV1, 1);
    } else if (strcmp(iface, zriver.ControlV1.getInterface().name) == 0) {
    } else if (mem.orderZ(u8, iface, zriver.ControlV1.getInterface().name) == .eq) {
        self.control = try registry.bind(name, zriver.ControlV1, 1);
    } else if (strcmp(iface, wl.Output.getInterface().name) == 0) {
    } else if (mem.orderZ(u8, iface, wl.Output.getInterface().name) == .eq) {
        const monitor = try Monitor.create(registry, name);
        try self.monitors.append(monitor);
    } else if (strcmp(iface, wl.Seat.getInterface().name) == 0) {
    } else if (mem.orderZ(u8, iface, wl.Seat.getInterface().name) == .eq) {
        const input = try Input.create(registry, name);
        try self.inputs.append(input);
    }

M src/modules/Backlight.zig => src/modules/Backlight.zig +3 -3
@@ 69,9 69,9 @@ pub fn refresh(self: *Backlight) !void {
pub fn print(self: *Backlight, writer: anytype) !void {
    try updateDevices(state.gpa, self.context, &self.devices);
    const device = self.devices.items[0];
    var percent = @intToFloat(f64, device.value) * 100.0;
    percent /= @intToFloat(f64, device.max);
    const value = @floatToInt(u8, @round(percent));
    var percent = @as(f64, @floatFromInt(device.value)) * 100.0;
    percent /= @as(f64, @floatFromInt(device.max));
    const value = @as(u8, @intFromFloat(@round(percent)));

    try writer.print("💡   {d}%", .{value});
}

M src/modules/Battery.zig => src/modules/Battery.zig +4 -4
@@ 36,8 36,8 @@ pub fn init() !Battery {
            .it_interval = .{ .tv_sec = 10, .tv_nsec = 0 },
            .it_value = .{ .tv_sec = 10, .tv_nsec = 0 },
        };
        _ = os.linux.timerfd_settime(@intCast(i32, fd), 0, &interval, null);
        break :tfd @intCast(os.fd_t, fd);
        _ = os.linux.timerfd_settime(@as(i32, @intCast(fd)), 0, &interval, null);
        break :tfd @as(os.fd_t, @intCast(fd));
    };

    const context = try udev.Udev.new();


@@ 161,7 161,7 @@ fn computeCapacityFromEnergy(dev: *udev.Device) !u8 {
    const energy_full = try fmt.parseFloat(f64, energy_full_str);

    const capacity = energy * 100.0 / energy_full;
    return @floatToInt(u8, @round(capacity));
    return @as(u8, @intFromFloat(@round(capacity)));
}

fn computeCapacityFromCharge(dev: *udev.Device) !u8 {


@@ 172,5 172,5 @@ fn computeCapacityFromCharge(dev: *udev.Device) !u8 {
    const charge_full = try fmt.parseFloat(f64, charge_full_str);

    const capacity = charge * 100.0 / charge_full;
    return @floatToInt(u8, @round(capacity));
    return @as(u8, @intFromFloat(@round(capacity)));
}

M src/modules/Pulse.zig => src/modules/Pulse.zig +5 -5
@@ 27,7 27,7 @@ pub fn init() !Pulse {
    // create descriptor for poll in Loop
    const efd = efd: {
        const fd = try os.eventfd(0, os.linux.EFD.NONBLOCK);
        break :efd @intCast(os.fd_t, fd);
        break :efd @as(os.fd_t, @intCast(fd));
    };

    // setup pulseaudio api


@@ 63,7 63,7 @@ pub fn start(self: *Pulse) !void {
    pulse.pa_context_set_state_callback(
        self.context,
        contextStateCallback,
        @ptrCast(*anyopaque, self),
        @as(*anyopaque, @ptrCast(self)),
    );
    const started = pulse.pa_threaded_mainloop_start(self.mainloop);
    if (started < 0) return error.StartFailed;


@@ 187,9 187,9 @@ export fn sinkInfoCallback(

    self.volume = volume: {
        const avg = pulse.pa_cvolume_avg(&info.volume);
        const norm = @intToFloat(f64, pulse.PA_VOLUME_NORM);
        const ratio = 100 * @intToFloat(f64, avg) / norm;
        break :volume @floatToInt(u8, @round(ratio));
        const norm = @as(f64, @floatFromInt(pulse.PA_VOLUME_NORM));
        const ratio = 100 * @as(f64, @floatFromInt(avg)) / norm;
        break :volume @as(u8, @intFromFloat(@round(ratio)));
    };
    self.muted = info.mute != 0;


M src/render.zig => src/render.zig +19 -19
@@ 30,9 30,9 @@ pub fn renderTags(bar: *Bar) !void {
    if (buffer.buffer == null) return;
    buffer.busy = true;

    for (tags) |*tag, i| {
        const offset = @intCast(i16, bar.height * i);
        try renderTag(buffer.pix.?, tag, bar.height, offset);
    for (&tags, 0..) |*tag, i| {
        const offset = bar.height * i;
        try renderTag(buffer.pix.?, tag, bar.height, @intCast(offset));
    }

    surface.setBufferScale(bar.monitor.scale);


@@ 59,13 59,13 @@ pub fn renderClock(bar: *Bar) !void {
    var i: usize = 0;
    var width: u16 = 0;
    while (i < run.count) : (i += 1) {
        width += @intCast(u16, run.glyphs[i].advance.x);
        width += @as(u16, @intCast(run.glyphs[i].advance.x));
    }

    // set subsurface offset
    const font_height = @intCast(u32, font.height);
    const x_offset = @intCast(i32, (bar.width - width) / 2);
    const y_offset = @intCast(i32, (bar.height - font_height) / 2);
    const font_height = @as(u32, @intCast(font.height));
    const x_offset = @as(i32, @intCast((bar.width - width) / 2));
    const y_offset = @as(i32, @intCast((bar.height - font_height) / 2));
    bar.clock.subsurface.setPosition(x_offset, y_offset);

    const buffers = &bar.clock.buffers;


@@ 84,10 84,10 @@ pub fn renderClock(bar: *Bar) !void {
    var color = pixman.Image.createSolidFill(&state.config.foregroundColor).?;
    while (i < run.count) : (i += 1) {
        const glyph = run.glyphs[i];
        x += @intCast(i32, glyph.x);
        const y = state.config.font.ascent - @intCast(i32, glyph.y);
        x += @as(i32, @intCast(glyph.x));
        const y = state.config.font.ascent - @as(i32, @intCast(glyph.y));
        pixman.Image.composite32(.over, color, glyph.pix, buffer.pix.?, 0, 0, 0, 0, x, y, glyph.width, glyph.height);
        x += glyph.advance.x - @intCast(i32, glyph.x);
        x += glyph.advance.x - @as(i32, @intCast(glyph.x));
    }

    surface.setBufferScale(bar.monitor.scale);


@@ 126,13 126,13 @@ pub fn renderModules(bar: *Bar) !void {
    var i: usize = 0;
    var width: u16 = 0;
    while (i < run.count) : (i += 1) {
        width += @intCast(u16, run.glyphs[i].advance.x);
        width += @as(u16, @intCast(run.glyphs[i].advance.x));
    }

    // set subsurface offset
    const font_height = @intCast(u32, state.config.font.height);
    var x_offset = @intCast(i32, bar.width - width);
    var y_offset = @intCast(i32, @divFloor(bar.height - font_height, 2));
    const font_height = @as(u32, @intCast(state.config.font.height));
    var x_offset = @as(i32, @intCast(bar.width - width));
    var y_offset = @as(i32, @intCast(@divFloor(bar.height - font_height, 2)));
    bar.modules.subsurface.setPosition(x_offset, y_offset);

    const buffers = &bar.modules.buffers;


@@ 151,10 151,10 @@ pub fn renderModules(bar: *Bar) !void {
    var color = pixman.Image.createSolidFill(&state.config.foregroundColor).?;
    while (i < run.count) : (i += 1) {
        const glyph = run.glyphs[i];
        x += @intCast(i32, glyph.x);
        const y = state.config.font.ascent - @intCast(i32, glyph.y);
        x += @as(i32, @intCast(glyph.x));
        const y = state.config.font.ascent - @as(i32, @intCast(glyph.y));
        pixman.Image.composite32(.over, color, glyph.pix, buffer.pix.?, 0, 0, 0, 0, x, y, glyph.width, glyph.height);
        x += glyph.advance.x - @intCast(i32, glyph.x);
        x += glyph.advance.x - @as(i32, @intCast(glyph.x));
    }

    surface.setBufferScale(bar.monitor.scale);


@@ 168,7 168,7 @@ fn renderTag(
    height: u16,
    offset: i16,
) !void {
    const size = @intCast(u16, height);
    const size = @as(u16, @intCast(height));

    const outer = [_]pixman.Rectangle16{
        .{ .x = offset, .y = 0, .width = size, .height = size },


@@ 217,5 217,5 @@ fn formatDatetime() ![]const u8 {
        state.config.clockFormat,
        local,
    );
    return state.gpa.resize(buf, len).?;
    return state.gpa.realloc(buf, len);
}

M src/utils.zig => src/utils.zig +5 -7
@@ 8,7 8,7 @@ const unicode = std.unicode;
pub fn cast(comptime to: type) fn (*anyopaque) *to {
    return (struct {
        pub fn cast(module: *anyopaque) *to {
            return @ptrCast(*to, @alignCast(@alignOf(to), module));
            return @ptrCast(@alignCast(module));
        }
    }).cast;
}


@@ 17,12 17,10 @@ pub fn toUtf8(gpa: mem.Allocator, bytes: []const u8) ![]u32 {
    const utf8 = try unicode.Utf8View.init(bytes);
    var iter = utf8.iterator();

    var runes = try gpa.alloc(u32, bytes.len);
    var i: usize = 0;
    while (iter.nextCodepoint()) |rune| : (i += 1) {
        runes[i] = rune;
    var runes = try std.ArrayList(u32).initCapacity(gpa, bytes.len);
    while (iter.nextCodepoint()) |rune| {
        runes.appendAssumeCapacity(rune);
    }

    runes = gpa.resize(runes, i).?;
    return runes;
    return runes.toOwnedSlice();
}