@@ 37,7 37,7 @@ const Output = struct {
wl_name: u32,
name: ?[]const u8 = null,
- scale: i32 = 1,
+ // TODO: scale
configured: bool = false,
@@ 50,9 50,13 @@ const Output = struct {
output.wl_output.setListener(*Output, output_listener, output);
}
- fn deinit(output: *Output) void {
+ fn destroy(output: *Output) void {
output.wl_output.release();
if (output.name) |name| ctx.gpa.free(name);
+
+ const node = @fieldParentPtr(std.SinglyLinkedList(Output).Node, "data", output);
+ ctx.backend.outputs.remove(node);
+ ctx.gpa.destroy(node);
}
fn output_listener(_: *wl.Output, event: wl.Output.Event, output: *Output) void {
@@ 61,7 65,6 @@ const Output = struct {
log.info("output {?s} configured", .{output.name});
output.configured = true;
},
- .scale => |ev| output.scale = ev.factor,
.name => |ev| {
output.name = ctx.gpa.dupe(u8, mem.span(ev.name)) catch |err| switch (err) {
error.OutOfMemory => {
@@ 125,9 128,13 @@ const Seat = struct {
seat.wl_seat.setListener(*Seat, seat_listener, seat);
}
- fn deinit(seat: *Seat) void {
+ fn destroy(seat: *Seat) void {
seat.release_pointer();
seat.wl_seat.release();
+
+ const node = @fieldParentPtr(std.SinglyLinkedList(Seat).Node, "data", seat);
+ ctx.backend.seats.remove(node);
+ ctx.gpa.destroy(node);
}
fn seat_listener(_: *wl.Seat, event: wl.Seat.Event, seat: *Seat) void {
@@ 222,7 229,7 @@ const Seat = struct {
.hand => "pointer",
};
- const cursor_size = 24 * ctx.backend.scale;
+ const cursor_size = 24;
if (seat.cursor_theme == null) {
seat.cursor_theme = try wl.CursorTheme.load(null, cursor_size, ctx.backend.shm.?);
@@ 245,7 252,6 @@ const Seat = struct {
seat.cursor_surface = null;
}
- seat.cursor_surface.?.setBufferScale(ctx.backend.scale);
seat.cursor_surface.?.attach(wl_buffer, 0, 0);
seat.cursor_surface.?.damageBuffer(0, 0, std.math.maxInt(i31), std.math.maxInt(u31));
seat.cursor_surface.?.commit();
@@ 253,8 259,8 @@ const Seat = struct {
seat.wl_pointer.?.setCursor(
seat.last_enter_serial,
seat.cursor_surface.?,
- @divFloor(@intCast(i32, cursor_image.hotspot_x), ctx.backend.scale),
- @divFloor(@intCast(i32, cursor_image.hotspot_y), ctx.backend.scale),
+ @intCast(i32, cursor_image.hotspot_x),
+ @intCast(i32, cursor_image.hotspot_y),
);
}
};
@@ 271,43 277,91 @@ const Surface = struct {
// True if we need to redraw.
dirty: bool = false,
+ width: u31 = 0,
+ height: u31 = 0,
+
configured: bool = false,
- fn init(surface: *Surface) !void {
- const wl_surface = try ctx.backend.compositor.?.createSurface();
- const layer_surface = try ctx.backend.layer_shell.?.getLayerSurface(
- wl_surface,
- ctx.backend.req_output.wl_output,
- switch (ctx.bar.config.layer) {
- .bottom => .bottom,
- .top => .top,
- .overlay => .overlay,
- },
- "panel",
- );
+ fn deinit(surface: *Surface) void {
+ surface.configured = false;
+ surface.hotspots.deinit(ctx.gpa);
+ if (surface.frame_callback) |cb| cb.destroy();
+ surface.layer_surface.destroy();
+ surface.wl_surface.destroy();
+ }
- surface.* = .{
- .wl_surface = wl_surface,
- .layer_surface = layer_surface,
- };
+ fn send_frame(surface: *Surface) !void {
+ // The surface is not initialized yet.
+ if (!surface.configured) {
+ const wl_surface = try ctx.backend.compositor.?.createSurface();
+ const layer_surface = try ctx.backend.layer_shell.?.getLayerSurface(
+ wl_surface,
+ ctx.backend.req_output.wl_output,
+ switch (ctx.bar.config.layer) {
+ .bottom => .bottom,
+ .top => .top,
+ .overlay => .overlay,
+ },
+ "panel",
+ );
+
+ surface.* = .{
+ .wl_surface = wl_surface,
+ .layer_surface = layer_surface,
+ };
+ surface.layer_surface.setListener(*Surface, layer_surface_listener, surface);
+ }
- // Configure the layer_surface.
- surface.layer_surface.setListener(*Surface, layer_surface_listener, surface);
- configure_layer(surface.layer_surface, 0, ctx.bar.config.height, ctx.backend.scale);
+ // Configure the layer_surface and resize it if needed.
+ if (surface.height != ctx.bar.config.height) {
+ const cfg = ctx.bar.config;
+ surface.layer_surface.setSize(0, cfg.height);
+ if (cfg.layer == .overlay) {
+ surface.layer_surface.setExclusiveZone(0);
+ } else {
+ surface.layer_surface.setExclusiveZone(cfg.height);
+ }
+ switch (cfg.location) {
+ .top => {
+ surface.layer_surface.setAnchor(.{ .top = true, .right = true, .bottom = false, .left = true });
+ },
+ .bottom => {
+ surface.layer_surface.setAnchor(.{ .top = false, .right = true, .bottom = true, .left = true });
+ },
+ }
+ surface.layer_surface.setMargin(cfg.margins.top, cfg.margins.right, cfg.margins.bottom, cfg.margins.left);
- // We need to commit the empty surface first so we can receive a configure
- // event with width and height requested for our surface.
- surface.wl_surface.commit();
+ // We need to commit the empty surface first so we can receive a configure
+ // event with width and height requested for our surface.
+ surface.wl_surface.commit();
+
+ return;
+ }
+
+ // If we made it till here we can finally draw on your surface.
+ // Get a new buffer not busy.
+ const buffer = try ctx.backend.pool.next_buffer(surface.width, surface.height);
+ assert(!buffer.busy);
+
+ try ctx.bar.render(buffer, @intCast(u16, surface.width), @intCast(u16, surface.height));
+
+ // Attach the buffer to the surface.
+ surface.wl_surface.attach(buffer.wl_buffer, 0, 0);
+ surface.wl_surface.damageBuffer(0, 0, buffer.width, buffer.height);
+ buffer.busy = true;
+
+ // Schedule a frame in case the surface become dirty again.
+ surface.schedule_frame_and_commit();
+ surface.dirty = false;
}
- fn deinit(surface: *Surface) void {
- if (surface.frame_callback) |cb| cb.destroy();
- surface.hotspots.deinit(ctx.gpa);
- surface.layer_surface.destroy();
- surface.wl_surface.destroy();
+ pub fn set_dirty(surface: *Surface) void {
+ if (surface.dirty) return;
+ surface.dirty = true;
+ surface.schedule_frame_and_commit();
}
- pub fn schedule_frame_and_commit(surface: *Surface) void {
+ fn schedule_frame_and_commit(surface: *Surface) void {
if (surface.frame_pending) return;
// Request a new callback for the next frame.
@@ 317,12 371,6 @@ const Surface = struct {
surface.frame_pending = true;
}
- pub fn set_dirty(surface: *Surface) void {
- if (surface.dirty) return;
- surface.dirty = true;
- surface.schedule_frame_and_commit();
- }
-
fn hotspot_from_point(surface: *Surface, x: u31, y: u31) ?*HotSpot {
for (surface.hotspots.items) |*hs| {
if (hs.contains_point(x, y)) return hs;
@@ 330,34 378,6 @@ const Surface = struct {
return null;
}
- fn configure_layer(
- layer_surface: *zwlr.LayerSurfaceV1,
- width: u31,
- height: u31,
- scale: i32,
- ) void {
- layer_surface.setSize(width, height);
- if (ctx.bar.config.layer == .overlay) {
- layer_surface.setExclusiveZone(0);
- } else {
- layer_surface.setExclusiveZone(height * scale);
- }
- switch (ctx.bar.config.location) {
- .top => {
- layer_surface.setAnchor(.{ .top = true, .right = true, .bottom = false, .left = true });
- },
- .bottom => {
- layer_surface.setAnchor(.{ .top = false, .right = true, .bottom = true, .left = true });
- },
- }
- layer_surface.setMargin(
- ctx.bar.config.margins.top * scale,
- ctx.bar.config.margins.right * scale,
- ctx.bar.config.margins.bottom * scale,
- ctx.bar.config.margins.left * scale,
- );
- }
-
fn layer_surface_listener(
_: *zwlr.LayerSurfaceV1,
event: zwlr.LayerSurfaceV1.Event,
@@ 365,19 385,21 @@ const Surface = struct {
) void {
switch (event) {
.configure => |ev| {
- surface.configured = true;
surface.layer_surface.ackConfigure(ev.serial);
const w = @truncate(u31, ev.width);
const h = @truncate(u31, ev.height);
- if (surface.configured and ctx.backend.width != w or ctx.backend.height != h) {
- configure_layer(surface.layer_surface, w, h, ctx.backend.scale);
-
- ctx.backend.width = w * @intCast(u31, ctx.backend.scale);
- ctx.backend.height = h * @intCast(u31, ctx.backend.scale);
- ctx.bar.render() catch return;
+ if (surface.configured and surface.width == w and surface.height == h) {
+ surface.wl_surface.commit();
+ return;
}
+
+ surface.configured = true;
+ surface.width = w;
+ surface.height = h;
+
+ surface.send_frame() catch return;
},
.closed => {
surface.deinit();
@@ 395,13 417,13 @@ const Surface = struct {
surface.frame_callback = null;
surface.frame_pending = false;
- if (surface.dirty) ctx.bar.render() catch return;
+ if (surface.dirty) surface.send_frame() catch return;
},
}
}
};
-// TODO: Credit source when published
+// Taken and adapted from https://git.sr.ht/~leon_plickat/wayprompt, same license.
const BufferPool = struct {
/// The amount of buffers per surface we consider the reasonable upper limit.
/// Some compositors sometimes tripple-buffer, so three seems to be ok.
@@ 493,7 515,7 @@ const BufferPool = struct {
}
};
-const Buffer = struct {
+pub const Buffer = struct {
wl_buffer: ?*wl.Buffer = null,
data: ?[]align(std.mem.page_size) u8 = null,
pixman_image: ?*pixman.Image = null,
@@ 575,24 597,21 @@ shm: ?*wl.Shm = null,
layer_shell: ?*zwlr.LayerShellV1 = null,
outputs: std.SinglyLinkedList(Output) = .{},
-req_output: *Output = undefined,
-scale: i32 = 1,
-
seats: std.SinglyLinkedList(Seat) = .{},
+/// Requested output by the user to display the bar on.
+// TODO: What if the req_output is destroyed
+req_output: *Output = undefined,
surface: ?Surface = null,
-
pool: BufferPool = .{},
-width: u31 = 0,
-height: u31 = 0,
-
-pub fn init(backend: *Backend, cfg: Bar.Config) !bool {
+pub fn init(backend: *Backend) !bool {
backend.* = .{
.display = wl.Display.connect(null) catch {
ctx.fatal(.backend, "failed to connect to Wayland compositor", .{});
},
.registry = try backend.display.getRegistry(),
+ .surface = Surface{},
};
backend.registry.setListener(*Backend, registry_listener, backend);
@@ 610,7 629,7 @@ pub fn init(backend: *Backend, cfg: Bar.Config) !bool {
_ = backend.display.roundtrip();
// Get the output requested by the user or use the first one in the outputs list.
- if (cfg.output_name) |name| {
+ if (ctx.bar.config.output_name) |name| {
var it = backend.outputs.first;
while (it) |node| : (it = node.next) {
const output_name = node.data.name orelse continue;
@@ 623,19 642,12 @@ pub fn init(backend: *Backend, cfg: Bar.Config) !bool {
}
} else {
const node = backend.outputs.first orelse return error.NoOutputDetected;
- log.info(
- "no output requested, using the first in the list({s})",
- .{node.data.name.?},
- );
+ log.info("no output requested, using the first in the list({?s})", .{node.data.name});
backend.req_output = &node.data;
}
- backend.scale = backend.req_output.scale;
-
- ctx.bar = try Bar.new(cfg);
- // Create the surface after the bar so we have all infos needed from the bar config.
- backend.surface = Surface{};
- try backend.surface.?.init();
+ // We finally have all the infos needed to create the surface and start rendering.
+ try backend.surface.?.send_frame();
return backend.display.roundtrip() == .SUCCESS;
}
@@ 652,14 664,8 @@ pub fn deinit(backend: *Backend) void {
if (backend.shm) |shm| shm.destroy();
if (backend.layer_shell) |ls| ls.destroy();
- while (backend.outputs.popFirst()) |node| {
- node.data.deinit();
- ctx.gpa.destroy(node);
- }
- while (backend.seats.popFirst()) |node| {
- node.data.deinit();
- ctx.gpa.destroy(node);
- }
+ while (backend.outputs.first) |node| node.data.destroy();
+ while (backend.seats.first) |node| node.data.destroy();
backend.registry.destroy();
backend.display.disconnect();
@@ 764,21 770,23 @@ fn registry_event(backend: *Backend, registry: *wl.Registry, event: wl.Registry.
}
},
.global_remove => |ev| {
- var output_it = backend.outputs.first;
- while (output_it) |node| : (output_it = node.next) {
- if (node.data.wl_name == ev.name) {
- node.data.deinit();
- backend.outputs.remove(node);
- break;
+ {
+ var it = backend.outputs.first;
+ while (it) |node| : (it = node.next) {
+ if (node.data.wl_name == ev.name) {
+ node.data.destroy();
+ break;
+ }
}
}
- var seat_it = backend.seats.first;
- while (seat_it) |node| : (seat_it = node.next) {
- if (node.data.id == ev.name) {
- node.data.deinit();
- backend.seats.remove(node);
- break;
+ {
+ var it = backend.seats.first;
+ while (it) |node| : (it = node.next) {
+ if (node.data.id == ev.name) {
+ node.data.destroy();
+ break;
+ }
}
}
},
@@ 20,6 20,7 @@ const assert = std.debug.assert;
const fcft = @import("fcft");
const ctx = &@import("main.zig").context;
+const Buffer = @import("Backend.zig").Buffer;
const data = @import("data.zig");
const decorations = @import("decorations.zig");
const Text = @import("Text.zig");
@@ 40,6 41,7 @@ pub const Config = struct {
width: u16 = 0,
height: u16 = 26,
+ /// Margins around the Surface.
margins: struct { top: u16, left: u16, bottom: u16, right: u16 } = .{
.top = 0,
.left = 0,
@@ 48,9 50,7 @@ pub const Config = struct {
},
colors: std.AutoHashMapUnmanaged(enum { bg, fg, line }, []const u8) = .{},
-
border: decorations.Border = .{},
-
line_height: u16 = 2,
fonts_name: []const u8 = "sans",
@@ 59,13 59,13 @@ pub const Config = struct {
config: Config = .{},
font: *fcft.Font = undefined,
-/// Raw input received from STDIN.
-input: std.ArrayListUnmanaged(u8) = .{},
-
/// True if user requested a width (i.e. config.width > 0). If false the width
/// received in the layer_surface configure event is used.
req_width: bool = false,
+/// Raw input received from STDIN.
+input: std.ArrayListUnmanaged(u8) = .{},
+
pub fn new(config: Config) !Bar {
return Bar{
.config = config,
@@ 80,52 80,31 @@ pub fn deinit(bar: *Bar) void {
bar.input.deinit(ctx.gpa);
}
-pub fn render(bar: *Bar) !void {
- const w = if (bar.req_width) bar.config.width else @intCast(u16, ctx.backend.width);
+/// Draw the bar on the Buffer.
+/// surface_w is always the width of the output.
+pub fn render(bar: *Bar, buffer: *Buffer, surface_w: u16, h: u16) !void {
+ // Width where we draw our bar.
+ const w = if (bar.req_width) bar.config.width else surface_w;
- bar.config.border.check_size(w, bar.config.height) catch |err| switch (err) {
- error.BordersGreaterHeight => {
- ctx.fatal(.bar, "borders greater than bar height({d})", .{bar.config.height});
- },
- error.BordersGreaterWidth => {
- ctx.fatal(.bar, "borders greater than bar width({d})", .{w});
- },
+ bar.config.border.check_size(w, h) catch |err| switch (err) {
+ error.BordersGreaterHeight => ctx.fatal(.bar, "borders greater than bar height({d})", .{h}),
+ error.BordersGreaterWidth => ctx.fatal(.bar, "borders greater than bar width({d})", .{w}),
else => return err,
};
// Starting position of the bar in the output.
const x: u16 = switch (bar.config.alignment) {
.left => 0,
- .center => @intCast(u16, ctx.backend.width >> 1) - (bar.config.width >> 1),
- .right => @intCast(u16, ctx.backend.width) - bar.config.width,
+ .center => surface_w / 2 - (w >> 1),
+ .right => surface_w - w,
};
- // Get a new buffer not busy.
- const buffer = try ctx.backend.pool.next_buffer(w, ctx.backend.height);
- assert(!buffer.busy);
-
// Render the background and the borders of the bar.
const pix = buffer.pixman_image orelse return error.PixmanImageEmpty;
const bg = bar.config.colors.get(.bg).?;
- try decorations.draw_rectangle(pix, @intCast(i16, x), 0, w, bar.config.height, bg);
- try bar.config.border.draw(pix, @intCast(i16, x), 0, w, bar.config.height);
+ try decorations.draw_rectangle(pix, @intCast(i16, x), 0, w, h, bg);
+ try bar.config.border.draw(pix, @intCast(i16, x), 0, w, h);
// Parse and render the raw input if any.
if (bar.input.items.len > 0) try data.expose(bar.input.items[0..], pix, x, w);
-
- // Attach the buffer to the surface.
- if (ctx.backend.surface) |*surface| {
- if (!surface.configured) {
- log.warn("surface is not configured", .{});
- return;
- }
- surface.wl_surface.setBufferScale(ctx.backend.scale);
- surface.wl_surface.attach(buffer.wl_buffer, 0, 0);
- surface.wl_surface.damageBuffer(0, 0, buffer.width, buffer.height);
- buffer.busy = true;
-
- // Schedule a frame in case the surface become dirty again.
- surface.schedule_frame_and_commit();
- surface.dirty = false;
- }
}