Add initial bar support
Right now it just renders a black bar at the top of the screen, nothing useful is in it and it has no configuration.
This commit is contained in:
parent
b48032bbba
commit
40088b4ab6
5 changed files with 345 additions and 26 deletions
13
build.zig
13
build.zig
|
|
@ -16,10 +16,12 @@ pub fn build(b: *std.Build) void {
|
||||||
const scanner = Scanner.create(b, .{});
|
const scanner = Scanner.create(b, .{});
|
||||||
const wayland = b.createModule(.{ .root_source_file = scanner.result });
|
const wayland = b.createModule(.{ .root_source_file = scanner.result });
|
||||||
// Rest of the deps
|
// Rest of the deps
|
||||||
|
const fcft = b.dependency("fcft", .{}).module("fcft");
|
||||||
const kdl = b.dependency("kdl", .{}).module("kdl");
|
const kdl = b.dependency("kdl", .{}).module("kdl");
|
||||||
const known_folders = b.dependency("known_folders", .{}).module("known-folders");
|
const known_folders = b.dependency("known_folders", .{}).module("known-folders");
|
||||||
const pixman = b.dependency("pixman", .{}).module("pixman");
|
const pixman = b.dependency("pixman", .{}).module("pixman");
|
||||||
const xkbcommon = b.dependency("xkbcommon", .{}).module("xkbcommon");
|
const xkbcommon = b.dependency("xkbcommon", .{}).module("xkbcommon");
|
||||||
|
const zeit = b.dependency("zeit", .{}).module("zeit");
|
||||||
const zigimg = b.dependency("zigimg", .{}).module("zigimg");
|
const zigimg = b.dependency("zigimg", .{}).module("zigimg");
|
||||||
|
|
||||||
scanner.addCustomProtocol(b.path("protocol/river-input-management-v1.xml"));
|
scanner.addCustomProtocol(b.path("protocol/river-input-management-v1.xml"));
|
||||||
|
|
@ -57,14 +59,17 @@ pub fn build(b: *std.Build) void {
|
||||||
beansprout.root_module.addOptions("build_options", options);
|
beansprout.root_module.addOptions("build_options", options);
|
||||||
|
|
||||||
beansprout.root_module.addImport("wayland", wayland);
|
beansprout.root_module.addImport("wayland", wayland);
|
||||||
|
beansprout.root_module.addImport("fcft", fcft);
|
||||||
beansprout.root_module.addImport("kdl", kdl);
|
beansprout.root_module.addImport("kdl", kdl);
|
||||||
beansprout.root_module.addImport("known_folders", known_folders);
|
beansprout.root_module.addImport("known_folders", known_folders);
|
||||||
beansprout.root_module.addImport("pixman", pixman);
|
beansprout.root_module.addImport("pixman", pixman);
|
||||||
beansprout.root_module.addImport("xkbcommon", xkbcommon);
|
beansprout.root_module.addImport("xkbcommon", xkbcommon);
|
||||||
beansprout.root_module.addImport("zigimg", zigimg);
|
beansprout.root_module.addImport("zigimg", zigimg);
|
||||||
|
beansprout.root_module.addImport("zeit", zeit);
|
||||||
|
|
||||||
beansprout.linkLibC();
|
beansprout.linkLibC();
|
||||||
beansprout.linkSystemLibrary("wayland-client");
|
beansprout.linkSystemLibrary("wayland-client");
|
||||||
|
beansprout.linkSystemLibrary("fcft");
|
||||||
beansprout.linkSystemLibrary("pixman-1");
|
beansprout.linkSystemLibrary("pixman-1");
|
||||||
beansprout.linkSystemLibrary("xkbcommon");
|
beansprout.linkSystemLibrary("xkbcommon");
|
||||||
|
|
||||||
|
|
@ -117,6 +122,14 @@ const manifest: struct {
|
||||||
url: []const u8,
|
url: []const u8,
|
||||||
hash: []const u8,
|
hash: []const u8,
|
||||||
},
|
},
|
||||||
|
fcft: struct {
|
||||||
|
url: []const u8,
|
||||||
|
hash: []const u8,
|
||||||
|
},
|
||||||
|
zeit: struct {
|
||||||
|
url: []const u8,
|
||||||
|
hash: []const u8,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
paths: []const []const u8,
|
paths: []const []const u8,
|
||||||
} = @import("build.zig.zon");
|
} = @import("build.zig.zon");
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,14 @@
|
||||||
.url = "https://github.com/zigimg/zigimg/archive/fb74dfb7c6d83f2bd01a229826669451525a4ba8.tar.gz",
|
.url = "https://github.com/zigimg/zigimg/archive/fb74dfb7c6d83f2bd01a229826669451525a4ba8.tar.gz",
|
||||||
.hash = "zigimg-0.1.0-8_eo2kSGFwADIkeZYTgfnLOV-khh6ZRoGmK6F2-s_QbY",
|
.hash = "zigimg-0.1.0-8_eo2kSGFwADIkeZYTgfnLOV-khh6ZRoGmK6F2-s_QbY",
|
||||||
},
|
},
|
||||||
|
.fcft = .{
|
||||||
|
.url = "https://git.sr.ht/~novakane/zig-fcft/archive/4bf5be61c869d08d5bcb0306049c63a9cb0795a7.tar.gz",
|
||||||
|
.hash = "fcft-3.0.0-zcx6CxQfAADhnwm8SjyCkQF-VFHGiVarigc2de3ciInC",
|
||||||
|
},
|
||||||
|
.zeit = .{
|
||||||
|
.url = "https://github.com/rockorager/zeit/archive/7ac64d72dbfb1a4ad549102e7d4e232a687d32d8.tar.gz",
|
||||||
|
.hash = "zeit-0.6.0-5I6bk36tAgATpSl9wjFmRPMqYN2Mn0JQHgIcRNcqDpJA",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
.paths = .{
|
.paths = .{
|
||||||
|
|
|
||||||
265
src/Bar.zig
Normal file
265
src/Bar.zig
Normal file
|
|
@ -0,0 +1,265 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Ben Buhse <me@benbuhse.email>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
const Bar = @This();
|
||||||
|
|
||||||
|
context: *Context,
|
||||||
|
|
||||||
|
// TODO: Get this in Config then save in Context
|
||||||
|
font: *fcft.Font,
|
||||||
|
|
||||||
|
/// The output that this Bar is on
|
||||||
|
output: *Output,
|
||||||
|
|
||||||
|
// Bar geometry
|
||||||
|
width: u31 = 0,
|
||||||
|
height: u31 = 0,
|
||||||
|
|
||||||
|
wl_surface: ?*wl.Surface = null,
|
||||||
|
layer_surface: ?*zwlr.LayerSurfaceV1 = null,
|
||||||
|
|
||||||
|
configured: bool = false,
|
||||||
|
|
||||||
|
pub fn init(context: *Context, output: *Output) !Bar {
|
||||||
|
return .{
|
||||||
|
.context = context,
|
||||||
|
.font = try getFcftFonts("monospace:size=14"),
|
||||||
|
.output = output,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add config options for whether it's top or bottom
|
||||||
|
pub fn initSurface(bar: *Bar) !void {
|
||||||
|
if (bar.layer_surface) |_| {
|
||||||
|
// This bar already has a layer surface, we can exit early
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = bar.context;
|
||||||
|
|
||||||
|
// TODO: Add padding to config
|
||||||
|
const padding = 5;
|
||||||
|
const bar_height: u31 = @intCast(bar.font.height + 2 * padding);
|
||||||
|
|
||||||
|
const wl_surface: *wl.Surface = try context.wl_compositor.createSurface();
|
||||||
|
errdefer wl_surface.destroy();
|
||||||
|
|
||||||
|
// TODO: Allow clicking on tags to switch between them?
|
||||||
|
// We don't want our surface to have any input region (default is infinite)
|
||||||
|
const empty_region: *wl.Region = try context.wl_compositor.createRegion();
|
||||||
|
defer empty_region.destroy();
|
||||||
|
wl_surface.setInputRegion(empty_region);
|
||||||
|
|
||||||
|
const layer_surface: *zwlr.LayerSurfaceV1 = try context
|
||||||
|
.zwlr_layer_shell_v1
|
||||||
|
.getLayerSurface(wl_surface, bar.output.wl_output, .top, "beansprout-bar");
|
||||||
|
layer_surface.setSize(0, bar_height);
|
||||||
|
layer_surface.setAnchor(.{ .top = true, .right = true, .left = true });
|
||||||
|
|
||||||
|
bar.wl_surface = wl_surface;
|
||||||
|
bar.layer_surface = layer_surface;
|
||||||
|
context.buffer_pool.surface_count += 1;
|
||||||
|
|
||||||
|
layer_surface.setListener(*Bar, layerSurfaceListener, bar);
|
||||||
|
wl_surface.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(bar: *Bar) void {
|
||||||
|
bar.configured = false;
|
||||||
|
if (bar.wl_surface) |wl_surface| {
|
||||||
|
wl_surface.destroy();
|
||||||
|
}
|
||||||
|
if (bar.layer_surface) |layer_surface| {
|
||||||
|
layer_surface.destroy();
|
||||||
|
bar.context.buffer_pool.surface_count -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn layerSurfaceListener(
|
||||||
|
layer_surface: *zwlr.LayerSurfaceV1,
|
||||||
|
event: zwlr.LayerSurfaceV1.Event,
|
||||||
|
bar: *Bar,
|
||||||
|
) void {
|
||||||
|
switch (event) {
|
||||||
|
.configure => |ev| {
|
||||||
|
layer_surface.ackConfigure(ev.serial);
|
||||||
|
const width: u31 = @intCast(ev.width);
|
||||||
|
const height: u31 = @intCast(ev.height);
|
||||||
|
|
||||||
|
if (bar.configured and bar.width == width and bar.height == height) {
|
||||||
|
if (bar.wl_surface) |wl_surface| {
|
||||||
|
wl_surface.commit();
|
||||||
|
} else {
|
||||||
|
log.warn("Bar is marked as configured but is missing a layer_surface for the wallpaper", .{});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("configuring bar surface with width {} and height {}", .{ width, height });
|
||||||
|
bar.width = width;
|
||||||
|
bar.height = height;
|
||||||
|
// Excluse zone == the bar's height
|
||||||
|
layer_surface.setExclusiveZone(bar.height);
|
||||||
|
|
||||||
|
// Full surface should be opaque
|
||||||
|
const opaque_region: *wl.Region = bar.context.wl_compositor.createRegion() catch |e| {
|
||||||
|
log.err("Failed to create opaque region for bar: {}", .{e});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// TODO: Need to change the x/y if we support anchoring to the bottom
|
||||||
|
opaque_region.add(0, 0, bar.width, bar.height);
|
||||||
|
defer opaque_region.destroy();
|
||||||
|
bar.wl_surface.?.setOpaqueRegion(opaque_region);
|
||||||
|
|
||||||
|
bar.configured = true;
|
||||||
|
|
||||||
|
log.debug("bwbuhse before render", .{});
|
||||||
|
bar.render() catch |err| {
|
||||||
|
log.err("Bar render failed: {}", .{err});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.closed => {
|
||||||
|
bar.deinit();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(bar: *Bar) !void {
|
||||||
|
const buffer = try bar.context.buffer_pool.nextBuffer(bar.context.wl_shm, bar.width, bar.height);
|
||||||
|
|
||||||
|
// Fill with a solid color (e.g., dark background)
|
||||||
|
const bg_color = pixman.Color{ .red = 0x1e1e, .green = 0x1e1e, .blue = 0x2e2e, .alpha = 0xffff };
|
||||||
|
const fill = pixman.Image.createSolidFill(&bg_color) orelse return;
|
||||||
|
defer _ = fill.unref();
|
||||||
|
pixman.Image.composite32(.src, fill, null, buffer.pixman_image, 0, 0, 0, 0, 0, 0, bar.width, bar.height);
|
||||||
|
|
||||||
|
const wl_surface = bar.wl_surface orelse return;
|
||||||
|
wl_surface.attach(buffer.wl_buffer, 0, 0);
|
||||||
|
wl_surface.damageBuffer(0, 0, bar.width, bar.height);
|
||||||
|
wl_surface.commit();
|
||||||
|
log.debug("bwbuhse end of render", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Borrowed and modified from https://git.sr.ht/~novakane/zig-fcft-example
|
||||||
|
fn renderChars(output: *Output, text: []const u32, buffer: *Buffer, color: *pixman.Image) !void {
|
||||||
|
const glyphs = try utils.gpa.alloc(*const fcft.Glyph, text.len);
|
||||||
|
const kerns = try utils.gpa.alloc(c_long, text.len);
|
||||||
|
defer utils.gpa.free(glyphs);
|
||||||
|
defer utils.gpa.free(kerns);
|
||||||
|
|
||||||
|
var text_width: i32 = 0;
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < text.len) : (i += 1) {
|
||||||
|
glyphs[i] = try output.ctx.fonts.rasterizeCharUtf32(text[i], .default);
|
||||||
|
|
||||||
|
kerns[i] = 0;
|
||||||
|
if (i > 0) {
|
||||||
|
var x_kern: c_long = 0;
|
||||||
|
if (output.ctx.fonts.kerning(text[i - 1], text[i], &x_kern, null)) {
|
||||||
|
kerns[i] = x_kern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_width += @intCast(kerns[i] + glyphs[i].advance.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
var x: i32 = @divFloor(buffer.width - text_width, 2);
|
||||||
|
const y: i32 = @divFloor(buffer.height - output.ctx.fonts.height, 2);
|
||||||
|
output.render_glyphs(buffer, &x, y, color, text.len, glyphs.ptr, kerns);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Borrowed https://git.sr.ht/~novakane/zig-fcft-example
|
||||||
|
fn renderGlyphs(
|
||||||
|
output: *Output,
|
||||||
|
buffer: *Buffer,
|
||||||
|
x: *i32,
|
||||||
|
y: i32,
|
||||||
|
color: *pixman.Image,
|
||||||
|
len: usize,
|
||||||
|
glyphs: [*]*const fcft.Glyph,
|
||||||
|
kerns: ?[]c_long,
|
||||||
|
) void {
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < len) : (i += 1) {
|
||||||
|
if (kerns) |kern| x.* += @intCast(kern[i]);
|
||||||
|
|
||||||
|
switch (pixman.Image.getFormat(glyphs[i].pix)) {
|
||||||
|
// Glyph is a pre-rendered image. (e.g. a color emoji)
|
||||||
|
.a8r8g8b8 => {
|
||||||
|
pixman.Image.composite32(
|
||||||
|
.over,
|
||||||
|
glyphs[i].pix,
|
||||||
|
null,
|
||||||
|
buffer.pixman_image,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
x.* + @as(i32, @intCast(glyphs[i].x)),
|
||||||
|
y + output.ctx.fonts.ascent - @as(i32, @intCast(glyphs[i].y)),
|
||||||
|
glyphs[i].width,
|
||||||
|
glyphs[i].height,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// Glyph is an alpha mask.
|
||||||
|
else => {
|
||||||
|
pixman.Image.composite32(
|
||||||
|
.over,
|
||||||
|
color,
|
||||||
|
glyphs[i].pix,
|
||||||
|
buffer.pixman_image,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
x.* + @as(i32, @intCast(glyphs[i].x)),
|
||||||
|
y + output.ctx.fonts.ascent - @as(i32, @intCast(glyphs[i].y)),
|
||||||
|
glyphs[i].width,
|
||||||
|
glyphs[i].height,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
x.* += glyphs[i].advance.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Borrowed from https://git.sr.ht/~novakane/zig-fcft-example
|
||||||
|
fn getFcftFonts(fonts: []const u8) !*fcft.Font {
|
||||||
|
// Create an arena to free just for this function;
|
||||||
|
// It makes cleaning up the ArrayList easier.
|
||||||
|
var arena = std.heap.ArenaAllocator.init(utils.gpa);
|
||||||
|
defer arena.deinit();
|
||||||
|
const arena_alloc = arena.allocator();
|
||||||
|
|
||||||
|
var list = try std.ArrayList([*:0]const u8).initCapacity(arena_alloc, 2);
|
||||||
|
|
||||||
|
var it = mem.tokenizeScalar(u8, fonts, ',');
|
||||||
|
while (it.next()) |font| {
|
||||||
|
try list.append(arena_alloc, try arena_alloc.dupeZ(u8, font));
|
||||||
|
}
|
||||||
|
|
||||||
|
const fcft_fonts = try fcft.Font.fromName(list.items[0..], null);
|
||||||
|
errdefer fcft_fonts.destroy();
|
||||||
|
fcft_fonts.setEmojiPresentation(.default);
|
||||||
|
|
||||||
|
return fcft_fonts;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const mem = std.mem;
|
||||||
|
|
||||||
|
const wayland = @import("wayland");
|
||||||
|
const wl = wayland.client.wl;
|
||||||
|
const zwlr = wayland.client.zwlr;
|
||||||
|
const fcft = @import("fcft");
|
||||||
|
const pixman = @import("pixman");
|
||||||
|
|
||||||
|
const utils = @import("utils.zig");
|
||||||
|
const Buffer = @import("Buffer.zig");
|
||||||
|
const Context = @import("Context.zig");
|
||||||
|
const Output = @import("Output.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.Bar);
|
||||||
|
|
@ -11,6 +11,9 @@ river_output_v1: *river.OutputV1,
|
||||||
// We have to wait for the rwm.wl_output event to get this
|
// We have to wait for the rwm.wl_output event to get this
|
||||||
wl_output: ?*wl.Output = null,
|
wl_output: ?*wl.Output = null,
|
||||||
|
|
||||||
|
// Friendly name of this output
|
||||||
|
name: ?[]const u8 = null,
|
||||||
|
|
||||||
// Output geometry
|
// Output geometry
|
||||||
scale: u31 = 1,
|
scale: u31 = 1,
|
||||||
width: u31 = 0,
|
width: u31 = 0,
|
||||||
|
|
@ -19,11 +22,15 @@ x: i32 = 0,
|
||||||
y: i32 = 0,
|
y: i32 = 0,
|
||||||
|
|
||||||
// Information for this Output's wallpaper
|
// Information for this Output's wallpaper
|
||||||
render_width: u31 = 0,
|
wallpaper_render_width: u31 = 0,
|
||||||
render_height: u31 = 0,
|
wallpaper_render_height: u31 = 0,
|
||||||
wl_surface: ?*wl.Surface = null,
|
wl_surface: ?*wl.Surface = null,
|
||||||
layer_surface: ?*zwlr.LayerSurfaceV1 = null,
|
layer_surface: ?*zwlr.LayerSurfaceV1 = null,
|
||||||
|
|
||||||
|
// TODO: Make Bar a user option, can disable if they want
|
||||||
|
// This Output's bar
|
||||||
|
bar: ?Bar,
|
||||||
|
|
||||||
/// Proportion of output width taken by the primary stack
|
/// Proportion of output width taken by the primary stack
|
||||||
primary_ratio: f32,
|
primary_ratio: f32,
|
||||||
|
|
||||||
|
|
@ -41,9 +48,6 @@ tags: u32 = 0x0001,
|
||||||
/// State consumed in manage() phase, reset at end of manage().
|
/// State consumed in manage() phase, reset at end of manage().
|
||||||
pending_manage: PendingManage = .{},
|
pending_manage: PendingManage = .{},
|
||||||
|
|
||||||
// Friendly name of this output
|
|
||||||
name: ?[]const u8 = null,
|
|
||||||
|
|
||||||
/// Used for wallpaper rendering management
|
/// Used for wallpaper rendering management
|
||||||
configured: bool = false,
|
configured: bool = false,
|
||||||
|
|
||||||
|
|
@ -72,9 +76,15 @@ pub fn create(context: *Context, river_output_v1: *river.OutputV1) !*Output {
|
||||||
var output = try utils.gpa.create(Output);
|
var output = try utils.gpa.create(Output);
|
||||||
errdefer output.destroy();
|
errdefer output.destroy();
|
||||||
|
|
||||||
|
const bar = Bar.init(context, output) catch |e| blk: {
|
||||||
|
log.err("Failed to create a bar: {}", .{e});
|
||||||
|
break :blk null;
|
||||||
|
};
|
||||||
|
|
||||||
output.* = .{
|
output.* = .{
|
||||||
.context = context,
|
.context = context,
|
||||||
.river_output_v1 = river_output_v1,
|
.river_output_v1 = river_output_v1,
|
||||||
|
.bar = bar,
|
||||||
.primary_count = context.config.primary_count,
|
.primary_count = context.config.primary_count,
|
||||||
.primary_ratio = context.config.primary_ratio,
|
.primary_ratio = context.config.primary_ratio,
|
||||||
.windows = undefined, // we will initialize this shortly
|
.windows = undefined, // we will initialize this shortly
|
||||||
|
|
@ -188,14 +198,20 @@ fn riverOutputListener(river_output_v1: *river.OutputV1, event: river.OutputV1.E
|
||||||
output.wl_output = output.context.wl_outputs.get(ev.name).?;
|
output.wl_output = output.context.wl_outputs.get(ev.name).?;
|
||||||
output.wl_output.?.setListener(*Output, wlOutputListener, output);
|
output.wl_output.?.setListener(*Output, wlOutputListener, output);
|
||||||
|
|
||||||
// The wl_output's initial events (mode, scale, name, done) were likely
|
// The wl_output's initial events come during the initial roundtrip
|
||||||
// already delivered during the initial roundtrip before we set our
|
// before we set our listener, so the .done event that triggers
|
||||||
// listener, so the .done event that triggers wallpaper init was lost.
|
// wallpaper init was lost. Explicitly init the surfaces here.
|
||||||
// Explicitly init the wallpaper surface here.
|
|
||||||
output.initWallpaperLayerSurface() catch |err| {
|
output.initWallpaperLayerSurface() catch |err| {
|
||||||
const output_name = output.name orelse "some output";
|
const output_name = output.name orelse "some output";
|
||||||
log.err("failed to add a surface to {s}: {}", .{ output_name, err });
|
log.err("failed to add a surface to {s}: {}", .{ output_name, err });
|
||||||
};
|
};
|
||||||
|
if (output.bar) |*bar| {
|
||||||
|
bar.initSurface() catch |err| {
|
||||||
|
const output_name = output.name orelse "some output";
|
||||||
|
log.err("failed to init bar for {s}: {}", .{ output_name, err });
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
.dimensions => |ev| {
|
.dimensions => |ev| {
|
||||||
// Protocol guarantees that width and height are strictly greater than zero
|
// Protocol guarantees that width and height are strictly greater than zero
|
||||||
|
|
@ -229,6 +245,13 @@ fn wlOutputListener(_: *wl.Output, event: wl.Output.Event, output: *Output) void
|
||||||
log.err("failed to add a surface to {s}: {}", .{ output_name, err });
|
log.err("failed to add a surface to {s}: {}", .{ output_name, err });
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if (output.bar) |*bar| {
|
||||||
|
bar.initSurface() catch |err| {
|
||||||
|
const output_name = output.name orelse "some output";
|
||||||
|
log.err("failed to init bar for {s}: {}", .{ output_name, err });
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
.scale => |ev| {
|
.scale => |ev| {
|
||||||
if (ev.factor < 0) {
|
if (ev.factor < 0) {
|
||||||
|
|
@ -251,7 +274,7 @@ pub fn initWallpaperLayerSurface(output: *Output) !void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.wl_surface) |_| {
|
if (output.layer_surface) |_| {
|
||||||
// This output already has a layer surface, we can exit early
|
// This output already has a layer surface, we can exit early
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -259,6 +282,7 @@ pub fn initWallpaperLayerSurface(output: *Output) !void {
|
||||||
const context = output.context;
|
const context = output.context;
|
||||||
|
|
||||||
const wl_surface: *wl.Surface = try context.wl_compositor.createSurface();
|
const wl_surface: *wl.Surface = try context.wl_compositor.createSurface();
|
||||||
|
errdefer wl_surface.destroy();
|
||||||
|
|
||||||
// We don't want our surface to have any input region (default is infinite)
|
// We don't want our surface to have any input region (default is infinite)
|
||||||
const empty_region: *wl.Region = try context.wl_compositor.createRegion();
|
const empty_region: *wl.Region = try context.wl_compositor.createRegion();
|
||||||
|
|
@ -267,10 +291,11 @@ pub fn initWallpaperLayerSurface(output: *Output) !void {
|
||||||
|
|
||||||
// Full surface should be opaque
|
// Full surface should be opaque
|
||||||
const opaque_region: *wl.Region = try context.wl_compositor.createRegion();
|
const opaque_region: *wl.Region = try context.wl_compositor.createRegion();
|
||||||
|
opaque_region.add(0, 0, output.width, output.height);
|
||||||
defer opaque_region.destroy();
|
defer opaque_region.destroy();
|
||||||
wl_surface.setOpaqueRegion(opaque_region);
|
wl_surface.setOpaqueRegion(opaque_region);
|
||||||
|
|
||||||
const layer_surface: *zwlr.LayerSurfaceV1 = try context.zwlr_layer_shell_v1.getLayerSurface(wl_surface, output.wl_output, .background, "beansprout");
|
const layer_surface: *zwlr.LayerSurfaceV1 = try context.zwlr_layer_shell_v1.getLayerSurface(wl_surface, output.wl_output, .background, "beansprout-wallpaper");
|
||||||
layer_surface.setExclusiveZone(-1);
|
layer_surface.setExclusiveZone(-1);
|
||||||
layer_surface.setAnchor(.{ .top = true, .right = true, .bottom = true, .left = true });
|
layer_surface.setAnchor(.{ .top = true, .right = true, .bottom = true, .left = true });
|
||||||
|
|
||||||
|
|
@ -301,15 +326,10 @@ fn wallpaperLayerSurfaceListener(layer_surface: *zwlr.LayerSurfaceV1, event: zwl
|
||||||
.configure => |ev| {
|
.configure => |ev| {
|
||||||
layer_surface.ackConfigure(ev.serial);
|
layer_surface.ackConfigure(ev.serial);
|
||||||
|
|
||||||
if (ev.width < 0 or ev.height < 0) {
|
|
||||||
// I'm not actually sure if this is possible, but just to be safe
|
|
||||||
log.warn("Received zwlr_layer_surface_v1.configure event with a negative width or height ({d}x{d})", .{ ev.width, ev.height });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const width: u31 = @intCast(ev.width);
|
const width: u31 = @intCast(ev.width);
|
||||||
const height: u31 = @intCast(ev.height);
|
const height: u31 = @intCast(ev.height);
|
||||||
|
|
||||||
if (output.configured and output.render_width == width and output.render_height == height) {
|
if (output.configured and output.wallpaper_render_width == width and output.wallpaper_render_height == height) {
|
||||||
if (output.wl_surface) |wl_surface| {
|
if (output.wl_surface) |wl_surface| {
|
||||||
wl_surface.commit();
|
wl_surface.commit();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -319,8 +339,8 @@ fn wallpaperLayerSurfaceListener(layer_surface: *zwlr.LayerSurfaceV1, event: zwl
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("configuring wallpaper surface with width {} and height {}", .{ width, height });
|
log.debug("configuring wallpaper surface with width {} and height {}", .{ width, height });
|
||||||
output.render_width = width;
|
output.wallpaper_render_width = width;
|
||||||
output.render_height = height;
|
output.wallpaper_render_height = height;
|
||||||
output.configured = true;
|
output.configured = true;
|
||||||
|
|
||||||
output.renderWallpaper() catch |err| {
|
output.renderWallpaper() catch |err| {
|
||||||
|
|
@ -334,7 +354,7 @@ fn wallpaperLayerSurfaceListener(layer_surface: *zwlr.LayerSurfaceV1, event: zwl
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates image_dimension / (output_dimension * scale)
|
/// Calculates image_dimension / (output_dimension * scale)
|
||||||
fn calculate_scale(image_dimension: c_int, output_dimension: u31, scale: u31) f64 {
|
fn calculateScale(image_dimension: c_int, output_dimension: u31, scale: u31) f64 {
|
||||||
const numerator: f64 = @floatFromInt(image_dimension);
|
const numerator: f64 = @floatFromInt(image_dimension);
|
||||||
const denominator: f64 = @floatFromInt(output_dimension * scale);
|
const denominator: f64 = @floatFromInt(output_dimension * scale);
|
||||||
|
|
||||||
|
|
@ -342,7 +362,7 @@ fn calculate_scale(image_dimension: c_int, output_dimension: u31, scale: u31) f6
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates (image_dimension / dimension_scale - output_dimension) / 2 / dimension_scale;
|
/// Calculates (image_dimension / dimension_scale - output_dimension) / 2 / dimension_scale;
|
||||||
fn calculate_transform(image_dimension: c_int, output_dimension: u31, dimension_scale: f64) f64 {
|
fn calculateTransform(image_dimension: c_int, output_dimension: u31, dimension_scale: f64) f64 {
|
||||||
const numerator1: f64 = @floatFromInt(image_dimension);
|
const numerator1: f64 = @floatFromInt(image_dimension);
|
||||||
const denominator1: f64 = dimension_scale;
|
const denominator1: f64 = dimension_scale;
|
||||||
const subtruend: f64 = @floatFromInt(output_dimension);
|
const subtruend: f64 = @floatFromInt(output_dimension);
|
||||||
|
|
@ -354,8 +374,8 @@ fn calculate_transform(image_dimension: c_int, output_dimension: u31, dimension_
|
||||||
/// Render the wallpaper image onto the layer surface
|
/// Render the wallpaper image onto the layer surface
|
||||||
pub fn renderWallpaper(output: *Output) !void {
|
pub fn renderWallpaper(output: *Output) !void {
|
||||||
const context = output.context;
|
const context = output.context;
|
||||||
const width = output.render_width;
|
const width = output.wallpaper_render_width;
|
||||||
const height = output.render_height;
|
const height = output.wallpaper_render_height;
|
||||||
const scale = output.scale;
|
const scale = output.scale;
|
||||||
|
|
||||||
// Don't have anything to render
|
// Don't have anything to render
|
||||||
|
|
@ -381,7 +401,7 @@ pub fn renderWallpaper(output: *Output) !void {
|
||||||
|
|
||||||
// Calculate image scale
|
// Calculate image scale
|
||||||
var sx: f64 = @as(f64, @floatFromInt(image_width)) / @as(f64, @floatFromInt(width * scale));
|
var sx: f64 = @as(f64, @floatFromInt(image_width)) / @as(f64, @floatFromInt(width * scale));
|
||||||
var sy: f64 = calculate_scale(image_height, height, scale);
|
var sy: f64 = calculateScale(image_height, height, scale);
|
||||||
|
|
||||||
const s = if (sx > sy) sy else sx;
|
const s = if (sx > sy) sy else sx;
|
||||||
sx = s;
|
sx = s;
|
||||||
|
|
@ -389,8 +409,8 @@ pub fn renderWallpaper(output: *Output) !void {
|
||||||
|
|
||||||
// Calculate translation offsets to center the image on the output.
|
// Calculate translation offsets to center the image on the output.
|
||||||
// If the scaled image is larger than the output, the offset crops equally from both sides.
|
// If the scaled image is larger than the output, the offset crops equally from both sides.
|
||||||
const tx: f64 = calculate_transform(image_width, width, sx);
|
const tx: f64 = calculateTransform(image_width, width, sx);
|
||||||
const ty: f64 = calculate_transform(image_height, height, sy);
|
const ty: f64 = calculateTransform(image_height, height, sy);
|
||||||
|
|
||||||
// Build a combined source-to-destination transform matrix.
|
// Build a combined source-to-destination transform matrix.
|
||||||
// Pixman transforms map destination pixels back to source pixels, so:
|
// Pixman transforms map destination pixels back to source pixels, so:
|
||||||
|
|
@ -613,6 +633,7 @@ const zwlr = wayland.client.zwlr;
|
||||||
const pixman = @import("pixman");
|
const pixman = @import("pixman");
|
||||||
|
|
||||||
const utils = @import("utils.zig");
|
const utils = @import("utils.zig");
|
||||||
|
const Bar = @import("Bar.zig");
|
||||||
const Buffer = @import("Buffer.zig");
|
const Buffer = @import("Buffer.zig");
|
||||||
const Context = @import("Context.zig");
|
const Context = @import("Context.zig");
|
||||||
const Window = @import("Window.zig");
|
const Window = @import("Window.zig");
|
||||||
|
|
|
||||||
12
src/main.zig
12
src/main.zig
|
|
@ -36,6 +36,7 @@ const usage: []const u8 =
|
||||||
\\
|
\\
|
||||||
;
|
;
|
||||||
|
|
||||||
|
// TODO: I'd like to clean this function up a bit and move some bits into helpers
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const result = flags.parser([*:0]const u8, &.{
|
const result = flags.parser([*:0]const u8, &.{
|
||||||
.{ .name = "h", .kind = .boolean },
|
.{ .name = "h", .kind = .boolean },
|
||||||
|
|
@ -78,6 +79,16 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize fcft
|
||||||
|
const fcft_log_level: fcft.LogClass = switch (runtime_log_level) {
|
||||||
|
.err => .err,
|
||||||
|
.warn => .warning,
|
||||||
|
.info => .info,
|
||||||
|
.debug => .debug,
|
||||||
|
};
|
||||||
|
_ = fcft.init(.auto, false, fcft_log_level);
|
||||||
|
defer fcft.fini();
|
||||||
|
|
||||||
const wayland_display_var = try utils.gpa.dupeZ(u8, process.getEnvVarOwned(utils.gpa, "WAYLAND_DISPLAY") catch {
|
const wayland_display_var = try utils.gpa.dupeZ(u8, process.getEnvVarOwned(utils.gpa, "WAYLAND_DISPLAY") catch {
|
||||||
fatal("Error getting WAYLAND_DISPLAY environment variable. Exiting", .{});
|
fatal("Error getting WAYLAND_DISPLAY environment variable. Exiting", .{});
|
||||||
});
|
});
|
||||||
|
|
@ -246,6 +257,7 @@ const wayland = @import("wayland");
|
||||||
const river = wayland.client.river;
|
const river = wayland.client.river;
|
||||||
const wl = wayland.client.wl;
|
const wl = wayland.client.wl;
|
||||||
const zwlr = wayland.client.zwlr;
|
const zwlr = wayland.client.zwlr;
|
||||||
|
const fcft = @import("fcft");
|
||||||
|
|
||||||
const flags = @import("flags.zig");
|
const flags = @import("flags.zig");
|
||||||
const utils = @import("utils.zig");
|
const utils = @import("utils.zig");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue