commit 27e73da715bc9ace58bef0d1082cc364a8a35064
parent 7c8a37dde0fda81570d11f35bdb48965cd292876
Author: Andrea Feletto <andrea@andreafeletto.com>
Date: Thu, 7 Apr 2022 19:54:47 +0200
major refactor and add alsa module
Diffstat:
M | README.md | | | 1 | - |
M | build.zig | | | 38 | +++++++++++++++----------------------- |
A | src/Buffer.zig | | | 66 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/Loop.zig | | | 85 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/Surface.zig | | | 146 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/Tags.zig | | | 93 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/c.zig | | | 1 | - |
D | src/event.zig | | | 135 | ------------------------------------------------------------------------------- |
M | src/main.zig | | | 33 | ++++++++++++++++++++++----------- |
M | src/modules.zig | | | 237 | ++++--------------------------------------------------------------------------- |
A | src/modules/Alsa.zig | | | 127 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/modules/Backlight.zig | | | 150 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/modules/Battery.zig | | | 205 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/render.zig | | | 63 | ++++++++++++++++++++++++++++++++------------------------------- |
D | src/shm.zig | | | 69 | --------------------------------------------------------------------- |
D | src/tags.zig | | | 93 | ------------------------------------------------------------------------------- |
A | src/utils.zig | | | 15 | +++++++++++++++ |
M | src/wayland.zig | | | 402 | ++++++++++++++++++++++++++++++++----------------------------------------------- |
18 files changed, 1126 insertions(+), 833 deletions(-)
diff --git a/README.md b/README.md
@@ -9,7 +9,6 @@ Some important things are not implemented yet:
* configuration via cli flags
* configuration via config file
-* volume information
## Build
diff --git a/build.zig b/build.zig
@@ -7,12 +7,19 @@ pub fn build(b: *std.build.Builder) void {
const target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();
+ const exe = b.addExecutable("levee", "src/main.zig");
+ exe.setTarget(target);
+ exe.setBuildMode(mode);
+
const scanner = ScanProtocolsStep.create(b);
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.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");
+ exe.step.dependOn(&scanner.step);
+ scanner.addCSource(exe);
+
const wayland = Pkg{
.name = "wayland",
.path = .{ .generated = &scanner.result },
@@ -31,25 +38,17 @@ pub fn build(b: *std.build.Builder) void {
.path = .{ .path = "deps/zig-udev/udev.zig" },
};
- const exe = b.addExecutable("levee", "src/main.zig");
- exe.setTarget(target);
- exe.setBuildMode(mode);
-
- exe.linkLibC();
-
- exe.addPackage(wayland);
- exe.linkSystemLibrary("wayland-client");
- exe.step.dependOn(&scanner.step);
- scanner.addCSource(exe);
-
+ exe.addPackage(fcft);
exe.addPackage(pixman);
- exe.linkSystemLibrary("pixman-1");
+ exe.addPackage(udev);
+ exe.addPackage(wayland);
- exe.addPackage(fcft);
+ exe.linkLibC();
+ exe.linkSystemLibrary("alsa");
exe.linkSystemLibrary("fcft");
-
- exe.addPackage(udev);
exe.linkSystemLibrary("libudev");
+ exe.linkSystemLibrary("pixman-1");
+ exe.linkSystemLibrary("wayland-client");
exe.install();
@@ -59,13 +58,6 @@ pub fn build(b: *std.build.Builder) void {
run_cmd.addArgs(args);
}
- const run_step = b.step("run", "Run the app");
+ const run_step = b.step("run", "Run levee");
run_step.dependOn(&run_cmd.step);
-
- const exe_tests = b.addTest("src/main.zig");
- exe_tests.setTarget(target);
- exe_tests.setBuildMode(mode);
-
- const test_step = b.step("test", "Run unit tests");
- test_step.dependOn(&exe_tests.step);
}
diff --git a/src/Buffer.zig b/src/Buffer.zig
@@ -0,0 +1,66 @@
+const std = @import("std");
+const mem = std.mem;
+const os = std.os;
+
+const pixman = @import("pixman");
+const wl = @import("wayland").client.wl;
+
+const Buffer = @This();
+
+data: ?[]align(4096) u8,
+buffer: ?*wl.Buffer,
+pix: ?*pixman.Image,
+busy: bool,
+width: u31,
+height: u31,
+size: u31,
+
+pub fn init(self: *Buffer, shm: *wl.Shm, width: u31, height: u31) !void {
+ self.busy = true;
+ self.width = width;
+ self.height = height;
+
+ const fd = try os.memfd_create("levee-wayland-shm-buffer-pool", 0);
+ defer os.close(fd);
+
+ const stride = width * 4;
+ self.size = stride * height;
+ try os.ftruncate(fd, self.size);
+
+ self.data = try os.mmap(null, self.size, os.PROT.READ | os.PROT.WRITE, os.MAP.SHARED, fd, 0);
+ errdefer os.munmap(self.data.?);
+
+ const pool = try shm.createPool(fd, self.size);
+ defer pool.destroy();
+
+ self.buffer = try pool.createBuffer(0, width, height, stride, .argb8888);
+ errdefer self.buffer.?.destroy();
+ self.buffer.?.setListener(*Buffer, listener, self);
+
+ self.pix = pixman.Image.createBitsNoClear(.a8r8g8b8, width, height, @ptrCast([*c]u32, self.data.?), stride);
+}
+
+pub fn deinit(self: *Buffer) void {
+ if (self.pix) |pix| _ = pix.unref();
+ if (self.buffer) |buf| buf.destroy();
+ if (self.data) |data| os.munmap(data);
+}
+
+fn listener(_: *wl.Buffer, event: wl.Buffer.Event, buffer: *Buffer) void {
+ switch (event) {
+ .release => buffer.busy = false,
+ }
+}
+
+pub fn nextBuffer(pool: *[2]Buffer, shm: *wl.Shm, width: u16, height: u16) !*Buffer {
+ if (pool[0].busy and pool[1].busy) {
+ return error.NoAvailableBuffers;
+ }
+ const buffer = if (!pool[0].busy) &pool[0] else &pool[1];
+
+ if (buffer.width != width or buffer.height != height) {
+ buffer.deinit();
+ try buffer.init(shm, width, height);
+ }
+ return buffer;
+}
diff --git a/src/Loop.zig b/src/Loop.zig
@@ -0,0 +1,85 @@
+const std = @import("std");
+const fmt = std.fmt;
+const mem = std.mem;
+const os = std.os;
+
+const wl = @import("wayland").client.wl;
+
+const Module = @import("modules.zig").Module;
+const State = @import("main.zig").State;
+const utils = @import("utils.zig");
+const Loop = @This();
+
+state: *State,
+sfd: os.fd_t,
+
+pub const Event = struct {
+ fd: os.pollfd,
+ data: *anyopaque,
+ callbackIn: Callback,
+ callbackOut: Callback,
+
+ pub const Callback = fn (*anyopaque) error{Terminate}!void;
+
+ pub fn terminate(_: *anyopaque) error{Terminate}!void {
+ return error.Terminate;
+ }
+
+ pub fn noop(_: *anyopaque) error{Terminate}!void {
+ return;
+ }
+};
+
+pub fn init(state: *State) !Loop {
+ var mask = mem.zeroes(os.linux.sigset_t);
+ os.linux.sigaddset(&mask, os.linux.SIG.INT);
+ os.linux.sigaddset(&mask, os.linux.SIG.TERM);
+ os.linux.sigaddset(&mask, os.linux.SIG.QUIT);
+ _ = os.linux.sigprocmask(os.linux.SIG.BLOCK, &mask, null);
+ const sfd = os.linux.signalfd(-1, &mask, os.linux.SFD.NONBLOCK);
+
+ return Loop{ .state = state, .sfd = @intCast(os.fd_t, sfd) };
+}
+
+pub fn run(self: *Loop) !void {
+ const gpa = self.state.gpa;
+ const display = self.state.wayland.display;
+
+ var events: std.MultiArrayList(Event) = .{};
+ defer events.deinit(gpa);
+
+ try events.append(gpa, .{
+ .fd = .{ .fd = self.sfd, .events = os.POLL.IN, .revents = 0 },
+ .data = undefined,
+ .callbackIn = Event.terminate,
+ .callbackOut = Event.noop,
+ });
+ try events.append(gpa, try self.state.wayland.getEvent());
+ for (self.state.modules.items) |*module| {
+ try events.append(gpa, try module.getEvent());
+ }
+
+ const fds = events.items(.fd);
+ while (true) {
+ while (true) {
+ const ret = try display.dispatchPending();
+ _ = try display.flush();
+ if (ret <= 0) break;
+ }
+ _ = try os.poll(fds, -1);
+
+ for (fds) |fd, i| {
+ if (fd.revents & os.POLL.HUP != 0) return;
+ if (fd.revents & os.POLL.ERR != 0) return;
+
+ if (fd.revents & os.POLL.IN != 0) {
+ const event = events.get(i);
+ event.callbackIn(event.data) catch return;
+ }
+ if (fd.revents & os.POLL.OUT != 0) {
+ const event = events.get(i);
+ event.callbackOut(event.data) catch return;
+ }
+ }
+ }
+}
diff --git a/src/Surface.zig b/src/Surface.zig
@@ -0,0 +1,146 @@
+const std = @import("std");
+const mem = std.mem;
+
+const wl = @import("wayland").client.wl;
+const zwlr = @import("wayland").client.zwlr;
+
+const Buffer = @import("Buffer.zig");
+const Monitor = @import("wayland.zig").Monitor;
+const render = @import("render.zig");
+const Surface = @This();
+
+monitor: *Monitor,
+
+backgroundSurface: *wl.Surface,
+layerSurface: *zwlr.LayerSurfaceV1,
+backgroundBuffers: [2]Buffer,
+
+tagsSurface: *wl.Surface,
+tagsSubsurface: *wl.Subsurface,
+tagsBuffers: [2]Buffer,
+
+clockSurface: *wl.Surface,
+clockSubsurface: *wl.Subsurface,
+clockBuffers: [2]Buffer,
+
+modulesSurface: *wl.Surface,
+modulesSubsurface: *wl.Subsurface,
+modulesBuffers: [2]Buffer,
+
+configured: bool,
+width: u16,
+height: u16,
+
+pub fn create(monitor: *Monitor) !*Surface {
+ const state = monitor.state;
+ const globals = &state.wayland.globals;
+
+ const self = try state.gpa.create(Surface);
+ self.monitor = monitor;
+ self.configured = false;
+
+ self.backgroundSurface = try globals.compositor.createSurface();
+ self.layerSurface = try globals.layerShell.getLayerSurface(
+ self.backgroundSurface,
+ monitor.output,
+ .top,
+ "levee",
+ );
+ self.backgroundBuffers = mem.zeroes([2]Buffer);
+
+ self.tagsSurface = try globals.compositor.createSurface();
+ self.tagsSubsurface = try globals.subcompositor.getSubsurface(
+ self.tagsSurface,
+ self.backgroundSurface,
+ );
+ self.tagsBuffers = mem.zeroes([2]Buffer);
+
+ self.clockSurface = try globals.compositor.createSurface();
+ self.clockSubsurface = try globals.subcompositor.getSubsurface(
+ self.clockSurface,
+ self.backgroundSurface,
+ );
+ self.clockBuffers = mem.zeroes([2]Buffer);
+
+ self.modulesSurface = try globals.compositor.createSurface();
+ self.modulesSubsurface = try globals.subcompositor.getSubsurface(
+ self.modulesSurface,
+ self.backgroundSurface,
+ );
+ self.modulesBuffers = mem.zeroes([2]Buffer);
+
+ // setup layer surface
+ self.layerSurface.setSize(0, state.config.height);
+ self.layerSurface.setAnchor(
+ .{ .top = true, .left = true, .right = true, .bottom = false },
+ );
+ self.layerSurface.setExclusiveZone(state.config.height);
+ self.layerSurface.setMargin(0, 0, 0, 0);
+ self.layerSurface.setListener(*Surface, layerSurfaceListener, self);
+
+ // setup subsurfaces
+ self.tagsSubsurface.setPosition(0, 0);
+ self.clockSubsurface.setPosition(0, 0);
+ self.modulesSubsurface.setPosition(0, 0);
+
+ self.tagsSurface.commit();
+ self.clockSurface.commit();
+ self.backgroundSurface.commit();
+
+ return self;
+}
+
+pub fn destroy(self: *Surface) void {
+ self.monitor.surface = null;
+
+ self.backgroundSurface.destroy();
+ self.layerSurface.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.modulesSurface.destroy();
+ self.modulesSubsurface.destroy();
+ self.modulesBuffers[0].deinit();
+ self.modulesBuffers[1].deinit();
+
+ self.monitor.state.gpa.destroy(self);
+}
+
+fn layerSurfaceListener(
+ layerSurface: *zwlr.LayerSurfaceV1,
+ event: zwlr.LayerSurfaceV1.Event,
+ surface: *Surface,
+) void {
+ switch (event) {
+ .configure => |data| {
+ surface.configured = true;
+ surface.width = @intCast(u16, data.width);
+ surface.height = @intCast(u16, data.height);
+
+ layerSurface.ackConfigure(data.serial);
+
+ render.renderBackground(surface) catch return;
+ render.renderTags(surface) catch return;
+ render.renderClock(surface) catch return;
+ render.renderModules(surface) catch return;
+
+ surface.tagsSurface.commit();
+ surface.clockSurface.commit();
+ surface.modulesSurface.commit();
+ surface.backgroundSurface.commit();
+ },
+ .closed => {
+ surface.destroy();
+ },
+ }
+}
diff --git a/src/Tags.zig b/src/Tags.zig
@@ -0,0 +1,93 @@
+const std = @import("std");
+
+const zriver = @import("wayland").client.zriver;
+
+const Monitor = @import("wayland.zig").Monitor;
+const render = @import("render.zig");
+const Input = @import("wayland.zig").Input;
+const State = @import("main.zig").State;
+const Tags = @This();
+
+monitor: *Monitor,
+outputStatus: *zriver.OutputStatusV1,
+tags: [9]Tag,
+
+pub const Tag = struct {
+ label: u8,
+ focused: bool = false,
+ occupied: bool = false,
+};
+
+pub fn create(state: *State, monitor: *Monitor) !*Tags {
+ const self = try state.gpa.create(Tags);
+ const globals = &state.wayland.globals;
+
+ self.monitor = monitor;
+ self.outputStatus = try globals.statusManager.getRiverOutputStatus(
+ monitor.output,
+ );
+ for (self.tags) |*tag, i| {
+ tag.label = '1' + @intCast(u8, i);
+ }
+
+ self.outputStatus.setListener(*Tags, outputStatusListener, self);
+ return self;
+}
+
+pub fn destroy(self: *Tags) void {
+ self.outputStatus.destroy();
+ self.monitor.state.gpa.destroy(self);
+}
+
+fn outputStatusListener(
+ _: *zriver.OutputStatusV1,
+ event: zriver.OutputStatusV1.Event,
+ tags: *Tags,
+) void {
+ switch (event) {
+ .focused_tags => |data| {
+ for (tags.tags) |*tag, i| {
+ const mask = @as(u32, 1) << @intCast(u5, i);
+ tag.focused = data.tags & mask != 0;
+ }
+ },
+ .view_tags => |data| {
+ 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);
+ if (view & mask != 0) tag.occupied = true;
+ }
+ }
+ },
+ }
+ if (tags.monitor.surface) |surface| {
+ if (surface.configured) {
+ render.renderTags(surface) catch return;
+ surface.tagsSurface.commit();
+ surface.backgroundSurface.commit();
+ }
+ }
+}
+
+pub fn handleClick(self: *Tags, x: u32, input: *Input) !void {
+ const state = self.monitor.state;
+ const control = state.wayland.globals.control;
+
+ if (self.monitor.surface) |surface| {
+ const index = x / surface.height;
+ const payload = try std.fmt.allocPrintZ(
+ state.gpa,
+ "{d}",
+ .{@as(u32, 1) << @intCast(u5, index)},
+ );
+ defer state.gpa.free(payload);
+
+ control.addArgument("set-focused-tags");
+ control.addArgument(payload);
+ const callback = try control.runCommand(input.seat);
+ _ = callback;
+ }
+}
diff --git a/src/c.zig b/src/c.zig
@@ -1 +0,0 @@
-pub const time = @cImport(@cInclude("time.h"));
diff --git a/src/event.zig b/src/event.zig
@@ -1,135 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const os = std.os;
-const ArrayList = std.ArrayList;
-
-const wl = @import("wayland").client.wl;
-const udev = @import("udev");
-
-const render = @import("render.zig");
-const State = @import("main.zig").State;
-
-pub const Loop = struct {
- state: *State,
- fds: [4]os.pollfd,
- monitor: *udev.Monitor,
-
- pub fn init(state: *State) !Loop {
- // signals
- var mask = mem.zeroes(os.linux.sigset_t);
- os.linux.sigaddset(&mask, os.linux.SIG.INT);
- os.linux.sigaddset(&mask, os.linux.SIG.TERM);
- os.linux.sigaddset(&mask, os.linux.SIG.QUIT);
- _ = os.linux.sigprocmask(os.linux.SIG.BLOCK, &mask, null);
- const sfd = os.linux.signalfd(-1, &mask, os.linux.SFD.NONBLOCK);
-
- // wayland
- const wfd = state.wayland.display.getFd();
-
- // timer
- const tfd = os.linux.timerfd_create(
- os.CLOCK.MONOTONIC,
- os.linux.TFD.CLOEXEC,
- );
- const interval: os.linux.itimerspec = .{
- .it_interval = .{ .tv_sec = 10, .tv_nsec = 0 },
- .it_value = .{ .tv_sec = 10, .tv_nsec = 0 },
- };
- _ = os.linux.timerfd_settime(@intCast(i32, tfd), 0, &interval, null);
-
- // udev
- const context = try udev.Udev.new();
- const monitor = try udev.Monitor.newFromNetlink(context, "udev");
- try monitor.filterAddMatchSubsystemDevType("backlight", null);
- try monitor.filterAddMatchSubsystemDevType("power_supply", null);
- try monitor.enableReceiving();
- const ufd = try monitor.getFd();
-
- // poll fds
- const fds: [4]os.fd_t = .{
- @intCast(os.fd_t, sfd),
- @intCast(os.fd_t, wfd),
- @intCast(os.fd_t, tfd),
- @intCast(os.fd_t, ufd),
- };
- var pfds: [4]os.pollfd = undefined;
- for (fds) |fd, i| {
- pfds[i] = .{ .fd = fd, .events = os.POLL.IN, .revents = 0 };
- }
-
- return Loop{
- .state = state,
- .fds = pfds,
- .monitor = monitor,
- };
- }
-
- pub fn run(self: *Loop) !void {
- const display = self.state.wayland.display;
-
- while (true) {
- while (true) {
- const ret = try display.dispatchPending();
- _ = try display.flush();
- if (ret <= 0) break;
- }
- _ = try os.poll(&self.fds, -1);
-
- for (self.fds) |fd| {
- if (fd.revents & os.POLL.HUP != 0) {
- return;
- }
- if (fd.revents & os.POLL.ERR != 0) {
- return;
- }
- }
-
- // signals
- if (self.fds[0].revents & os.POLL.IN != 0) {
- return;
- }
-
- // wayland
- if (self.fds[1].revents & os.POLL.IN != 0) {
- _ = try display.dispatch();
- }
- if (self.fds[1].revents & os.POLL.OUT != 0) {
- _ = try display.flush();
- }
-
- // timer
- if (self.fds[2].revents & os.POLL.IN != 0) {
- const tfd = self.fds[2].fd;
- 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;
- render.renderModules(surface) catch continue;
- surface.clockSurface.commit();
- surface.modulesSurface.commit();
- surface.backgroundSurface.commit();
- }
- }
- }
- }
-
- // udev
- if (self.fds[3].revents & os.POLL.IN != 0) {
- _ = try self.monitor.receiveDevice();
-
- for (self.state.wayland.outputs.items) |output| {
- if (output.surface) |surface| {
- if (surface.configured) {
- render.renderModules(surface) catch continue;
- surface.modulesSurface.commit();
- surface.backgroundSurface.commit();
- }
- }
- }
- }
- }
- }
-};
diff --git a/src/main.zig b/src/main.zig
@@ -1,46 +1,57 @@
const std = @import("std");
+const heap = std.heap;
+const mem = std.mem;
const fcft = @import("fcft");
const Config = @import("config.zig").Config;
-const Loop = @import("event.zig").Loop;
+const Loop = @import("Loop.zig");
const modules = @import("modules.zig");
const Wayland = @import("wayland.zig").Wayland;
pub const State = struct {
- allocator: std.mem.Allocator,
+ gpa: mem.Allocator,
config: Config,
wayland: Wayland,
loop: Loop,
- battery: modules.Battery,
+ alsa: modules.Alsa,
backlight: modules.Backlight,
+ battery: modules.Battery,
modules: std.ArrayList(modules.Module),
};
pub fn main() anyerror!void {
- var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena.deinit();
+ var gpa: heap.GeneralPurposeAllocator(.{}) = .{};
+ defer _ = gpa.deinit();
fcft.init(.auto, false, .info);
// initialization
var state: State = undefined;
- state.allocator = arena.allocator();
+ state.gpa = gpa.allocator();
state.config = try Config.init();
state.wayland = try Wayland.init(&state);
+ defer state.wayland.deinit();
state.loop = try Loop.init(&state);
// modules
- state.modules = std.ArrayList(modules.Module).init(state.allocator);
+ state.modules = std.ArrayList(modules.Module).init(state.gpa);
+ defer state.modules.deinit();
+
+ state.alsa = try modules.Alsa.init(&state);
state.backlight = try modules.Backlight.init(&state);
- try state.modules.append(state.backlight.module());
+ defer state.backlight.deinit();
state.battery = try modules.Battery.init(&state);
- try state.modules.append(state.battery.module());
+ defer state.battery.deinit();
- // wayland
- try state.wayland.registerGlobals();
+ try state.modules.appendSlice(&.{
+ try state.backlight.module(),
+ state.battery.module(),
+ state.alsa.module(),
+ });
// event loop
+ try state.wayland.registerGlobals();
try state.loop.run();
}
diff --git a/src/modules.zig b/src/modules.zig
@@ -1,241 +1,24 @@
const std = @import("std");
-const fmt = std.fmt;
-const fs = std.fs;
-const io = std.io;
-const mem = std.mem;
const os = std.os;
-const udev = @import("udev");
+const Event = @import("Loop.zig").Event;
-const State = @import("main.zig").State;
-
-const StringWriter = std.ArrayList(u8).Writer;
+pub const Alsa = @import("modules/Alsa.zig");
+pub const Backlight = @import("modules/Backlight.zig");
+pub const Battery = @import("modules/Battery.zig");
pub const Module = struct {
impl: *anyopaque,
-
+ eventFn: fn (*anyopaque) anyerror!Event,
printFn: fn (*anyopaque, StringWriter) anyerror!void,
- pub fn print(self: *Module, writer: StringWriter) !void {
- try self.printFn(self.impl, writer);
- }
-
- pub fn cast(comptime to: type) fn (*anyopaque) *to {
- return (struct {
- pub fn cast(module: *anyopaque) *to {
- return @ptrCast(*to, @alignCast(@alignOf(to), module));
- }
- }).cast;
- }
-};
-
-pub const Battery = struct {
- state: *State,
- context: *udev.Udev,
- devices: DeviceList,
-
- const Device = struct {
- name: []const u8,
- status: []const u8,
- capacity: u8,
- };
- const DeviceList = std.ArrayList(Device);
-
- pub fn init(state: *State) !Battery {
- const context = try udev.Udev.new();
-
- var devices = DeviceList.init(state.allocator);
- try updateDevices(state.allocator, context, &devices);
- if (devices.items.len == 0) return error.NoDevicesFound;
-
- return Battery{
- .state = state,
- .context = context,
- .devices = devices,
- };
- }
-
- pub fn module(self: *Battery) Module {
- return .{ .impl = @ptrCast(*anyopaque, self), .printFn = print };
- }
-
- pub fn print(self_opaque: *anyopaque, writer: StringWriter) !void {
- const self = Module.cast(Battery)(self_opaque);
-
- try updateDevices(self.state.allocator, self.context, &self.devices);
- const device = self.devices.items[0];
-
- var icon: []const u8 = "❓";
- if (mem.eql(u8, device.status, "Discharging")) {
- icon = "🔋";
- } else if (mem.eql(u8, device.status, "Charging")) {
- icon = "🔌";
- } else if (mem.eql(u8, device.status, "Full")) {
- icon = "⚡";
- }
-
- try fmt.format(writer, "{s} {d}%", .{ icon, device.capacity });
- }
-
- fn updateDevices(
- allocator: mem.Allocator,
- context: *udev.Udev,
- devices: *DeviceList,
- ) !void {
- const enumerate = try udev.Enumerate.new(context);
- try enumerate.addMatchSubsystem("power_supply");
- try enumerate.addMatchSysattr("type", "Battery");
- try enumerate.scanDevices();
- const entries = enumerate.getListEntry();
-
- var maybe_entry = entries;
- while (maybe_entry) |entry| : (maybe_entry = entry.getNext()) {
- const path = entry.getName();
- const device = try udev.Device.newFromSyspath(context, path);
- try updateOrAppend(allocator, devices, device);
- }
- }
-
- fn updateOrAppend(
- allocator: mem.Allocator,
- devices: *DeviceList,
- dev: *udev.Device,
- ) !void {
- const name = dev.getSysname() catch return;
- const status = dev.getSysattrValue("status") catch return;
- const capacity = getCapacity(dev) catch return;
-
- const device = blk: {
- for (devices.items) |*device| {
- if (mem.eql(u8, device.name, name)) {
- break :blk device;
- }
- } else {
- const device = try devices.addOne();
- device.name = try allocator.dupe(u8, name);
- break :blk device;
- }
- };
-
- device.status = try allocator.dupe(u8, status);
- device.capacity = capacity;
- }
-
- fn getCapacity(dev: *udev.Device) !u8 {
- const capacity_str = dev.getSysattrValue("capacity") catch {
- return computeCapacityFromCharge(dev) catch {
- return computeCapacityFromEnergy(dev);
- };
- };
-
- const capacity = try fmt.parseInt(u8, capacity_str, 10);
- return capacity;
- }
-
- fn computeCapacityFromEnergy(dev: *udev.Device) !u8 {
- const energy_str = try dev.getSysattrValue("energy_now");
- const energy_full_str = try dev.getSysattrValue("energy_full");
-
- const energy = try fmt.parseFloat(f64, energy_str);
- const energy_full = try fmt.parseFloat(f64, energy_full_str);
-
- const capacity = energy * 100.0 / energy_full;
- return @floatToInt(u8, @round(capacity));
- }
-
- fn computeCapacityFromCharge(dev: *udev.Device) !u8 {
- const charge_str = try dev.getSysattrValue("charge_now");
- const charge_full_str = try dev.getSysattrValue("charge_full");
-
- const charge = try fmt.parseFloat(f64, charge_str);
- const charge_full = try fmt.parseFloat(f64, charge_full_str);
+ pub const StringWriter = std.ArrayList(u8).Writer;
- const capacity = charge * 100.0 / charge_full;
- return @floatToInt(u8, @round(capacity));
+ pub fn getEvent(self: *Module) !Event {
+ return self.eventFn(self.impl);
}
-};
-
-pub const Backlight = struct {
- state: *State,
- context: *udev.Udev,
- devices: DeviceList,
-
- const Device = struct {
- name: []const u8,
- value: u64,
- max: u64,
- };
- const DeviceList = std.ArrayList(Device);
-
- pub fn init(state: *State) !Backlight {
- const context = try udev.Udev.new();
-
- var devices = DeviceList.init(state.allocator);
- try updateDevices(state.allocator, context, &devices);
- if (devices.items.len == 0) return error.NoDevicesFound;
- return Backlight{
- .state = state,
- .context = context,
- .devices = devices,
- };
- }
-
- pub fn module(self: *Backlight) Module {
- return .{ .impl = @ptrCast(*anyopaque, self), .printFn = print };
- }
-
- pub fn print(self_opaque: *anyopaque, writer: StringWriter) !void {
- const self = Module.cast(Backlight)(self_opaque);
-
- try updateDevices(self.state.allocator, 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));
-
- try writer.print("💡 {d}%", .{value});
- }
-
- fn updateDevices(
- allocator: mem.Allocator,
- context: *udev.Udev,
- devices: *DeviceList,
- ) !void {
- const enumerate = try udev.Enumerate.new(context);
- try enumerate.addMatchSubsystem("backlight");
- try enumerate.scanDevices();
- const entries = enumerate.getListEntry();
-
- var maybe_entry = entries;
- while (maybe_entry) |entry| : (maybe_entry = entry.getNext()) {
- const path = entry.getName();
- const device = try udev.Device.newFromSyspath(context, path);
- try updateOrAppend(allocator, devices, device);
- }
- }
-
- fn updateOrAppend(
- allocator: mem.Allocator,
- devices: *DeviceList,
- dev: *udev.Device,
- ) !void {
- const value = try dev.getSysattrValue("actual_brightness");
- const max = try dev.getSysattrValue("max_brightness");
- const name = try dev.getSysname();
-
- const device = blk: {
- for (devices.items) |*device| {
- if (mem.eql(u8, device.name, name)) {
- break :blk device;
- }
- } else {
- const device = try devices.addOne();
- device.name = try allocator.dupe(u8, name);
- break :blk device;
- }
- };
- device.value = try fmt.parseInt(u64, value, 10);
- device.max = try fmt.parseInt(u64, max, 10);
+ pub fn print(self: *Module, writer: StringWriter) !void {
+ return self.printFn(self.impl, writer);
}
};
diff --git a/src/modules/Alsa.zig b/src/modules/Alsa.zig
@@ -0,0 +1,127 @@
+const std = @import("std");
+const fmt = std.fmt;
+const math = std.math;
+const mem = std.mem;
+const os = std.os;
+
+const alsa = @cImport(@cInclude("alsa/asoundlib.h"));
+
+const Module = @import("../modules.zig").Module;
+const Event = @import("../Loop.zig").Event;
+const render = @import("../render.zig");
+const State = @import("../main.zig").State;
+const utils = @import("../utils.zig");
+const Alsa = @This();
+
+state: *State,
+context: *alsa.snd_ctl_t,
+
+pub fn init(state: *State) !Alsa {
+ return Alsa{
+ .state = state,
+ .context = try getAlsaCtl(state.gpa),
+ };
+}
+
+pub fn module(self: *Alsa) Module {
+ return .{
+ .impl = @ptrCast(*anyopaque, self),
+ .eventFn = getEvent,
+ .printFn = print,
+ };
+}
+
+fn getEvent(self_opaque: *anyopaque) !Event {
+ const self = utils.cast(Alsa)(self_opaque);
+
+ var fd = mem.zeroes(alsa.pollfd);
+ _ = alsa.snd_ctl_poll_descriptors(self.context, &fd, 1);
+
+ return Event{
+ .fd = @bitCast(os.pollfd, fd),
+ .data = self_opaque,
+ .callbackIn = callbackIn,
+ .callbackOut = Event.noop,
+ };
+}
+
+fn print(self_opaque: *anyopaque, writer: Module.StringWriter) !void {
+ const self = utils.cast(Alsa)(self_opaque);
+ _ = self;
+
+ var handle: ?*alsa.snd_mixer_t = null;
+ _ = alsa.snd_mixer_open(&handle, 0);
+ _ = alsa.snd_mixer_attach(handle, "default");
+ _ = alsa.snd_mixer_selem_register(handle, null, null);
+ _ = alsa.snd_mixer_load(handle);
+
+ var sid: ?*alsa.snd_mixer_selem_id_t = null;
+ _ = alsa.snd_mixer_selem_id_malloc(&sid);
+ defer alsa.snd_mixer_selem_id_free(sid);
+ alsa.snd_mixer_selem_id_set_index(sid, 0);
+ alsa.snd_mixer_selem_id_set_name(sid, "Master");
+ const elem = alsa.snd_mixer_find_selem(handle, sid);
+ _ = elem;
+
+ var unmuted: i32 = 0;
+ _ = alsa.snd_mixer_selem_get_playback_switch(
+ elem,
+ alsa.SND_MIXER_SCHN_MONO,
+ &unmuted,
+ );
+ if (unmuted == 0) {
+ return writer.print(" 🔇 ", .{});
+ }
+
+ var min: i64 = 0;
+ var max: i64 = 0;
+ _ = alsa.snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
+
+ var volume: i64 = 0;
+ _ = alsa.snd_mixer_selem_get_playback_volume(
+ elem,
+ alsa.SND_MIXER_SCHN_MONO,
+ &volume,
+ );
+
+ const percent = percent: {
+ var x = @intToFloat(f64, volume) / @intToFloat(f64, max);
+ x = math.tanh(math.sqrt(x) * 0.65) * 180.0;
+ break :percent @floatToInt(u8, @round(x));
+ };
+ return writer.print("🔊 {d}%", .{ percent });
+}
+
+fn callbackIn(self_opaque: *anyopaque) error{Terminate}!void {
+ const self = utils.cast(Alsa)(self_opaque);
+
+ var event: ?*alsa.snd_ctl_event_t = null;
+ _ = alsa.snd_ctl_event_malloc(&event);
+ defer alsa.snd_ctl_event_free(event);
+ _ = alsa.snd_ctl_read(self.context, event);
+
+ for (self.state.wayland.monitors.items) |monitor| {
+ if (monitor.surface) |surface| {
+ if (surface.configured) {
+ render.renderClock(surface) catch continue;
+ render.renderModules(surface) catch continue;
+ surface.clockSurface.commit();
+ surface.modulesSurface.commit();
+ surface.backgroundSurface.commit();
+ }
+ }
+ }
+}
+
+fn getAlsaCtl(gpa: mem.Allocator) !*alsa.snd_ctl_t {
+ var card: i32 = -1;
+ _ = alsa.snd_card_next(&card);
+ const name = try fmt.allocPrintZ(gpa, "hw:{d}", .{ card });
+ defer gpa.free(name);
+
+ var ctl: ?*alsa.snd_ctl_t = null;
+ _ = alsa.snd_ctl_open(&ctl, name.ptr, alsa.SND_CTL_READONLY);
+ _ = alsa.snd_ctl_subscribe_events(ctl, 1);
+
+ return ctl.?;
+}
diff --git a/src/modules/Backlight.zig b/src/modules/Backlight.zig
@@ -0,0 +1,150 @@
+const std = @import("std");
+const fmt = std.fmt;
+const mem = std.mem;
+const os = std.os;
+
+const udev = @import("udev");
+
+const Module = @import("../modules.zig").Module;
+const Event = @import("../Loop.zig").Event;
+const render = @import("../render.zig");
+const State = @import("../main.zig").State;
+const utils = @import("../utils.zig");
+const Backlight = @This();
+
+state: *State,
+context: *udev.Udev,
+monitor: *udev.Monitor,
+devices: DeviceList,
+
+const Device = struct {
+ name: []const u8,
+ value: u64,
+ max: u64,
+
+ pub fn deinit(self: *Device, gpa: mem.Allocator) void {
+ gpa.free(self.name);
+ }
+};
+
+const DeviceList = std.ArrayList(Device);
+
+pub fn init(state: *State) !Backlight {
+ const context = try udev.Udev.new();
+
+ const monitor = try udev.Monitor.newFromNetlink(context, "udev");
+ try monitor.filterAddMatchSubsystemDevType("backlight", null);
+ try monitor.filterAddMatchSubsystemDevType("power_supply", null);
+ try monitor.enableReceiving();
+
+ var devices = DeviceList.init(state.gpa);
+ try updateDevices(state.gpa, context, &devices);
+ if (devices.items.len == 0) return error.NoDevicesFound;
+
+ return Backlight{
+ .state = state,
+ .context = context,
+ .monitor = monitor,
+ .devices = devices,
+ };
+}
+
+pub fn deinit(self: *Backlight) void {
+ _ = self.context.unref();
+ for (self.devices.items) |*device| {
+ device.deinit(self.state.gpa);
+ }
+ self.devices.deinit();
+}
+
+pub fn module(self: *Backlight) !Module {
+ return Module{
+ .impl = @ptrCast(*anyopaque, self),
+ .eventFn = getEvent,
+ .printFn = print,
+ };
+}
+
+pub fn getEvent(self_opaque: *anyopaque) !Event {
+ const self = utils.cast(Backlight)(self_opaque);
+
+ return Event{
+ .fd = .{
+ .fd = try self.monitor.getFd(),
+ .events = os.POLL.IN,
+ .revents = undefined,
+ },
+ .data = self_opaque,
+ .callbackIn = callbackIn,
+ .callbackOut = Event.noop,
+ };
+}
+
+fn callbackIn(self_opaque: *anyopaque) error{Terminate}!void {
+ const self = utils.cast(Backlight)(self_opaque);
+
+ _ = self.monitor.receiveDevice() catch return;
+ for (self.state.wayland.monitors.items) |monitor| {
+ if (monitor.surface) |surface| {
+ if (surface.configured) {
+ render.renderModules(surface) catch continue;
+ surface.modulesSurface.commit();
+ surface.backgroundSurface.commit();
+ }
+ }
+ }
+}
+
+pub fn print(self_opaque: *anyopaque, writer: Module.StringWriter) !void {
+ const self = utils.cast(Backlight)(self_opaque);
+
+ try updateDevices(self.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));
+
+ try writer.print("💡 {d}%", .{value});
+}
+
+fn updateDevices(
+ gpa: mem.Allocator,
+ context: *udev.Udev,
+ devices: *DeviceList,
+) !void {
+ const enumerate = try udev.Enumerate.new(context);
+ try enumerate.addMatchSubsystem("backlight");
+ try enumerate.scanDevices();
+ const entries = enumerate.getListEntry();
+
+ var maybe_entry = entries;
+ while (maybe_entry) |entry| : (maybe_entry = entry.getNext()) {
+ const path = entry.getName();
+ const device = try udev.Device.newFromSyspath(context, path);
+ try updateOrAppend(gpa, devices, device);
+ }
+}
+
+fn updateOrAppend(
+ gpa: mem.Allocator,
+ devices: *DeviceList,
+ dev: *udev.Device,
+) !void {
+ const value = try dev.getSysattrValue("actual_brightness");
+ const max = try dev.getSysattrValue("max_brightness");
+ const name = try dev.getSysname();
+
+ const device = blk: {
+ for (devices.items) |*device| {
+ if (mem.eql(u8, device.name, name)) {
+ break :blk device;
+ }
+ } else {
+ const device = try devices.addOne();
+ device.name = try gpa.dupe(u8, name);
+ break :blk device;
+ }
+ };
+ device.value = try fmt.parseInt(u64, value, 10);
+ device.max = try fmt.parseInt(u64, max, 10);
+}
diff --git a/src/modules/Battery.zig b/src/modules/Battery.zig
@@ -0,0 +1,205 @@
+const std = @import("std");
+const fmt = std.fmt;
+const mem = std.mem;
+const os = std.os;
+
+const udev = @import("udev");
+
+const Module = @import("../modules.zig").Module;
+const Event = @import("../Loop.zig").Event;
+const render = @import("../render.zig");
+const State = @import("../main.zig").State;
+const utils = @import("../utils.zig");
+const Battery = @This();
+
+state: *State,
+context: *udev.Udev,
+timerFd: os.fd_t,
+devices: DeviceList,
+
+const Device = struct {
+ name: []const u8,
+ status: []const u8,
+ capacity: u8,
+
+ pub fn deinit(self: *Device, gpa: mem.Allocator) void {
+ gpa.free(self.name);
+ gpa.free(self.status);
+ }
+};
+
+const DeviceList = std.ArrayList(Device);
+
+pub fn init(state: *State) !Battery {
+ const tfd = os.linux.timerfd_create(
+ os.CLOCK.MONOTONIC,
+ os.linux.TFD.CLOEXEC,
+ );
+ const interval: os.linux.itimerspec = .{
+ .it_interval = .{ .tv_sec = 10, .tv_nsec = 0 },
+ .it_value = .{ .tv_sec = 10, .tv_nsec = 0 },
+ };
+ _ = os.linux.timerfd_settime(@intCast(i32, tfd), 0, &interval, null);
+
+ const context = try udev.Udev.new();
+
+ var devices = DeviceList.init(state.gpa);
+ try updateDevices(state.gpa, context, &devices);
+ if (devices.items.len == 0) return error.NoDevicesFound;
+
+ return Battery{
+ .state = state,
+ .context = context,
+ .timerFd = @intCast(os.fd_t, tfd),
+ .devices = devices,
+ };
+}
+
+pub fn deinit(self: *Battery) void {
+ _ = self.context.unref();
+ for (self.devices.items) |*device| {
+ device.deinit(self.state.gpa);
+ }
+ self.devices.deinit();
+}
+
+pub fn module(self: *Battery) Module {
+ return .{
+ .impl = @ptrCast(*anyopaque, self),
+ .eventFn = getEvent,
+ .printFn = print,
+ };
+}
+
+pub fn getEvent(self_opaque: *anyopaque) !Event {
+ const self = utils.cast(Battery)(self_opaque);
+
+ return Event{
+ .fd = .{
+ .fd = self.timerFd,
+ .events = os.POLL.IN,
+ .revents = undefined,
+ },
+ .data = self_opaque,
+ .callbackIn = callbackIn,
+ .callbackOut = Event.noop,
+ };
+}
+
+fn callbackIn(self_opaque: *anyopaque) error{Terminate}!void {
+ const self = utils.cast(Battery)(self_opaque);
+
+ var expirations = mem.zeroes([8]u8);
+ _ = os.read(self.timerFd, &expirations) catch return;
+
+ for (self.state.wayland.monitors.items) |monitor| {
+ if (monitor.surface) |surface| {
+ if (surface.configured) {
+ render.renderClock(surface) catch continue;
+ render.renderModules(surface) catch continue;
+ surface.clockSurface.commit();
+ surface.modulesSurface.commit();
+ surface.backgroundSurface.commit();
+ }
+ }
+ }
+}
+
+pub fn print(self_opaque: *anyopaque, writer: Module.StringWriter) !void {
+ const self = utils.cast(Battery)(self_opaque);
+
+ try updateDevices(self.state.gpa, self.context, &self.devices);
+ const device = self.devices.items[0];
+
+ var icon: []const u8 = "❓";
+ if (mem.eql(u8, device.status, "Discharging")) {
+ icon = "🔋";
+ } else if (mem.eql(u8, device.status, "Charging")) {
+ icon = "🔌";
+ } else if (mem.eql(u8, device.status, "Full")) {
+ icon = "⚡";
+ }
+
+ try fmt.format(writer, "{s} {d}%", .{ icon, device.capacity });
+}
+
+fn updateDevices(
+ gpa: mem.Allocator,
+ context: *udev.Udev,
+ devices: *DeviceList,
+) !void {
+ const enumerate = try udev.Enumerate.new(context);
+ defer _ = enumerate.unref();
+
+ try enumerate.addMatchSubsystem("power_supply");
+ try enumerate.addMatchSysattr("type", "Battery");
+ try enumerate.scanDevices();
+
+ const entries = enumerate.getListEntry();
+
+ var maybe_entry = entries;
+ while (maybe_entry) |entry| : (maybe_entry = entry.getNext()) {
+ const path = entry.getName();
+ const device = try udev.Device.newFromSyspath(context, path);
+ try updateOrAppend(gpa, devices, device);
+ }
+}
+
+fn updateOrAppend(
+ gpa: mem.Allocator,
+ devices: *DeviceList,
+ dev: *udev.Device,
+) !void {
+ const name = dev.getSysname() catch return;
+ const status = dev.getSysattrValue("status") catch return;
+ const capacity = getCapacity(dev) catch return;
+
+ const device = blk: {
+ for (devices.items) |*device| {
+ if (mem.eql(u8, device.name, name)) {
+ gpa.free(device.status);
+ break :blk device;
+ }
+ } else {
+ const device = try devices.addOne();
+ device.name = try gpa.dupe(u8, name);
+ break :blk device;
+ }
+ };
+
+ device.status = try gpa.dupe(u8, status);
+ device.capacity = capacity;
+}
+
+fn getCapacity(dev: *udev.Device) !u8 {
+ const capacity_str = dev.getSysattrValue("capacity") catch {
+ return computeCapacityFromCharge(dev) catch {
+ return computeCapacityFromEnergy(dev);
+ };
+ };
+
+ const capacity = try fmt.parseInt(u8, capacity_str, 10);
+ return capacity;
+}
+
+fn computeCapacityFromEnergy(dev: *udev.Device) !u8 {
+ const energy_str = try dev.getSysattrValue("energy_now");
+ const energy_full_str = try dev.getSysattrValue("energy_full");
+
+ const energy = try fmt.parseFloat(f64, energy_str);
+ const energy_full = try fmt.parseFloat(f64, energy_full_str);
+
+ const capacity = energy * 100.0 / energy_full;
+ return @floatToInt(u8, @round(capacity));
+}
+
+fn computeCapacityFromCharge(dev: *udev.Device) !u8 {
+ const charge_str = try dev.getSysattrValue("charge_now");
+ const charge_full_str = try dev.getSysattrValue("charge_full");
+
+ const charge = try fmt.parseFloat(f64, charge_str);
+ const charge_full = try fmt.parseFloat(f64, charge_full_str);
+
+ const capacity = charge * 100.0 / charge_full;
+ return @floatToInt(u8, @round(capacity));
+}
diff --git a/src/render.zig b/src/render.zig
@@ -3,23 +3,22 @@ const mem = std.mem;
const fcft = @import("fcft");
const pixman = @import("pixman");
+const time = @cImport(@cInclude("time.h"));
-const Buffer = @import("shm.zig").Buffer;
-const c = @import("c.zig");
+const Buffer = @import("Buffer.zig");
const State = @import("main.zig").State;
-const Surface = @import("wayland.zig").Surface;
-const Tag = @import("tags.zig").Tag;
-const Tags = @import("tags.zig").Tags;
+const Surface = @import("Surface.zig");
+const Tag = @import("Tags.zig").Tag;
pub const RenderFn = fn (*Surface) anyerror!void;
pub fn renderBackground(surface: *Surface) !void {
- const state = surface.output.state;
+ const state = surface.monitor.state;
const wlSurface = surface.backgroundSurface;
const buffer = try Buffer.nextBuffer(
&surface.backgroundBuffers,
- state.wayland.shm,
+ state.wayland.globals.shm,
surface.width,
surface.height,
);
@@ -31,19 +30,19 @@ pub fn renderBackground(surface: *Surface) !void {
const color = &state.config.backgroundColor;
_ = pixman.Image.fillRectangles(.src, buffer.pix.?, color, 1, &area);
- wlSurface.setBufferScale(surface.output.scale);
+ wlSurface.setBufferScale(surface.monitor.scale);
wlSurface.damageBuffer(0, 0, surface.width, surface.height);
wlSurface.attach(buffer.buffer, 0, 0);
}
pub fn renderTags(surface: *Surface) !void {
- const state = surface.output.state;
+ const state = surface.monitor.state;
const wlSurface = surface.tagsSurface;
- const tags = surface.output.tags.tags;
+ const tags = surface.monitor.tags.tags;
const buffer = try Buffer.nextBuffer(
&surface.tagsBuffers,
- surface.output.state.wayland.shm,
+ surface.monitor.state.wayland.globals.shm,
surface.width,
surface.height,
);
@@ -54,18 +53,18 @@ pub fn renderTags(surface: *Surface) !void {
try renderTag(buffer.pix.?, tag, surface.height, offset, state);
}
- wlSurface.setBufferScale(surface.output.scale);
+ wlSurface.setBufferScale(surface.monitor.scale);
wlSurface.damageBuffer(0, 0, surface.width, surface.height);
wlSurface.attach(buffer.buffer, 0, 0);
}
pub fn renderClock(surface: *Surface) !void {
- const state = surface.output.state;
+ const state = surface.monitor.state;
const wlSurface = surface.clockSurface;
const buffer = try Buffer.nextBuffer(
&surface.clockBuffers,
- surface.output.state.wayland.shm,
+ surface.monitor.state.wayland.globals.shm,
surface.width,
surface.height,
);
@@ -80,20 +79,20 @@ pub fn renderClock(surface: *Surface) !void {
// get formatted datetime
const str = try formatDatetime(state);
- defer state.allocator.free(str);
+ defer state.gpa.free(str);
// ut8 encoding
const utf8 = try std.unicode.Utf8View.init(str);
var utf8_iter = utf8.iterator();
- var runes = try state.allocator.alloc(u32, str.len);
- defer state.allocator.free(runes);
+ var runes = try state.gpa.alloc(u32, str.len);
+ defer state.gpa.free(runes);
var i: usize = 0;
while (utf8_iter.nextCodepoint()) |rune| : (i += 1) {
runes[i] = rune;
}
- runes = state.allocator.resize(runes, i).?;
+ runes = state.gpa.resize(runes, i).?;
const run = try fcft.TextRun.rasterizeUtf32(
state.config.font,
@@ -122,25 +121,27 @@ pub fn renderClock(surface: *Surface) !void {
x_offset += glyph.advance.x;
}
- wlSurface.setBufferScale(surface.output.scale);
+ wlSurface.setBufferScale(surface.monitor.scale);
wlSurface.damageBuffer(0, 0, surface.width, surface.height);
wlSurface.attach(buffer.buffer, 0, 0);
}
pub fn renderModules(surface: *Surface) !void {
- const state = surface.output.state;
+ const state = surface.monitor.state;
const wlSurface = surface.modulesSurface;
const buffer = try Buffer.nextBuffer(
&surface.modulesBuffers,
- surface.output.state.wayland.shm,
+ surface.monitor.state.wayland.globals.shm,
surface.width,
surface.height,
);
buffer.busy = true;
// compose string
- var string = std.ArrayList(u8).init(state.allocator);
+ var string = std.ArrayList(u8).init(state.gpa);
+ defer string.deinit();
+
const writer = string.writer();
for (state.modules.items) |*module| {
try std.fmt.format(writer, " | ", .{});
@@ -151,14 +152,14 @@ pub fn renderModules(surface: *Surface) !void {
const utf8 = try std.unicode.Utf8View.init(string.items);
var utf8_iter = utf8.iterator();
- var runes = try state.allocator.alloc(u32, string.items.len);
- defer state.allocator.free(runes);
+ var runes = try state.gpa.alloc(u32, string.items.len);
+ defer state.gpa.free(runes);
var i: usize = 0;
while (utf8_iter.nextCodepoint()) |rune| : (i += 1) {
runes[i] = rune;
}
- runes = state.allocator.resize(runes, i).?;
+ runes = state.gpa.resize(runes, i).?;
// clear the buffer
const bg_area = [_]pixman.Rectangle16{
@@ -196,7 +197,7 @@ pub fn renderModules(surface: *Surface) !void {
x_offset += glyph.advance.x;
}
- wlSurface.setBufferScale(surface.output.scale);
+ wlSurface.setBufferScale(surface.monitor.scale);
wlSurface.damageBuffer(0, 0, surface.width, surface.height);
wlSurface.attach(buffer.buffer, 0, 0);
}
@@ -248,14 +249,14 @@ fn renderTag(
}
fn formatDatetime(state: *State) ![]const u8 {
- var buf = try state.allocator.alloc(u8, 256);
- const now = c.time.time(null);
- const local = c.time.localtime(&now);
- const len = c.time.strftime(
+ var buf = try state.gpa.alloc(u8, 256);
+ const now = time.time(null);
+ const local = time.localtime(&now);
+ const len = time.strftime(
buf.ptr,
buf.len,
state.config.clockFormat,
local,
);
- return state.allocator.resize(buf, len).?;
+ return state.gpa.resize(buf, len).?;
}
diff --git a/src/shm.zig b/src/shm.zig
@@ -1,69 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const os = std.os;
-
-const wayland = @import("wayland").client;
-const wl = wayland.wl;
-
-const pixman = @import("pixman");
-
-pub const Buffer = struct {
- data: ?[]align(4096) u8,
- buffer: ?*wl.Buffer,
- pix: ?*pixman.Image,
-
- busy: bool,
- width: u31,
- height: u31,
- size: u31,
-
- pub fn init(self: *Buffer, shm: *wl.Shm, width: u31, height: u31) !void {
- self.busy = true;
- self.width = width;
- self.height = height;
-
- const fd = try os.memfd_create("levee-wayland-shm-buffer-pool", 0);
- defer os.close(fd);
-
- const stride = width * 4;
- self.size = stride * height;
- try os.ftruncate(fd, self.size);
-
- self.data = try os.mmap(null, self.size, os.PROT.READ | os.PROT.WRITE, os.MAP.SHARED, fd, 0);
- errdefer os.munmap(self.data.?);
-
- const pool = try shm.createPool(fd, self.size);
- defer pool.destroy();
-
- self.buffer = try pool.createBuffer(0, width, height, stride, .argb8888);
- errdefer self.buffer.?.destroy();
- self.buffer.?.setListener(*Buffer, listener, self);
-
- self.pix = pixman.Image.createBitsNoClear(.a8r8g8b8, width, height, @ptrCast([*c]u32, self.data.?), stride);
- }
-
- pub fn deinit(self: *Buffer) void {
- if (self.pix) |pix| _ = pix.unref();
- if (self.buffer) |buf| buf.destroy();
- if (self.data) |data| os.munmap(data);
- }
-
- fn listener(_: *wl.Buffer, event: wl.Buffer.Event, buffer: *Buffer) void {
- switch (event) {
- .release => buffer.busy = false,
- }
- }
-
- pub fn nextBuffer(pool: *[2]Buffer, shm: *wl.Shm, width: u16, height: u16) !*Buffer {
- if (pool[0].busy and pool[1].busy) {
- return error.NoAvailableBuffers;
- }
- const buffer = if (!pool[0].busy) &pool[0] else &pool[1];
-
- if (buffer.width != width or buffer.height != height) {
- buffer.deinit();
- try buffer.init(shm, width, height);
- }
- return buffer;
- }
-};
diff --git a/src/tags.zig b/src/tags.zig
@@ -1,93 +0,0 @@
-const std = @import("std");
-
-const zriver = @import("wayland").client.zriver;
-
-const Output = @import("wayland.zig").Output;
-const render = @import("render.zig");
-const Seat = @import("wayland.zig").Seat;
-const State = @import("main.zig").State;
-
-pub const Tag = struct {
- label: u8,
- focused: bool = false,
- occupied: bool = false,
-};
-
-pub const Tags = struct {
- output: *Output,
- outputStatus: *zriver.OutputStatusV1,
- tags: [9]Tag,
-
- pub fn create(state: *State, output: *Output) !*Tags {
- const self = try state.allocator.create(Tags);
- const wayland = state.wayland;
-
- self.output = output;
- self.outputStatus = try wayland.statusManager.getRiverOutputStatus(
- output.wlOutput,
- );
- for (self.tags) |*tag, i| {
- tag.label = '1' + @intCast(u8, i);
- }
-
- self.outputStatus.setListener(*Tags, outputStatusListener, self);
- return self;
- }
-
- pub fn destroy(self: *Tags) void {
- self.outputStatus.destroy();
- }
-
- fn outputStatusListener(
- _: *zriver.OutputStatusV1,
- event: zriver.OutputStatusV1.Event,
- tags: *Tags,
- ) void {
- switch (event) {
- .focused_tags => |data| {
- for (tags.tags) |*tag, i| {
- const mask = @as(u32, 1) << @intCast(u5, i);
- tag.focused = data.tags & mask != 0;
- }
- },
- .view_tags => |data| {
- 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);
- if (view & mask != 0) tag.occupied = true;
- }
- }
- },
- }
- if (tags.output.surface) |surface| {
- if (surface.configured) {
- render.renderTags(surface) catch return;
- surface.tagsSurface.commit();
- surface.backgroundSurface.commit();
- }
- }
- }
-
- pub fn handleClick(self: *Tags, x: u32, seat: *Seat) !void {
- const state = self.output.state;
- const control = state.wayland.control;
-
- if (self.output.surface) |surface| {
- const index = x / surface.height;
- const payload = try std.fmt.allocPrintZ(
- state.allocator,
- "{d}",
- .{@as(u32, 1) << @intCast(u5, index)},
- );
- defer state.allocator.free(payload);
-
- control.addArgument("set-focused-tags");
- control.addArgument(payload);
- const callback = try control.runCommand(seat.wlSeat);
- _ = callback;
- }
- }
-};
diff --git a/src/utils.zig b/src/utils.zig
@@ -0,0 +1,15 @@
+const std = @import("std");
+const meta = std.meta;
+
+pub fn cast(comptime to: type) fn (*anyopaque) *to {
+ return (struct {
+ pub fn cast(module: *anyopaque) *to {
+ return @ptrCast(*to, @alignCast(@alignOf(to), module));
+ }
+ }).cast;
+}
+
+pub fn Mask(comptime container: type) type {
+ const len = meta.fields(container).len;
+ return [len]bool;
+}
diff --git a/src/wayland.zig b/src/wayland.zig
@@ -1,5 +1,7 @@
const std = @import("std");
const mem = std.mem;
+const meta = std.meta;
+const os = std.os;
const strcmp = std.cstr.cmp;
const ArrayList = std.ArrayList;
@@ -7,27 +9,33 @@ const wl = @import("wayland").client.wl;
const zwlr = @import("wayland").client.zwlr;
const zriver = @import("wayland").client.zriver;
-const Buffer = @import("shm.zig").Buffer;
+const Buffer = @import("Buffer.zig");
+const Event = @import("Loop.zig").Event;
const render = @import("render.zig");
const State = @import("main.zig").State;
-const Tags = @import("tags.zig").Tags;
+const Surface = @import("Surface.zig");
+const Tags = @import("Tags.zig");
+const utils = @import("utils.zig");
pub const Wayland = struct {
state: *State,
display: *wl.Display,
registry: *wl.Registry,
- outputs: ArrayList(*Output),
- seats: ArrayList(*Seat),
-
- compositor: *wl.Compositor,
- subcompositor: *wl.Subcompositor,
- shm: *wl.Shm,
- layerShell: *zwlr.LayerShellV1,
- statusManager: *zriver.StatusManagerV1,
- control: *zriver.ControlV1,
-
- globalsRegistered: [6]bool,
+ monitors: ArrayList(*Monitor),
+ inputs: ArrayList(*Input),
+ globals: Globals,
+ globalsMask: GlobalsMask,
+
+ const Globals = struct {
+ compositor: *wl.Compositor,
+ subcompositor: *wl.Subcompositor,
+ shm: *wl.Shm,
+ layerShell: *zwlr.LayerShellV1,
+ statusManager: *zriver.StatusManagerV1,
+ control: *zriver.ControlV1,
+ };
+ const GlobalsMask = utils.Mask(Globals);
pub fn init(state: *State) !Wayland {
const display = try wl.Display.connect(null);
@@ -36,73 +44,78 @@ pub const Wayland = struct {
.state = state,
.display = display,
.registry = try display.getRegistry(),
- .outputs = ArrayList(*Output).init(state.allocator),
- .seats = ArrayList(*Seat).init(state.allocator),
- .compositor = undefined,
- .subcompositor = undefined,
- .shm = undefined,
- .layerShell = undefined,
- .statusManager = undefined,
- .control = undefined,
- .globalsRegistered = mem.zeroes([6]bool),
+ .monitors = ArrayList(*Monitor).init(state.gpa),
+ .inputs = ArrayList(*Input).init(state.gpa),
+ .globals = undefined,
+ .globalsMask = mem.zeroes(GlobalsMask),
};
}
+ pub fn deinit(self: *Wayland) void {
+ for (self.monitors.items) |monitor| monitor.destroy();
+ for (self.inputs.items) |input| input.destroy();
+
+ self.monitors.deinit();
+ self.inputs.deinit();
+ }
+
pub fn registerGlobals(self: *Wayland) !void {
self.registry.setListener(*State, registryListener, self.state);
_ = try self.display.roundtrip();
- for (self.globalsRegistered) |globalRegistered| {
- if (!globalRegistered) return error.UnsupportedGlobal;
+ for (self.globalsMask) |is_registered| {
+ if (!is_registered) return error.UnsupportedGlobal;
}
}
- fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, state: *State) void {
- const wayland = &state.wayland;
+ pub fn getEvent(self: *Wayland) !Event {
+ const fd = self.display.getFd();
+
+ return Event{
+ .fd = .{
+ .fd = @intCast(os.fd_t, fd),
+ .events = os.POLL.IN,
+ .revents = undefined,
+ },
+ .data = @ptrCast(*anyopaque, self),
+ .callbackIn = dispatch,
+ .callbackOut = flush,
+ };
+ }
+
+ fn dispatch(self_opaque: *anyopaque) error{Terminate}!void {
+ const self = utils.cast(Wayland)(self_opaque);
+ _ = self.display.dispatch() catch return;
+ }
+
+ fn flush(self_opaque: *anyopaque) error{Terminate}!void {
+ const self = utils.cast(Wayland)(self_opaque);
+ _ = self.display.flush() catch return;
+ }
+
+ fn registryListener(
+ registry: *wl.Registry,
+ event: wl.Registry.Event,
+ state: *State,
+ ) void {
+ const self = &state.wayland;
switch (event) {
- .global => |global| {
- const interface = global.interface;
- const name = global.name;
-
- if (strcmp(interface, wl.Compositor.getInterface().name) == 0) {
- wayland.compositor = registry.bind(name, wl.Compositor, 4) catch return;
- wayland.globalsRegistered[0] = true;
- } else if (strcmp(interface, wl.Subcompositor.getInterface().name) == 0) {
- wayland.subcompositor = registry.bind(name, wl.Subcompositor, 1) catch return;
- wayland.globalsRegistered[1] = true;
- } else if (strcmp(interface, wl.Shm.getInterface().name) == 0) {
- wayland.shm = registry.bind(name, wl.Shm, 1) catch return;
- wayland.globalsRegistered[2] = true;
- } else if (strcmp(interface, zwlr.LayerShellV1.getInterface().name) == 0) {
- wayland.layerShell = registry.bind(name, zwlr.LayerShellV1, 1) catch return;
- wayland.globalsRegistered[3] = true;
- } else if (strcmp(interface, zriver.StatusManagerV1.getInterface().name) == 0) {
- wayland.statusManager = registry.bind(name, zriver.StatusManagerV1, 1) catch return;
- wayland.globalsRegistered[4] = true;
- } else if (strcmp(interface, zriver.ControlV1.getInterface().name) == 0) {
- wayland.control = registry.bind(name, zriver.ControlV1, 1) catch return;
- wayland.globalsRegistered[5] = true;
- } else if (strcmp(interface, wl.Output.getInterface().name) == 0) {
- const output = Output.create(state, registry, name) catch return;
- wayland.outputs.append(output) catch return;
- } else if (strcmp(interface, wl.Seat.getInterface().name) == 0) {
- const seat = Seat.create(state, registry, name) catch return;
- wayland.seats.append(seat) catch return;
- }
+ .global => |g| {
+ self.bindGlobal(registry, g.interface, g.name) catch return;
},
.global_remove => |data| {
- for (wayland.outputs.items) |output, i| {
- if (output.globalName == data.name) {
- output.destroy();
- _ = wayland.outputs.swapRemove(i);
+ for (self.monitors.items) |monitor, i| {
+ if (monitor.globalName == data.name) {
+ monitor.destroy();
+ _ = self.monitors.swapRemove(i);
break;
}
}
- for (wayland.seats.items) |seat, i| {
- if (seat.globalName == data.name) {
- seat.destroy();
- _ = wayland.seats.swapRemove(i);
+ for (self.inputs.items) |input, i| {
+ if (input.globalName == data.name) {
+ input.destroy();
+ _ = self.inputs.swapRemove(i);
break;
}
}
@@ -110,12 +123,55 @@ pub const Wayland = struct {
}
}
+ fn bindGlobal(
+ self: *Wayland,
+ registry: *wl.Registry,
+ iface: [*:0]const u8,
+ name: u32,
+ ) !void {
+ if (strcmp(iface, wl.Compositor.getInterface().name) == 0) {
+ const global = try registry.bind(name, wl.Compositor, 4);
+ self.setGlobal(global);
+ } else if (strcmp(iface, wl.Subcompositor.getInterface().name) == 0) {
+ const global = try registry.bind(name, wl.Subcompositor, 1);
+ self.setGlobal(global);
+ } else if (strcmp(iface, wl.Shm.getInterface().name) == 0) {
+ const global = try registry.bind(name, wl.Shm, 1);
+ self.setGlobal(global);
+ } else if (strcmp(iface, zwlr.LayerShellV1.getInterface().name) == 0) {
+ const global = try registry.bind(name, zwlr.LayerShellV1, 1);
+ self.setGlobal(global);
+ } else if (strcmp(iface, zriver.StatusManagerV1.getInterface().name) == 0) {
+ const global = try registry.bind(name, zriver.StatusManagerV1, 1);
+ self.setGlobal(global);
+ } else if (strcmp(iface, zriver.ControlV1.getInterface().name) == 0) {
+ const global = try registry.bind(name, zriver.ControlV1, 1);
+ self.setGlobal(global);
+ } else if (strcmp(iface, wl.Output.getInterface().name) == 0) {
+ const monitor = try Monitor.create(self.state, registry, name);
+ try self.monitors.append(monitor);
+ } else if (strcmp(iface, wl.Seat.getInterface().name) == 0) {
+ const input = try Input.create(self.state, registry, name);
+ try self.inputs.append(input);
+ }
+ }
+
+ pub fn setGlobal(self: *Wayland, global: anytype) void {
+ inline for (meta.fields(Globals)) |field, i| {
+ if (field.field_type == @TypeOf(global)) {
+ @field(self.globals, field.name) = global;
+ self.globalsMask[i] = true;
+ break;
+ }
+ }
+ }
+
pub fn findSurface(self: *Wayland, wlSurface: ?*wl.Surface) ?*Surface {
if (wlSurface == null) {
return null;
}
- for (self.outputs.items) |output| {
- if (output.surface) |surface| {
+ for (self.monitors.items) |monitor| {
+ if (monitor.surface) |surface| {
if (surface.backgroundSurface == wlSurface or
surface.tagsSurface == wlSurface or
surface.clockSurface == wlSurface or
@@ -129,58 +185,58 @@ pub const Wayland = struct {
}
};
-pub const Output = struct {
+pub const Monitor = struct {
state: *State,
- wlOutput: *wl.Output,
+ output: *wl.Output,
globalName: u32,
scale: i32,
surface: ?*Surface,
tags: *Tags,
- pub fn create(state: *State, registry: *wl.Registry, name: u32) !*Output {
- const self = try state.allocator.create(Output);
+ pub fn create(state: *State, registry: *wl.Registry, name: u32) !*Monitor {
+ const self = try state.gpa.create(Monitor);
self.state = state;
- self.wlOutput = try registry.bind(name, wl.Output, 3);
+ self.output = try registry.bind(name, wl.Output, 3);
self.globalName = name;
self.scale = 1;
self.surface = null;
self.tags = try Tags.create(state, self);
- self.wlOutput.setListener(*Output, listener, self);
+ self.output.setListener(*Monitor, listener, self);
return self;
}
- pub fn destroy(self: *Output) void {
+ pub fn destroy(self: *Monitor) void {
if (self.surface) |surface| {
surface.destroy();
}
self.tags.destroy();
- self.state.allocator.destroy(self);
+ self.state.gpa.destroy(self);
}
- fn listener(_: *wl.Output, event: wl.Output.Event, output: *Output) void {
+ fn listener(_: *wl.Output, event: wl.Output.Event, monitor: *Monitor) void {
switch (event) {
.scale => |scale| {
- output.scale = scale.factor;
+ monitor.scale = scale.factor;
},
.geometry => {},
.mode => {},
.name => {},
.description => {},
.done => {
- if (output.surface) |_| {} else {
- output.surface = Surface.create(output) catch return;
+ if (monitor.surface) |_| {} else {
+ monitor.surface = Surface.create(monitor) catch return;
}
},
}
}
};
-pub const Seat = struct {
+pub const Input = struct {
state: *State,
- wlSeat: *wl.Seat,
+ seat: *wl.Seat,
globalName: u32,
pointer: struct {
@@ -190,40 +246,40 @@ pub const Seat = struct {
surface: ?*Surface,
},
- pub fn create(state: *State, registry: *wl.Registry, name: u32) !*Seat {
- const self = try state.allocator.create(Seat);
+ pub fn create(state: *State, registry: *wl.Registry, name: u32) !*Input {
+ const self = try state.gpa.create(Input);
self.state = state;
- self.wlSeat = try registry.bind(name, wl.Seat, 3);
+ self.seat = try registry.bind(name, wl.Seat, 3);
self.globalName = name;
self.pointer.wlPointer = null;
self.pointer.surface = null;
- self.wlSeat.setListener(*Seat, listener, self);
+ self.seat.setListener(*Input, listener, self);
return self;
}
- pub fn destroy(self: *Seat) void {
+ pub fn destroy(self: *Input) void {
if (self.pointer.wlPointer) |wlPointer| {
wlPointer.release();
}
- self.wlSeat.release();
- self.state.allocator.destroy(self);
+ self.seat.release();
+ self.state.gpa.destroy(self);
}
- fn listener(wlSeat: *wl.Seat, event: wl.Seat.Event, seat: *Seat) void {
+ fn listener(seat: *wl.Seat, event: wl.Seat.Event, input: *Input) void {
switch (event) {
.capabilities => |data| {
- if (seat.pointer.wlPointer) |wlPointer| {
+ if (input.pointer.wlPointer) |wlPointer| {
wlPointer.release();
- seat.pointer.wlPointer = null;
+ input.pointer.wlPointer = null;
}
if (data.capabilities.pointer) {
- seat.pointer.wlPointer = wlSeat.getPointer() catch return;
- seat.pointer.wlPointer.?.setListener(
- *Seat,
+ input.pointer.wlPointer = seat.getPointer() catch return;
+ input.pointer.wlPointer.?.setListener(
+ *Input,
pointerListener,
- seat,
+ input,
);
}
},
@@ -234,30 +290,30 @@ pub const Seat = struct {
fn pointerListener(
_: *wl.Pointer,
event: wl.Pointer.Event,
- seat: *Seat,
+ input: *Input,
) void {
switch (event) {
.enter => |data| {
- seat.pointer.x = data.surface_x.toInt();
- seat.pointer.y = data.surface_y.toInt();
- const surface = seat.state.wayland.findSurface(data.surface);
- seat.pointer.surface = surface;
+ input.pointer.x = data.surface_x.toInt();
+ input.pointer.y = data.surface_y.toInt();
+ const surface = input.state.wayland.findSurface(data.surface);
+ input.pointer.surface = surface;
},
.leave => |_| {
- seat.pointer.surface = null;
+ input.pointer.surface = null;
},
.motion => |data| {
- seat.pointer.x = data.surface_x.toInt();
- seat.pointer.y = data.surface_y.toInt();
+ input.pointer.x = data.surface_x.toInt();
+ input.pointer.y = data.surface_y.toInt();
},
.button => |data| {
if (data.state != .pressed) return;
- if (seat.pointer.surface) |surface| {
+ if (input.pointer.surface) |surface| {
if (!surface.configured) return;
- const x = @intCast(u32, seat.pointer.x);
+ const x = @intCast(u32, input.pointer.x);
if (x < surface.height * 9) {
- surface.output.tags.handleClick(x, seat) catch return;
+ surface.monitor.tags.handleClick(x, input) catch return;
}
}
},
@@ -265,141 +321,3 @@ pub const Seat = struct {
}
}
};
-
-pub const Surface = struct {
- output: *Output,
-
- backgroundSurface: *wl.Surface,
- layerSurface: *zwlr.LayerSurfaceV1,
- backgroundBuffers: [2]Buffer,
-
- tagsSurface: *wl.Surface,
- tagsSubsurface: *wl.Subsurface,
- tagsBuffers: [2]Buffer,
-
- clockSurface: *wl.Surface,
- clockSubsurface: *wl.Subsurface,
- clockBuffers: [2]Buffer,
-
- modulesSurface: *wl.Surface,
- modulesSubsurface: *wl.Subsurface,
- modulesBuffers: [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 wayland.compositor.createSurface();
- self.layerSurface = try wayland.layerShell.getLayerSurface(
- self.backgroundSurface,
- output.wlOutput,
- .top,
- "levee",
- );
- self.backgroundBuffers = mem.zeroes([2]Buffer);
-
- 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 wayland.compositor.createSurface();
- self.clockSubsurface = try wayland.subcompositor.getSubsurface(
- self.clockSurface,
- self.backgroundSurface,
- );
- self.clockBuffers = mem.zeroes([2]Buffer);
-
- self.modulesSurface = try wayland.compositor.createSurface();
- self.modulesSubsurface = try wayland.subcompositor.getSubsurface(
- self.modulesSurface,
- self.backgroundSurface,
- );
- self.modulesBuffers = mem.zeroes([2]Buffer);
-
- // setup layer surface
- self.layerSurface.setSize(0, state.config.height);
- self.layerSurface.setAnchor(
- .{ .top = true, .left = true, .right = true, .bottom = false },
- );
- self.layerSurface.setExclusiveZone(state.config.height);
- self.layerSurface.setMargin(0, 0, 0, 0);
- self.layerSurface.setListener(*Surface, layerSurfaceListener, self);
-
- // setup subsurfaces
- self.tagsSubsurface.setPosition(0, 0);
- self.clockSubsurface.setPosition(0, 0);
- self.modulesSubsurface.setPosition(0, 0);
-
- self.tagsSurface.commit();
- self.clockSurface.commit();
- self.backgroundSurface.commit();
-
- return self;
- }
-
- pub fn destroy(self: *Surface) void {
- self.output.surface = null;
-
- self.backgroundSurface.destroy();
- self.layerSurface.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.modulesSurface.destroy();
- self.modulesSubsurface.destroy();
- self.modulesBuffers[0].deinit();
- self.modulesBuffers[1].deinit();
-
- self.output.state.allocator.destroy(self);
- }
-
- fn layerSurfaceListener(
- layerSurface: *zwlr.LayerSurfaceV1,
- event: zwlr.LayerSurfaceV1.Event,
- surface: *Surface,
- ) void {
- switch (event) {
- .configure => |data| {
- surface.configured = true;
- surface.width = @intCast(u16, data.width);
- surface.height = @intCast(u16, data.height);
-
- layerSurface.ackConfigure(data.serial);
-
- render.renderBackground(surface) catch return;
- render.renderTags(surface) catch return;
- render.renderClock(surface) catch return;
- render.renderModules(surface) catch return;
-
- surface.tagsSurface.commit();
- surface.clockSurface.commit();
- surface.modulesSurface.commit();
- surface.backgroundSurface.commit();
- },
- .closed => {
- surface.destroy();
- },
- }
- }
-};