Fix Wallpaper when using multiple outputs

Every wallpaper's node was getting set to position (0,0)... which is
global, not output-local. Adding the node.setPosition() call fixed it.

I also cleaned up some comments and other bits of code in Wallpaper.zig

Fixes: #8
This commit is contained in:
Ben Buhse 2026-04-03 13:48:47 -05:00
commit 4b5405f847
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4

View file

@ -46,7 +46,7 @@ pub const Image = struct {
try image.zigimg_image.convert(utils.gpa, .rgba32); try image.zigimg_image.convert(utils.gpa, .rgba32);
} }
log.debug("image loaded ({}x{})", .{ image.zigimg_image.width, image.zigimg_image.height }); log.debug("Image loaded ({}x{})", .{ image.zigimg_image.width, image.zigimg_image.height });
const pixels = image.zigimg_image.pixels.rgba32; const pixels = image.zigimg_image.pixels.rgba32;
const width: c_int = @intCast(image.zigimg_image.width); const width: c_int = @intCast(image.zigimg_image.width);
@ -165,6 +165,10 @@ pub fn render(wallpaper: *Wallpaper) void {
opaque_region.add(0, 0, width, height); opaque_region.add(0, 0, width, height);
wallpaper.surfaces.node.placeBottom(); wallpaper.surfaces.node.placeBottom();
wallpaper.surfaces.node.setPosition(
wallpaper.output.geometry.x,
wallpaper.output.geometry.y,
);
wallpaper.surfaces.wl_surface.setOpaqueRegion(opaque_region); wallpaper.surfaces.wl_surface.setOpaqueRegion(opaque_region);
wallpaper.draw(width, height, scale) catch |err| { wallpaper.draw(width, height, scale) catch |err| {
@ -191,14 +195,14 @@ fn calculateTransform(image_dimension: c_int, output_dimension: u31, dimension_s
return numerator2 / 2 / dimension_scale; return numerator2 / 2 / dimension_scale;
} }
/// Render the wallpaper image onto the layer surface /// Draw the shared Wallpaper.Image onto the specific layer surface for this Wallpaper
fn draw(wallpaper: *Wallpaper, width: u31, height: u31, scale: u31) !void { fn draw(wallpaper: *Wallpaper, output_width: u31, output_height: u31, output_scale: u31) !void {
const context = wallpaper.context; const context = wallpaper.context;
// Don't have anything to render if (output_width == 0 or output_height == 0 or output_scale == 0) {
if (width == 0 or height == 0 or scale == 0) {
return; return;
} }
// Scale our loaded image and then copy it into the Buffer's pixman.Image // Scale our loaded image and then copy it into the Buffer's pixman.Image
const wp_image = context.wallpaper_image orelse return error.MissingWallpaperImage; const wp_image = context.wallpaper_image orelse return error.MissingWallpaperImage;
const image = wp_image.pix_image; const image = wp_image.pix_image;
@ -208,26 +212,37 @@ fn draw(wallpaper: *Wallpaper, width: u31, height: u31, scale: u31) !void {
const image_stride = image.getStride(); const image_stride = image.getStride();
const image_format = image.getFormat(); const image_format = image.getFormat();
const buffer = try context.buffer_pool.nextBuffer(context.wl_shm, width * scale, height * scale); const buffer = try context.buffer_pool.nextBuffer(
context.wl_shm,
output_width * output_scale,
output_height * output_scale,
);
const pix = pixman.Image.createBitsNoClear(image_format, image_width, image_height, image_data, image_stride) orelse { const pix = pixman.Image.createBitsNoClear(
image_format,
image_width,
image_height,
image_data,
image_stride,
) orelse {
log.err("Failed to copy the wallpaper image for rendering", .{}); log.err("Failed to copy the wallpaper image for rendering", .{});
return error.FailedToCreatePixmanImage; return error.FailedToCreatePixmanImage;
}; };
defer _ = pix.unref(); defer _ = pix.unref();
// Calculate image scale // Calculate image scale in both dimensions. We choose the larger of the two scales to use
var sx: f64 = @as(f64, @floatFromInt(image_width)) / @as(f64, @floatFromInt(width * scale)); // (they're not equal when image aspect ratio != the output's aspect ratio).
var sy: f64 = calculateScale(image_height, height, scale); const s = blk: {
const sx: f64 = calculateScale(image_width, output_width, output_scale);
const sy: f64 = calculateScale(image_height, output_height, output_scale);
const s = if (sx > sy) sy else sx; break :blk if (sx > sy) sy else sx;
sx = s; };
sy = s;
// 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 = calculateTransform(image_width, width * scale, sx); const tx: f64 = calculateTransform(image_width, output_width * output_scale, s);
const ty: f64 = calculateTransform(image_height, height * scale, sy); const ty: f64 = calculateTransform(image_height, output_height * output_scale, s);
// 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:
@ -240,23 +255,23 @@ fn draw(wallpaper: *Wallpaper, width: u31, height: u31, scale: u31) !void {
// t2 is the fixed-point version of t, which is what pixman actually uses internally // t2 is the fixed-point version of t, which is what pixman actually uses internally
var t2: pixman.Transform = undefined; var t2: pixman.Transform = undefined;
pixman.FTransform.initScale(&t_scale, sx, sy); pixman.FTransform.initScale(&t_scale, s, s);
pixman.FTransform.initTranslate(&t_trans, tx, ty); pixman.FTransform.initTranslate(&t_trans, tx, ty);
pixman.FTransform.multiply(&t, &t_trans, &t_scale); pixman.FTransform.multiply(&t, &t_scale, &t_trans);
_ = pixman.Transform.fromFTransform(&t2, &t); _ = pixman.Transform.fromFTransform(&t2, &t);
_ = pix.setTransform(&t2); _ = pix.setTransform(&t2);
_ = pix.setFilter(.best, &[_]pixman.Fixed{}, 0); _ = pix.setFilter(.best, &[_]pixman.Fixed{}, 0);
// Combine the transformed source image into the buffer. // Combine the transformed source image into the buffer.
pixman.Image.composite32(.src, pix, null, buffer.pixman_image, 0, 0, 0, 0, 0, 0, width * scale, height * scale); pixman.Image.composite32(.src, pix, null, buffer.pixman_image, 0, 0, 0, 0, 0, 0, output_width * output_scale, output_height * output_scale);
log.info("render: {}x{} (scaled from {}x{})", .{ width * scale, height * scale, image_width, image_height }); log.info("draw: {}x{} (scaled from {}x{})", .{ output_width * output_scale, output_height * output_scale, image_width, image_height });
// Attach the buffer to the surface // Attach the buffer to the surface
const wl_surface = wallpaper.surfaces.wl_surface; const wl_surface = wallpaper.surfaces.wl_surface;
wl_surface.setBufferScale(scale); wl_surface.setBufferScale(output_scale);
wl_surface.attach(buffer.wl_buffer, 0, 0); wl_surface.attach(buffer.wl_buffer, 0, 0);
wl_surface.damageBuffer(0, 0, width * scale, height * scale); wl_surface.damageBuffer(0, 0, output_width * output_scale, output_height * output_scale);
wl_surface.commit(); wl_surface.commit();
} }