commit 808900456e850ace23a37b40ba00cbdd71254236
parent e2a1a91f361fcce9616d73210f877fa65845d821
Author: Andrea Feletto <andrea@andreafeletto.com>
Date: Wed, 19 Jan 2022 18:23:13 +0100
render tags without labels (yet)
Diffstat:
A | src/config.zig | | | 27 | +++++++++++++++++++++++++++ |
M | src/main.zig | | | 3 | +++ |
M | src/render.zig | | | 72 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
A | src/tags.zig | | | 69 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/wayland.zig | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++------------ |
5 files changed, 216 insertions(+), 20 deletions(-)
diff --git a/src/config.zig b/src/config.zig
@@ -0,0 +1,27 @@
+const pixman = @import("pixman");
+
+pub const Config = struct {
+ height: u16,
+ backgroundColor: pixman.Color,
+ foregroundColor: pixman.Color,
+ border: u15,
+
+ pub fn init() Config {
+ return Config{
+ .height = 32,
+ .backgroundColor = .{
+ .red = 0,
+ .green = 0,
+ .blue = 0,
+ .alpha = 0xffff,
+ },
+ .foregroundColor = .{
+ .red = 0xffff,
+ .green = 0xffff,
+ .blue = 0xffff,
+ .alpha = 0xffff,
+ },
+ .border = 2,
+ };
+ }
+};
diff --git a/src/main.zig b/src/main.zig
@@ -1,10 +1,12 @@
const std = @import("std");
+const Config = @import("config.zig").Config;
const Loop = @import("event.zig").Loop;
const Wayland = @import("wayland.zig").Wayland;
pub const State = struct {
allocator: std.mem.Allocator,
+ config: Config,
wayland: Wayland,
loop: Loop,
};
@@ -16,6 +18,7 @@ pub fn main() anyerror!void {
std.log.info("initialization", .{});
var state: State = undefined;
state.allocator = arena.allocator();
+ state.config = Config.init();
state.wayland = try Wayland.init(&state);
state.loop = try Loop.init(&state);
diff --git a/src/render.zig b/src/render.zig
@@ -1,14 +1,18 @@
const pixman = @import("pixman");
const Buffer = @import("shm.zig").Buffer;
+const State = @import("main.zig").State;
const Surface = @import("wayland.zig").Surface;
+const Tag = @import("tags.zig").Tag;
+const Tags = @import("tags.zig").Tags;
pub fn renderBackground(surface: *Surface) !void {
+ const state = surface.output.state;
const wlSurface = surface.backgroundSurface;
const buffer = try Buffer.nextBuffer(
&surface.backgroundBuffers,
- surface.output.state.wayland.shm,
+ state.wayland.shm,
surface.width,
surface.height,
);
@@ -17,15 +21,67 @@ pub fn renderBackground(surface: *Surface) !void {
const area = [_]pixman.Rectangle16{
.{ .x = 0, .y = 0, .width = surface.width, .height = surface.height },
};
- const color = pixman.Color{
- .red = 0,
- .green = 0,
- .blue = 0,
- .alpha = 0xffff,
- };
- _ = pixman.Image.fillRectangles(.src, buffer.pix.?, &color, 1, &area);
+ const color = &state.config.backgroundColor;
+ _ = pixman.Image.fillRectangles(.src, buffer.pix.?, color, 1, &area);
wlSurface.setBufferScale(surface.output.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 wlSurface = surface.tagsSurface;
+ const tags = surface.output.tags.tags;
+
+ const buffer = try Buffer.nextBuffer(
+ &surface.tagsBuffers,
+ surface.output.state.wayland.shm,
+ surface.width,
+ surface.height,
+ );
+ buffer.busy = true;
+
+ for (tags) |*tag, i| {
+ const offset = @intCast(i16, surface.height * i);
+ try renderTag(buffer.pix.?, tag, surface.height, offset, state);
+ }
+
+ wlSurface.setBufferScale(surface.output.scale);
+ wlSurface.damageBuffer(0, 0, surface.width, surface.height);
+ wlSurface.attach(buffer.buffer, 0, 0);
+}
+
+fn renderTag(
+ pix: *pixman.Image,
+ tag: *const Tag,
+ height: u16,
+ offset: i16,
+ state: *State,
+) !void {
+ const size = @intCast(u16, height);
+
+ const outer = [_]pixman.Rectangle16{
+ .{ .x = offset, .y = 0, .width = size, .height = size },
+ };
+ const outer_color = if (tag.focused or tag.occupied) blk: {
+ break :blk &state.config.foregroundColor;
+ } else blk: {
+ break :blk &state.config.backgroundColor;
+ };
+ _ = pixman.Image.fillRectangles(.over, pix, outer_color, 1, &outer);
+
+ const border = state.config.border;
+ const inner = [_]pixman.Rectangle16{
+ .{
+ .x = offset + border,
+ .y = border,
+ .width = size - 2 * border,
+ .height = size - 2 * border,
+ },
+ };
+ const inner_color = &state.config.backgroundColor;
+ if (!tag.focused and tag.occupied) {
+ _ = pixman.Image.fillRectangles(.over, pix, inner_color, 1, &inner);
+ }
+}
diff --git a/src/tags.zig b/src/tags.zig
@@ -0,0 +1,69 @@
+const zriver = @import("wayland").client.zriver;
+
+const Output = @import("wayland.zig").Output;
+const render = @import("render.zig");
+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);
+
+ self.output = output;
+ self.outputStatus = try state.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();
+ }
+ }
+ }
+};
diff --git a/src/wayland.zig b/src/wayland.zig
@@ -10,6 +10,7 @@ const zriver = @import("wayland").client.zriver;
const Buffer = @import("shm.zig").Buffer;
const render = @import("render.zig");
const State = @import("main.zig").State;
+const Tags = @import("tags.zig").Tags;
pub const Wayland = struct {
state: *State,
@@ -19,11 +20,12 @@ pub const Wayland = struct {
outputs: ArrayList(*Output),
compositor: *wl.Compositor,
+ subcompositor: *wl.Subcompositor,
shm: *wl.Shm,
layerShell: *zwlr.LayerShellV1,
statusManager: *zriver.StatusManagerV1,
- globalsRegistered: [4]bool,
+ globalsRegistered: [5]bool,
pub fn init(state: *State) !Wayland {
const display = try wl.Display.connect(null);
@@ -34,10 +36,11 @@ pub const Wayland = struct {
.registry = try display.getRegistry(),
.outputs = ArrayList(*Output).init(state.allocator),
.compositor = undefined,
+ .subcompositor = undefined,
.shm = undefined,
.layerShell = undefined,
.statusManager = undefined,
- .globalsRegistered = mem.zeroes([4]bool),
+ .globalsRegistered = mem.zeroes([5]bool),
};
}
@@ -61,15 +64,18 @@ pub const Wayland = struct {
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[1] = true;
+ 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[2] = true;
+ 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[3] = true;
+ wayland.globalsRegistered[4] = 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;
@@ -78,7 +84,7 @@ pub const Wayland = struct {
.global_remove => |data| {
for (wayland.outputs.items) |output, i| {
if (output.globalName == data.name) {
- state.allocator.destroy(output);
+ output.destroy();
_ = wayland.outputs.swapRemove(i);
break;
}
@@ -93,7 +99,9 @@ pub const Output = struct {
wlOutput: *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);
@@ -101,12 +109,22 @@ pub const Output = struct {
self.wlOutput = 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);
return self;
}
+ pub fn destroy(self: *Output) void {
+ if (self.surface) |surface| {
+ surface.destroy();
+ }
+ self.tags.destroy();
+ self.state.allocator.destroy(self);
+ }
+
fn listener(_: *wl.Output, event: wl.Output.Event, output: *Output) void {
switch (event) {
.scale => |scale| {
@@ -129,9 +147,12 @@ pub const Surface = struct {
output: *Output,
backgroundSurface: *wl.Surface,
+ layerSurface: *zwlr.LayerSurfaceV1,
backgroundBuffers: [2]Buffer,
- layerSurface: *zwlr.LayerSurfaceV1,
+ tagsSurface: *wl.Surface,
+ tagsSubsurface: *wl.Subsurface,
+ tagsBuffers: [2]Buffer,
configured: bool,
width: u16,
@@ -145,24 +166,37 @@ pub const Surface = struct {
self.configured = false;
self.backgroundSurface = try state.wayland.compositor.createSurface();
- self.backgroundBuffers = mem.zeroes([2]Buffer);
-
self.layerSurface = try state.wayland.layerShell.getLayerSurface(
self.backgroundSurface,
output.wlOutput,
.overlay,
"levee",
);
+ self.backgroundBuffers = mem.zeroes([2]Buffer);
+
+ self.tagsSurface = try state.wayland.compositor.createSurface();
+ self.tagsSubsurface = try state.wayland.subcompositor.getSubsurface(
+ self.tagsSurface,
+ self.backgroundSurface,
+ );
+ self.tagsBuffers = mem.zeroes([2]Buffer);
- const height = 32;
- self.layerSurface.setSize(0, height);
+ // setup layer surface
+ self.layerSurface.setSize(0, state.config.height);
self.layerSurface.setAnchor(
.{ .top = true, .left = true, .right = true, .bottom = false },
);
- self.layerSurface.setExclusiveZone(@intCast(i32, height));
+ 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);
+ const region = try state.wayland.compositor.createRegion();
+ self.tagsSurface.setInputRegion(region);
+ region.destroy();
+
+ self.tagsSurface.commit();
self.backgroundSurface.commit();
return self;
@@ -172,8 +206,12 @@ pub const Surface = struct {
self.output.surface = null;
self.backgroundSurface.destroy();
self.layerSurface.destroy();
+ self.tagsSurface.destroy();
+ self.tagsSubsurface.destroy();
self.backgroundBuffers[0].deinit();
self.backgroundBuffers[1].deinit();
+ self.tagsBuffers[0].deinit();
+ self.tagsBuffers[1].deinit();
self.output.state.allocator.destroy(self);
}
@@ -191,6 +229,9 @@ pub const Surface = struct {
layerSurface.ackConfigure(data.serial);
render.renderBackground(surface) catch return;
+ render.renderTags(surface) catch return;
+
+ surface.tagsSurface.commit();
surface.backgroundSurface.commit();
},
.closed => {