diff --git a/src/Bar.zig b/src/Bar.zig index 66c8b54..ce63d9b 100644 --- a/src/Bar.zig +++ b/src/Bar.zig @@ -25,17 +25,15 @@ output: *Output, // Bar geometry geometry: Rect = .{}, -surfaces: ?struct { +surfaces: struct { wl_surface: *wl.Surface, river_shell_surface: *river.ShellSurfaceV1, node: *river.NodeV1, -} = null, +}, pending_manage: PendingManage = .{}, pending_render: PendingRender = .{}, -configured: bool = false, - pub const PendingManage = struct { output_geometry: bool = false, }; @@ -116,21 +114,20 @@ pub fn init(context: *Context, output: *Output, options: Options) !Bar { .river_shell_surface = river_shell_surface, .node = node, }, - .configured = true, .pending_manage = .{ .output_geometry = true }, }; } pub fn deinit(bar: *Bar) void { - bar.configured = false; + bar.output.bar = null; + bar.timezone.deinit(); bar.fcft_fonts.destroy(); - if (bar.surfaces) |surfaces| { - surfaces.node.destroy(); - surfaces.river_shell_surface.destroy(); - surfaces.wl_surface.destroy(); - bar.context.buffer_pool.surface_count -= 1; - } + + bar.surfaces.node.destroy(); + bar.surfaces.river_shell_surface.destroy(); + bar.surfaces.wl_surface.destroy(); + bar.context.buffer_pool.surface_count -= 1; } /// Update bar options in-place without destroying/recreating Wayland surfaces @@ -144,52 +141,50 @@ pub fn reconfigure(bar: *Bar, options: Options) !void { } pub fn manage(bar: *Bar) !void { - if (!bar.configured) return; defer bar.pending_manage = .{}; - // The only manage actions we need to do are when the output changes geometry - if (!bar.pending_manage.output_geometry) return; + // Need to adjust for new output dimensions + if (bar.pending_manage.output_geometry) { + const output = bar.output; + const options = bar.options; - const output = bar.output; - const options = bar.options; + // Recreate fonts if the output scale changed, so geometry calculations + // below use the correct font metrics. + const scale = output.scale; + if (scale != bar.font_scale) { + bar.fcft_fonts.destroy(); + bar.fcft_fonts = try getFcftFonts(bar.options.fonts, scale); + bar.font_scale = scale; + } - // Recreate fonts if the output scale changed, so geometry calculations - // below use the correct font metrics. - const scale = output.scale; - if (scale != bar.font_scale) { - bar.fcft_fonts.destroy(); - bar.fcft_fonts = try getFcftFonts(bar.options.fonts, scale); - bar.font_scale = scale; + const logical_font_height = @divFloor(bar.fcft_fonts.height, @as(i32, bar.font_scale)); + const height: u31 = @intCast(logical_font_height + 2 * options.vertical_padding); + const width: u31 = output.geometry.width -| @as(u31, @intCast(options.margins.left + options.margins.right)); + + if (bar.geometry.width != width or bar.geometry.height != height) { + bar.geometry.width = width; + bar.geometry.height = height; + + const opaque_region = try bar.context.wl_compositor.createRegion(); + defer opaque_region.destroy(); + opaque_region.add(0, 0, width, height); + bar.surfaces.wl_surface.setOpaqueRegion(opaque_region); + } + + const x = output.geometry.x + options.margins.left; + const y = switch (options.position) { + .top => output.geometry.y + options.margins.top, + .bottom => output.geometry.y + output.geometry.height - bar.geometry.height - options.margins.bottom, + }; + bar.pending_render.position = .{ .x = x, .y = y }; + bar.pending_render.draw = true; } - - const logical_font_height = @divFloor(bar.fcft_fonts.height, @as(i32, bar.font_scale)); - const height: u31 = @intCast(logical_font_height + 2 * options.vertical_padding); - const width: u31 = output.geometry.width -| @as(u31, @intCast(options.margins.left + options.margins.right)); - - if (bar.geometry.width != width or bar.geometry.height != height) { - bar.geometry.width = width; - bar.geometry.height = height; - - const opaque_region = try bar.context.wl_compositor.createRegion(); - defer opaque_region.destroy(); - opaque_region.add(0, 0, width, height); - bar.surfaces.?.wl_surface.setOpaqueRegion(opaque_region); - } - - const x = output.geometry.x + options.margins.left; - const y = switch (options.position) { - .top => output.geometry.y + options.margins.top, - .bottom => output.geometry.y + output.geometry.height - bar.geometry.height - options.margins.bottom, - }; - bar.pending_render.position = .{ .x = x, .y = y }; - bar.pending_render.draw = true; } pub fn render(bar: *Bar) void { - if (!bar.configured) return; defer bar.pending_render = .{}; - const surfaces = bar.surfaces orelse return; + const surfaces = bar.surfaces; if (bar.pending_render.position) |position| { surfaces.node.setPosition(position.x, position.y); @@ -204,7 +199,7 @@ pub fn render(bar: *Bar) void { } /// Draw the bar and its components (clock, title, etc.) -pub fn draw(bar: *Bar) !void { +fn draw(bar: *Bar) !void { const context = bar.context; const options = bar.options; @@ -317,7 +312,7 @@ pub fn draw(bar: *Bar) !void { } // Attach the buffer to the surface - const surfaces = bar.surfaces orelse return error.NoSurfaces; + const surfaces = bar.surfaces; const wl_surface = surfaces.wl_surface; wl_surface.setBufferScale(scale); wl_surface.attach(buffer.wl_buffer, 0, 0); diff --git a/src/Context.zig b/src/Context.zig index 86cc9e7..cc9262a 100644 --- a/src/Context.zig +++ b/src/Context.zig @@ -241,9 +241,7 @@ pub fn manage(context: *Context) void { if (output.wallpaper) |*wp| wp.deinit(); output.wallpaper = null; } else if (output.wallpaper) |*wp| { - wp.render() catch |err| { - log.err("Wallpaper re-render failed: {}", .{err}); - }; + wp.pending_render.draw = true; } else { output.wallpaper = Wallpaper.init(context, output) catch |err| { log.err("Failed to init wallpaper surface: {}", .{err}); diff --git a/src/Output.zig b/src/Output.zig index aad00c4..67a2168 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -273,14 +273,6 @@ fn wlOutputListener(_: *wl.Output, event: wl.Output.Event, output: *Output) void bar.pending_manage.output_geometry = true; } } - // Re-render wallpaper if scale changed - if (output.wallpaper) |*wp| { - if (wp.configured and output.scale != wp.render_scale) { - wp.render() catch |err| { - log.err("Wallpaper render failed: {}", .{err}); - }; - } - } }, .scale => |ev| { if (ev.factor < 0) { @@ -288,7 +280,13 @@ fn wlOutputListener(_: *wl.Output, event: wl.Output.Event, output: *Output) void log.warn("Received wl_output.scale event with a negative factor ({d})", .{ev.factor}); return; } - output.scale = @intCast(ev.factor); + const new_scale: u31 = @intCast(ev.factor); + if (new_scale != output.scale) { + output.scale = new_scale; + if (output.wallpaper) |*wallpaper| { + wallpaper.pending_render.draw = true; + } + } }, .name => |ev| { if (output.name) |old_name| utils.gpa.free(old_name); @@ -311,6 +309,9 @@ pub fn manage(output: *Output) void { } if (output.pending_manage.position != null or output.pending_manage.dimensions != null) { + if (output.wallpaper) |*wallpaper| { + wallpaper.pending_render.draw = true; + } if (output.bar) |*bar| { bar.pending_manage.output_geometry = true; } @@ -459,6 +460,10 @@ pub fn manage(output: *Output) void { } pub fn render(output: *Output) void { + if (output.wallpaper) |*wallpaper| { + wallpaper.render(); + } + if (output.bar) |*bar| { bar.render(); } diff --git a/src/Wallpaper.zig b/src/Wallpaper.zig index 5fae8a9..60ff391 100644 --- a/src/Wallpaper.zig +++ b/src/Wallpaper.zig @@ -7,13 +7,17 @@ const Wallpaper = @This(); context: *Context, output: *Output, -wl_surface: *wl.Surface, -layer_surface: *zwlr.LayerSurfaceV1, +surfaces: struct { + wl_surface: *wl.Surface, + river_shell_surface: *river.ShellSurfaceV1, + node: *river.NodeV1, +}, -render_scale: u31 = 0, -render_width: u31 = 0, -render_height: u31 = 0, -configured: bool = false, +pending_render: PendingRender = .{}, + +pub const PendingRender = struct { + draw: bool = false, +}; /// Decoded image data shared across all outputs. /// Stored on Context; each output's Wallpaper references it for rendering. @@ -98,74 +102,73 @@ pub fn init(context: *Context, output: *Output) !Wallpaper { const wl_surface = try context.wl_compositor.createSurface(); errdefer wl_surface.destroy(); - const layer_surface = try context.zwlr_layer_shell_v1.getLayerSurface(wl_surface, output.wl_output, .background, "beansprout-wallpaper"); - errdefer layer_surface.destroy(); + const river_shell_surface = try context + .wm + .river_window_manager_v1 + .getShellSurface(wl_surface); + errdefer river_shell_surface.destroy(); + + const node = try river_shell_surface.getNode(); + errdefer node.destroy(); // We don't want our surface to have any input region (default is infinite) const empty_region = try context.wl_compositor.createRegion(); defer empty_region.destroy(); wl_surface.setInputRegion(empty_region); + context.buffer_pool.surface_count += 1; + // Full surface should be opaque const opaque_region = try context.wl_compositor.createRegion(); opaque_region.add(0, 0, output.geometry.width, output.geometry.height); defer opaque_region.destroy(); wl_surface.setOpaqueRegion(opaque_region); - layer_surface.setExclusiveZone(-1); - layer_surface.setAnchor(.{ .top = true, .right = true, .bottom = true, .left = true }); - - context.buffer_pool.surface_count += 1; - - layer_surface.setListener(*Output, layerSurfaceListener, output); - wl_surface.commit(); - return .{ .context = context, .output = output, - .wl_surface = wl_surface, - .layer_surface = layer_surface, + .surfaces = .{ + .wl_surface = wl_surface, + .river_shell_surface = river_shell_surface, + .node = node, + }, + .pending_render = .{ .draw = true }, }; } pub fn deinit(wallpaper: *Wallpaper) void { - wallpaper.layer_surface.destroy(); - wallpaper.wl_surface.destroy(); + wallpaper.output.wallpaper = null; + + wallpaper.surfaces.node.destroy(); + wallpaper.surfaces.river_shell_surface.destroy(); + wallpaper.surfaces.wl_surface.destroy(); wallpaper.context.buffer_pool.surface_count -= 1; } -fn layerSurfaceListener(layer_surface: *zwlr.LayerSurfaceV1, event: zwlr.LayerSurfaceV1.Event, output: *Output) void { - switch (event) { - .configure => |ev| { - layer_surface.ackConfigure(ev.serial); +pub fn render(wallpaper: *Wallpaper) void { + defer wallpaper.pending_render = .{}; - const wallpaper = &(output.wallpaper orelse return); + // We draw whenever the output's dimensions or scale change + if (wallpaper.pending_render.draw) { + const output = wallpaper.output; + const geometry = output.geometry; + const width = geometry.width; + const height = geometry.height; + const scale = output.scale; - const width: u31 = @intCast(ev.width); - const height: u31 = @intCast(ev.height); + const opaque_region = wallpaper.context.wl_compositor.createRegion() catch |err| { + log.err("Failed to create opaque region: {}", .{err}); + return; + }; + defer opaque_region.destroy(); + opaque_region.add(0, 0, width, height); - if (wallpaper.configured and - wallpaper.render_width == width and - wallpaper.render_height == height and - output.scale == wallpaper.render_scale) - { - wallpaper.wl_surface.commit(); - return; - } + wallpaper.surfaces.node.placeBottom(); - log.debug("configuring wallpaper surface with width {} and height {}", .{ width, height }); - wallpaper.render_width = width; - wallpaper.render_height = height; - wallpaper.configured = true; - - wallpaper.render() catch |err| { - log.err("Wallpaper render failed: {}", .{err}); - }; - }, - .closed => { - if (output.wallpaper) |*wp| wp.deinit(); - output.wallpaper = null; - }, + wallpaper.surfaces.wl_surface.setOpaqueRegion(opaque_region); + wallpaper.draw(width, height, scale) catch |err| { + log.err("Wallpaper draw failed: {}", .{err}); + }; } } @@ -188,12 +191,8 @@ fn calculateTransform(image_dimension: c_int, output_dimension: u31, dimension_s } /// Render the wallpaper image onto the layer surface -pub fn render(wallpaper: *Wallpaper) !void { +fn draw(wallpaper: *Wallpaper, width: u31, height: u31, scale: u31) !void { const context = wallpaper.context; - const output = wallpaper.output; - const width = wallpaper.render_width; - const height = wallpaper.render_height; - const scale = output.scale; // Don't have anything to render if (width == 0 or height == 0 or scale == 0) { @@ -253,13 +252,11 @@ pub fn render(wallpaper: *Wallpaper) !void { log.info("render: {}x{} (scaled from {}x{})", .{ width * scale, height * scale, image_width, image_height }); // Attach the buffer to the surface - const wl_surface = wallpaper.wl_surface; + const wl_surface = wallpaper.surfaces.wl_surface; wl_surface.setBufferScale(scale); wl_surface.attach(buffer.wl_buffer, 0, 0); wl_surface.damageBuffer(0, 0, width * scale, height * scale); wl_surface.commit(); - - wallpaper.render_scale = scale; } const std = @import("std"); @@ -268,7 +265,7 @@ const native_endian = builtin.cpu.arch.endian(); const wayland = @import("wayland"); const wl = wayland.client.wl; -const zwlr = wayland.client.zwlr; +const river = wayland.client.river; const pixman = @import("pixman"); const zigimg = @import("zigimg");