Merge branch 'new-tag-overlay'
This commit is contained in:
commit
38400c66d6
12 changed files with 143 additions and 351 deletions
|
|
@ -195,29 +195,6 @@ tag_overlay {
|
||||||
| `square_inactive_border_color` | color | `0x6c7086` | Inactive tag square border |
|
| `square_inactive_border_color` | color | `0x6c7086` | Inactive tag square border |
|
||||||
| `square_inactive_occupied_color` | color | `0xcdd6f4` | Inactive tag occupied indicator |
|
| `square_inactive_occupied_color` | color | `0xcdd6f4` | Inactive tag occupied indicator |
|
||||||
|
|
||||||
### Anchors
|
|
||||||
|
|
||||||
The `anchors` child block controls which edge(s) of the screen the overlay
|
|
||||||
attaches to. Each direction is a boolean (`#true` / `#false`). Default: none, i.e. centered on output.
|
|
||||||
|
|
||||||
| Setting | Type | Default |
|
|
||||||
|----------|------|----------|
|
|
||||||
| `top` | bool | `#false` |
|
|
||||||
| `right` | bool | `#false` |
|
|
||||||
| `bottom` | bool | `#false` |
|
|
||||||
| `left` | bool | `#false` |
|
|
||||||
|
|
||||||
### Margins
|
|
||||||
|
|
||||||
The `margins` child block sets pixel offsets from the anchored edge(s).
|
|
||||||
|
|
||||||
| Setting | Type | Default |
|
|
||||||
|----------|------|---------|
|
|
||||||
| `top` | i32 | `0` |
|
|
||||||
| `right` | i32 | `0` |
|
|
||||||
| `bottom` | i32 | `0` |
|
|
||||||
| `left` | i32 | `0` |
|
|
||||||
|
|
||||||
## Keybinds
|
## Keybinds
|
||||||
|
|
||||||
Keyboard bindings are placed inside a `keybinds` block. Each binding has the
|
Keyboard bindings are placed inside a `keybinds` block. Each binding has the
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ These are in rough order of my priority, though no promises I do them in this or
|
||||||
- [ ] Save window positions between restarts
|
- [ ] Save window positions between restarts
|
||||||
- [ ] Support multiple seats
|
- [ ] Support multiple seats
|
||||||
- [ ] Support clipping floating windows on edge of/between outputs
|
- [ ] Support clipping floating windows on edge of/between outputs
|
||||||
- [ ] Use per-output timerfds for tag overlay instead of a single shared one
|
|
||||||
- [ ] Support configurable focus-follows-window on send-to-output
|
- [ ] Support configurable focus-follows-window on send-to-output
|
||||||
- [ ] Support configurable prepend/append on send-to-output
|
- [ ] Support configurable prepend/append on send-to-output
|
||||||
- [ ] Support taking new output's tags on send-to-output
|
- [ ] Support taking new output's tags on send-to-output
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,6 @@ pub fn init(context: *Context, output: *Output, options: Options) !Bar {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(bar: *Bar) void {
|
pub fn deinit(bar: *Bar) void {
|
||||||
bar.output.bar = null;
|
|
||||||
|
|
||||||
bar.timezone.deinit();
|
bar.timezone.deinit();
|
||||||
bar.fcft_fonts.destroy();
|
bar.fcft_fonts.destroy();
|
||||||
|
|
||||||
|
|
@ -128,6 +126,8 @@ pub fn deinit(bar: *Bar) void {
|
||||||
bar.surfaces.river_shell_surface.destroy();
|
bar.surfaces.river_shell_surface.destroy();
|
||||||
bar.surfaces.wl_surface.destroy();
|
bar.surfaces.wl_surface.destroy();
|
||||||
bar.context.buffer_pool.surface_count -= 1;
|
bar.context.buffer_pool.surface_count -= 1;
|
||||||
|
|
||||||
|
bar.output.bar = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update bar options in-place without destroying/recreating Wayland surfaces
|
/// Update bar options in-place without destroying/recreating Wayland surfaces
|
||||||
|
|
|
||||||
|
|
@ -161,5 +161,10 @@ const seal = switch (builtin.target.os.tag) {
|
||||||
},
|
},
|
||||||
else => @compileError("target OS not supported"),
|
else => @compileError("target OS not supported"),
|
||||||
};
|
};
|
||||||
|
comptime {
|
||||||
|
if (@hasField(os.linux.F, "SEAL_SEAL")) {
|
||||||
|
@compileError("SEAL_SEAL added to std.os.linux, get rid of the hardcoded values above.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const log = std.log.scoped(.Buffer);
|
const log = std.log.scoped(.Buffer);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ wl_registry: *wl.Registry,
|
||||||
wl_shm: *wl.Shm,
|
wl_shm: *wl.Shm,
|
||||||
|
|
||||||
river_layer_shell_v1: *river.LayerShellV1,
|
river_layer_shell_v1: *river.LayerShellV1,
|
||||||
zwlr_layer_shell_v1: *zwlr.LayerShellV1,
|
|
||||||
|
|
||||||
// Wayland globals that we have special structs for
|
// Wayland globals that we have special structs for
|
||||||
im: *InputManager,
|
im: *InputManager,
|
||||||
|
|
@ -33,10 +32,6 @@ wallpaper_image: ?*Wallpaper.Image,
|
||||||
// WM Configuration
|
// WM Configuration
|
||||||
config: *Config,
|
config: *Config,
|
||||||
|
|
||||||
/// Shared timerfd for hiding tag overlays after their timeout expires.
|
|
||||||
/// This stays null if no tag overlays exist.
|
|
||||||
tag_overlay_timer_fd: ?posix.fd_t,
|
|
||||||
|
|
||||||
/// 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 = .{},
|
||||||
|
|
||||||
|
|
@ -58,7 +53,6 @@ pub const Options = struct {
|
||||||
river_window_manager_v1: *river.WindowManagerV1,
|
river_window_manager_v1: *river.WindowManagerV1,
|
||||||
river_xkb_bindings_v1: *river.XkbBindingsV1,
|
river_xkb_bindings_v1: *river.XkbBindingsV1,
|
||||||
|
|
||||||
zwlr_layer_shell_v1: *zwlr.LayerShellV1,
|
|
||||||
config: *Config,
|
config: *Config,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -73,16 +67,11 @@ pub fn create(options: Options) !*Context {
|
||||||
const xkb_bindings = try XkbBindings.create(context, options.river_xkb_bindings_v1);
|
const xkb_bindings = try XkbBindings.create(context, options.river_xkb_bindings_v1);
|
||||||
errdefer xkb_bindings.destroy();
|
errdefer xkb_bindings.destroy();
|
||||||
|
|
||||||
const env = try process.getEnvMap(utils.gpa);
|
var env = try process.getEnvMap(utils.gpa);
|
||||||
errdefer env.deinit();
|
errdefer env.deinit();
|
||||||
|
|
||||||
const tag_overlay_timer_fd: ?posix.fd_t = if (options.config.tag_overlay_config) |_|
|
// Don't force children to have WAYLAND_DEBUG
|
||||||
posix.timerfd_create(.MONOTONIC, .{ .CLOEXEC = true }) catch |e| blk: {
|
env.remove("WAYLAND_DEBUG");
|
||||||
log.err("Failed to create tag overlay timer: {}", .{e});
|
|
||||||
break :blk null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
null;
|
|
||||||
|
|
||||||
context.* = .{
|
context.* = .{
|
||||||
.initialized = false,
|
.initialized = false,
|
||||||
|
|
@ -92,13 +81,11 @@ pub fn create(options: Options) !*Context {
|
||||||
.wl_registry = options.wl_registry,
|
.wl_registry = options.wl_registry,
|
||||||
.wl_shm = options.wl_shm,
|
.wl_shm = options.wl_shm,
|
||||||
.river_layer_shell_v1 = options.river_layer_shell_v1,
|
.river_layer_shell_v1 = options.river_layer_shell_v1,
|
||||||
.zwlr_layer_shell_v1 = options.zwlr_layer_shell_v1,
|
|
||||||
.wallpaper_image = loadWallpaperImage(options.config),
|
.wallpaper_image = loadWallpaperImage(options.config),
|
||||||
.im = im,
|
.im = im,
|
||||||
.wm = wm,
|
.wm = wm,
|
||||||
.xkb_bindings = xkb_bindings,
|
.xkb_bindings = xkb_bindings,
|
||||||
.config = options.config,
|
.config = options.config,
|
||||||
.tag_overlay_timer_fd = tag_overlay_timer_fd,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
|
@ -113,12 +100,10 @@ pub fn destroy(context: *Context) void {
|
||||||
if (context.wallpaper_image) |wallpaper_image| {
|
if (context.wallpaper_image) |wallpaper_image| {
|
||||||
wallpaper_image.destroy();
|
wallpaper_image.destroy();
|
||||||
}
|
}
|
||||||
if (context.tag_overlay_timer_fd) |fd| posix.close(fd);
|
|
||||||
context.buffer_pool.deinit();
|
context.buffer_pool.deinit();
|
||||||
|
|
||||||
// Destroy Wayland globals
|
// Destroy Wayland globals
|
||||||
context.river_layer_shell_v1.destroy();
|
context.river_layer_shell_v1.destroy();
|
||||||
context.zwlr_layer_shell_v1.destroy();
|
|
||||||
context.wl_shm.destroy();
|
context.wl_shm.destroy();
|
||||||
context.wl_compositor.destroy();
|
context.wl_compositor.destroy();
|
||||||
context.wl_registry.destroy();
|
context.wl_registry.destroy();
|
||||||
|
|
@ -168,30 +153,21 @@ pub fn manage(context: *Context) void {
|
||||||
// Handle tag overlay config changes
|
// Handle tag overlay config changes
|
||||||
const has_overlay = new_config.tag_overlay_config != null;
|
const has_overlay = new_config.tag_overlay_config != null;
|
||||||
|
|
||||||
if (!had_overlay and has_overlay) {
|
// Reconfigure, create, or destroy tag overlays on all outputs
|
||||||
// Create timerfd for newly enabled tag overlay
|
|
||||||
context.tag_overlay_timer_fd = posix.timerfd_create(.MONOTONIC, .{ .CLOEXEC = true }) catch |e| blk: {
|
|
||||||
log.err("Failed to create tag overlay timer: {}", .{e});
|
|
||||||
break :blk null;
|
|
||||||
};
|
|
||||||
} else if (had_overlay and !has_overlay) {
|
|
||||||
// Close timerfd for disabled tag overlay
|
|
||||||
if (context.tag_overlay_timer_fd) |fd| posix.close(fd);
|
|
||||||
context.tag_overlay_timer_fd = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recreate or destroy tag overlays on all outputs
|
|
||||||
if (had_overlay or has_overlay) {
|
if (had_overlay or has_overlay) {
|
||||||
var out_it = context.wm.outputs.iterator(.forward);
|
var out_it = context.wm.outputs.iterator(.forward);
|
||||||
while (out_it.next()) |output| {
|
while (out_it.next()) |output| {
|
||||||
// Destroy existing overlay
|
|
||||||
if (output.tag_overlay) |*tag_overlay| {
|
if (output.tag_overlay) |*tag_overlay| {
|
||||||
tag_overlay.deinit();
|
|
||||||
output.tag_overlay = null;
|
|
||||||
}
|
|
||||||
// Create new overlay if configured
|
|
||||||
// Create new overlay struct if configured (surfaces created on-demand)
|
|
||||||
if (new_config.tag_overlay_config) |tag_overlay_config| {
|
if (new_config.tag_overlay_config) |tag_overlay_config| {
|
||||||
|
// Reconfigure existing overlay
|
||||||
|
tag_overlay.deinitSurfaces();
|
||||||
|
tag_overlay.reconfigure(tag_overlay_config.toTagOverlayOptions());
|
||||||
|
} else {
|
||||||
|
// Remove overlay
|
||||||
|
tag_overlay.deinit();
|
||||||
|
}
|
||||||
|
} else if (new_config.tag_overlay_config) |tag_overlay_config| {
|
||||||
|
// Create new overlay struct (surfaces created on-demand)
|
||||||
output.tag_overlay = TagOverlay.init(context, output, tag_overlay_config.toTagOverlayOptions()) catch |e| {
|
output.tag_overlay = TagOverlay.init(context, output, tag_overlay_config.toTagOverlayOptions()) catch |e| {
|
||||||
log.err("Failed to create tag overlay: {}", .{e});
|
log.err("Failed to create tag overlay: {}", .{e});
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -282,7 +258,6 @@ const process = std.process;
|
||||||
const wayland = @import("wayland");
|
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 utils = @import("utils.zig");
|
const utils = @import("utils.zig");
|
||||||
const Bar = @import("Bar.zig");
|
const Bar = @import("Bar.zig");
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,6 @@ pub fn create(context: *Context, river_output_v1: *river.OutputV1) !*Output {
|
||||||
break :blk null;
|
break :blk null;
|
||||||
};
|
};
|
||||||
} else null;
|
} else null;
|
||||||
errdefer if (output.tag_overlay) |*to| to.deinit();
|
|
||||||
|
|
||||||
output.windows.init();
|
output.windows.init();
|
||||||
|
|
||||||
|
|
@ -388,31 +387,15 @@ pub fn manage(output: *Output) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show tag overlay and arm the hide timer
|
// Show tag overlay (or refresh its timeout if already visible)
|
||||||
if (output.tag_overlay) |*tag_overlay| {
|
if (output.tag_overlay) |*tag_overlay| blk: {
|
||||||
if (tag_overlay.surfaces) |_| {
|
tag_overlay.initSurfaces() catch |e| {
|
||||||
// The overlay is already visible, but we still need to re-render
|
log.err("Failed to show TagOverlay: {}", .{e});
|
||||||
tag_overlay.render() catch |err| {
|
break :blk;
|
||||||
log.err("tag_overlay render failed: {}", .{err});
|
|
||||||
};
|
};
|
||||||
} else {
|
tag_overlay.last_shown = time.Instant.now() catch
|
||||||
// Create surface; the configure handler renders for us
|
std.process.fatal("System does not support a monotonic or steady clock", .{});
|
||||||
tag_overlay.initSurface() catch |err| {
|
tag_overlay.pending_render.draw = true;
|
||||||
log.err("tag_overlay initSurface failed: {}", .{err});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (output.context.tag_overlay_timer_fd) |fd| {
|
|
||||||
const timeout_ms: isize = tag_overlay.options.timeout;
|
|
||||||
posix.timerfd_settime(fd, .{}, &.{
|
|
||||||
.it_interval = .{ .sec = 0, .nsec = 0 },
|
|
||||||
.it_value = .{
|
|
||||||
.sec = @divFloor(timeout_ms, 1000),
|
|
||||||
.nsec = @mod(timeout_ms, 1000) * std.time.ns_per_ms,
|
|
||||||
},
|
|
||||||
}, null) catch |err| {
|
|
||||||
log.err("Failed to arm tag overlay timer: {}", .{err});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.bar) |*bar| {
|
if (output.bar) |*bar| {
|
||||||
|
|
@ -487,6 +470,10 @@ pub fn render(output: *Output) void {
|
||||||
f.river_node_v1.placeTop();
|
f.river_node_v1.placeTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (output.tag_overlay) |*tag_overlay| {
|
||||||
|
tag_overlay.render();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate primary/stack layout positions for all windows.
|
/// Calculate primary/stack layout positions for all windows.
|
||||||
|
|
@ -649,12 +636,12 @@ const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const posix = std.posix;
|
const posix = std.posix;
|
||||||
|
const time = std.time;
|
||||||
const DoublyLinkedList = std.DoublyLinkedList;
|
const DoublyLinkedList = std.DoublyLinkedList;
|
||||||
|
|
||||||
const wayland = @import("wayland");
|
const wayland = @import("wayland");
|
||||||
const wl = wayland.client.wl;
|
const wl = wayland.client.wl;
|
||||||
const river = wayland.client.river;
|
const river = wayland.client.river;
|
||||||
const zwlr = wayland.client.zwlr;
|
|
||||||
|
|
||||||
const utils = @import("utils.zig");
|
const utils = @import("utils.zig");
|
||||||
const Rect = utils.Rect;
|
const Rect = utils.Rect;
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,23 @@ options: Options,
|
||||||
|
|
||||||
output: *Output,
|
output: *Output,
|
||||||
|
|
||||||
// Overlay geometry
|
|
||||||
width: u31 = 0,
|
|
||||||
height: u31 = 0,
|
|
||||||
scale: u31 = 1,
|
|
||||||
|
|
||||||
rows: u31,
|
rows: u31,
|
||||||
|
|
||||||
surfaces: ?struct {
|
surfaces: ?struct {
|
||||||
wl_surface: *wl.Surface,
|
wl_surface: *wl.Surface,
|
||||||
layer_surface: *zwlr.LayerSurfaceV1,
|
river_shell_surface: *river.ShellSurfaceV1,
|
||||||
|
node: *river.NodeV1,
|
||||||
} = null,
|
} = null,
|
||||||
|
|
||||||
configured: bool = false,
|
/// Monotonic timestamp of the last time the overlay was shown/refreshed.
|
||||||
|
/// Used by the event loop to hide the overlay after `options.timeout` ms.
|
||||||
|
last_shown: ?time.Instant = null,
|
||||||
|
|
||||||
|
pending_render: PendingRender = .{},
|
||||||
|
|
||||||
|
pub const PendingRender = struct {
|
||||||
|
draw: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Options = struct {
|
pub const Options = struct {
|
||||||
/// Width of the widget border in pixels
|
/// Width of the widget border in pixels
|
||||||
|
|
@ -65,10 +69,6 @@ pub const Options = struct {
|
||||||
// square_urgent_border_color: pixman.Color,
|
// square_urgent_border_color: pixman.Color,
|
||||||
// /// Occupied indicator color of urgent tag squares
|
// /// Occupied indicator color of urgent tag squares
|
||||||
// square_urgent_occupied_color: pixman.Color,
|
// square_urgent_occupied_color: pixman.Color,
|
||||||
/// Directional anchors top, right bottom, left; true for on, false for off
|
|
||||||
anchors: zwlr.LayerSurfaceV1.Anchor,
|
|
||||||
/// Directional margins top, right, bottom, left, in pixels
|
|
||||||
margins: struct { top: i32 = 0, right: i32 = 0, bottom: i32 = 0, left: i32 = 0 } = .{},
|
|
||||||
/// Duration of widget display in milliseconds
|
/// Duration of widget display in milliseconds
|
||||||
timeout: u32,
|
timeout: u32,
|
||||||
};
|
};
|
||||||
|
|
@ -84,113 +84,86 @@ pub fn init(context: *Context, output: *Output, options: Options) !TagOverlay {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initSurface(tag_overlay: *TagOverlay) !void {
|
pub fn deinit(tag_overlay: *TagOverlay) void {
|
||||||
if (tag_overlay.surfaces) |_| {
|
tag_overlay.deinitSurfaces();
|
||||||
// This tag overlay already has a layer surface, we can exit early
|
tag_overlay.output.tag_overlay = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reconfigure(tag_overlay: *TagOverlay, options: Options) void {
|
||||||
|
tag_overlay.options = options;
|
||||||
|
tag_overlay.rows = math.divCeil(u31, options.tag_amount, options.tags_per_row) catch {
|
||||||
|
log.err("Failed to calculate rows for tag overlay", .{});
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initSurfaces(tag_overlay: *TagOverlay) !void {
|
||||||
|
if (tag_overlay.surfaces != null) return;
|
||||||
|
|
||||||
const context = tag_overlay.context;
|
const context = tag_overlay.context;
|
||||||
const options = tag_overlay.options;
|
|
||||||
|
|
||||||
const wl_surface = try context.wl_compositor.createSurface();
|
const wl_surface = try context.wl_compositor.createSurface();
|
||||||
errdefer wl_surface.destroy();
|
errdefer wl_surface.destroy();
|
||||||
|
|
||||||
const layer_surface = try context
|
const river_shell_surface = try context
|
||||||
.zwlr_layer_shell_v1
|
.wm
|
||||||
.getLayerSurface(wl_surface, tag_overlay.output.wl_output, .overlay, "beansprout-tag-overlay");
|
.river_window_manager_v1
|
||||||
errdefer layer_surface.destroy();
|
.getShellSurface(wl_surface);
|
||||||
layer_surface.setExclusiveZone(-1);
|
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)
|
// We don't want our surface to have any input region (default is infinite)
|
||||||
const empty_region = try context.wl_compositor.createRegion();
|
const empty_region = try context.wl_compositor.createRegion();
|
||||||
defer empty_region.destroy();
|
defer empty_region.destroy();
|
||||||
wl_surface.setInputRegion(empty_region);
|
wl_surface.setInputRegion(empty_region);
|
||||||
|
|
||||||
const surface_width: u31 = @as(u31, @min(options.tag_amount, options.tags_per_row)) * (@as(u31, options.square_size) + options.square_padding) + options.square_padding + 2 * @as(u31, options.border_width);
|
const now = time.Instant.now() catch
|
||||||
const surface_height: u31 = @as(u31, tag_overlay.rows) * (@as(u31, options.square_size) + options.square_padding) + options.square_padding + 2 * @as(u31, options.border_width);
|
std.process.fatal("System does not support a monotonic or steady clock", .{});
|
||||||
layer_surface.setSize(surface_width, surface_height);
|
|
||||||
|
|
||||||
layer_surface.setAnchor(options.anchors);
|
|
||||||
layer_surface.setMargin(options.margins.top, options.margins.right, options.margins.bottom, options.margins.left);
|
|
||||||
|
|
||||||
tag_overlay.surfaces = .{ .wl_surface = wl_surface, .layer_surface = layer_surface };
|
|
||||||
context.buffer_pool.surface_count += 1;
|
context.buffer_pool.surface_count += 1;
|
||||||
|
|
||||||
layer_surface.setListener(*TagOverlay, layerSurfaceListener, tag_overlay);
|
tag_overlay.surfaces = .{
|
||||||
wl_surface.commit();
|
.wl_surface = wl_surface,
|
||||||
|
.river_shell_surface = river_shell_surface,
|
||||||
|
.node = node,
|
||||||
|
};
|
||||||
|
tag_overlay.last_shown = now;
|
||||||
|
tag_overlay.pending_render.draw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroy surfaces only (used to hide the overlay). The TagOverlay struct stays valid
|
|
||||||
/// and can be re-shown by calling initSurface() again.
|
|
||||||
pub fn deinitSurfaces(tag_overlay: *TagOverlay) void {
|
pub fn deinitSurfaces(tag_overlay: *TagOverlay) void {
|
||||||
tag_overlay.configured = false;
|
const surfaces = tag_overlay.surfaces orelse return;
|
||||||
if (tag_overlay.surfaces) |surfaces| {
|
surfaces.node.destroy();
|
||||||
surfaces.layer_surface.destroy();
|
surfaces.river_shell_surface.destroy();
|
||||||
surfaces.wl_surface.destroy();
|
surfaces.wl_surface.destroy();
|
||||||
tag_overlay.context.buffer_pool.surface_count -= 1;
|
tag_overlay.context.buffer_pool.surface_count -= 1;
|
||||||
tag_overlay.surfaces = null;
|
tag_overlay.surfaces = null;
|
||||||
}
|
tag_overlay.last_shown = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(tag_overlay: *TagOverlay) void {
|
pub fn render(tag_overlay: *TagOverlay) void {
|
||||||
tag_overlay.deinitSurfaces();
|
defer tag_overlay.pending_render = .{};
|
||||||
}
|
|
||||||
|
|
||||||
pub fn layerSurfaceListener(
|
if (tag_overlay.pending_render.draw) {
|
||||||
layer_surface: *zwlr.LayerSurfaceV1,
|
tag_overlay.draw() catch |err| {
|
||||||
event: zwlr.LayerSurfaceV1.Event,
|
log.err("tag_overlay draw failed: {}", .{err});
|
||||||
tag_overlay: *TagOverlay,
|
|
||||||
) void {
|
|
||||||
assert(tag_overlay.surfaces.?.layer_surface == layer_surface);
|
|
||||||
switch (event) {
|
|
||||||
.configure => |ev| {
|
|
||||||
layer_surface.ackConfigure(ev.serial);
|
|
||||||
const width: u31 = @intCast(ev.width);
|
|
||||||
const height: u31 = @intCast(ev.height);
|
|
||||||
|
|
||||||
if (tag_overlay.configured and
|
|
||||||
tag_overlay.width == width and
|
|
||||||
tag_overlay.height == height and
|
|
||||||
tag_overlay.output.scale == tag_overlay.scale)
|
|
||||||
{
|
|
||||||
tag_overlay.surfaces.?.wl_surface.commit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("Configuring tag_overlay surface with width {} and height {}", .{ width, height });
|
|
||||||
tag_overlay.width = width;
|
|
||||||
tag_overlay.height = height;
|
|
||||||
|
|
||||||
// Full surface should be opaque
|
|
||||||
const opaque_region: *wl.Region = tag_overlay.context.wl_compositor.createRegion() catch |e| {
|
|
||||||
log.err("Failed to create opaque region for tag_overlay: {}", .{e});
|
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
defer opaque_region.destroy();
|
|
||||||
opaque_region.add(0, 0, tag_overlay.width, tag_overlay.height);
|
|
||||||
tag_overlay.surfaces.?.wl_surface.setOpaqueRegion(opaque_region);
|
|
||||||
|
|
||||||
tag_overlay.configured = true;
|
|
||||||
|
|
||||||
tag_overlay.render() catch |err| {
|
|
||||||
log.err("tag_overlay render failed: {}", .{err});
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.closed => {
|
|
||||||
tag_overlay.deinit();
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(tag_overlay: *TagOverlay) !void {
|
pub fn draw(tag_overlay: *TagOverlay) !void {
|
||||||
|
const surfaces = tag_overlay.surfaces orelse return;
|
||||||
const context = tag_overlay.context;
|
const context = tag_overlay.context;
|
||||||
const options = tag_overlay.options;
|
const options = tag_overlay.options;
|
||||||
|
|
||||||
// Scaled width/height
|
// Scaled width/height
|
||||||
|
const surface_width: u31 = @as(u31, @min(options.tag_amount, options.tags_per_row)) * (@as(u31, options.square_size) + options.square_padding) + options.square_padding + 2 * @as(u31, options.border_width);
|
||||||
|
const surface_height: u31 = @as(u31, tag_overlay.rows) * (@as(u31, options.square_size) + options.square_padding) + options.square_padding + 2 * @as(u31, options.border_width);
|
||||||
const scale = tag_overlay.output.scale;
|
const scale = tag_overlay.output.scale;
|
||||||
const render_width = tag_overlay.width * scale;
|
const render_width = surface_width * scale;
|
||||||
const render_height = tag_overlay.height * scale;
|
const render_height = surface_height * scale;
|
||||||
|
|
||||||
// Don't have anything to render
|
// Don't have anything to render
|
||||||
if (render_width == 0 or render_height == 0) {
|
if (render_width == 0 or render_height == 0) {
|
||||||
|
|
@ -198,7 +171,7 @@ pub fn render(tag_overlay: *TagOverlay) !void {
|
||||||
}
|
}
|
||||||
const buffer = try context.buffer_pool.nextBuffer(context.wl_shm, render_width, render_height);
|
const buffer = try context.buffer_pool.nextBuffer(context.wl_shm, render_width, render_height);
|
||||||
|
|
||||||
buffer.borderedRectangle(.{ .width = tag_overlay.width, .height = tag_overlay.height }, options.border_width, scale, &options.background_color, &options.border_color);
|
buffer.borderedRectangle(.{ .width = surface_width, .height = surface_height }, options.border_width, scale, &options.background_color, &options.border_color);
|
||||||
|
|
||||||
const focused_tags = tag_overlay.output.tags;
|
const focused_tags = tag_overlay.output.tags;
|
||||||
const occupied_tags = tag_overlay.output.occupiedTags();
|
const occupied_tags = tag_overlay.output.occupiedTags();
|
||||||
|
|
@ -237,8 +210,14 @@ pub fn render(tag_overlay: *TagOverlay) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Position the overlay centered on the output
|
||||||
|
const output_geo = tag_overlay.output.geometry;
|
||||||
|
const x = output_geo.x + @divFloor(output_geo.width - surface_width, 2);
|
||||||
|
const y = output_geo.y + @divFloor(output_geo.height - surface_height, 2);
|
||||||
|
surfaces.node.setPosition(x, y);
|
||||||
|
surfaces.node.placeTop();
|
||||||
|
|
||||||
// Finally, attach the buffer to the surface
|
// Finally, attach the buffer to the surface
|
||||||
const surfaces = tag_overlay.surfaces orelse return error.NoSurfaces;
|
|
||||||
const wl_surface = surfaces.wl_surface;
|
const wl_surface = surfaces.wl_surface;
|
||||||
wl_surface.setBufferScale(scale);
|
wl_surface.setBufferScale(scale);
|
||||||
wl_surface.attach(buffer.wl_buffer, 0, 0);
|
wl_surface.attach(buffer.wl_buffer, 0, 0);
|
||||||
|
|
@ -249,10 +228,11 @@ pub fn render(tag_overlay: *TagOverlay) !void {
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const math = std.math;
|
const math = std.math;
|
||||||
|
const time = std.time;
|
||||||
|
|
||||||
const wayland = @import("wayland");
|
const wayland = @import("wayland");
|
||||||
const wl = wayland.client.wl;
|
const wl = wayland.client.wl;
|
||||||
const zwlr = wayland.client.zwlr;
|
const river = wayland.client.river;
|
||||||
const fcft = @import("fcft");
|
const fcft = @import("fcft");
|
||||||
const pixman = @import("pixman");
|
const pixman = @import("pixman");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,12 +137,13 @@ pub fn init(context: *Context, output: *Output) !Wallpaper {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(wallpaper: *Wallpaper) void {
|
pub fn deinit(wallpaper: *Wallpaper) void {
|
||||||
wallpaper.output.wallpaper = null;
|
|
||||||
|
|
||||||
wallpaper.surfaces.node.destroy();
|
wallpaper.surfaces.node.destroy();
|
||||||
wallpaper.surfaces.river_shell_surface.destroy();
|
wallpaper.surfaces.river_shell_surface.destroy();
|
||||||
wallpaper.surfaces.wl_surface.destroy();
|
wallpaper.surfaces.wl_surface.destroy();
|
||||||
|
|
||||||
wallpaper.context.buffer_pool.surface_count -= 1;
|
wallpaper.context.buffer_pool.surface_count -= 1;
|
||||||
|
|
||||||
|
wallpaper.output.wallpaper = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(wallpaper: *Wallpaper) void {
|
pub fn render(wallpaper: *Wallpaper) void {
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@ const XkbBinding = struct {
|
||||||
switch (xkb_binding.command) {
|
switch (xkb_binding.command) {
|
||||||
.spawn => |cmd| {
|
.spawn => |cmd| {
|
||||||
var child = std.process.Child.init(cmd, utils.gpa);
|
var child = std.process.Child.init(cmd, utils.gpa);
|
||||||
|
child.env_map = &context.env;
|
||||||
_ = child.spawn() catch |err| {
|
_ = child.spawn() catch |err| {
|
||||||
log.err("Failed to spawn \"{s}\": {}", .{ cmd[0], err });
|
log.err("Failed to spawn \"{s}\": {}", .{ cmd[0], err });
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -126,9 +126,7 @@ pub fn load(config: *Config, parser: *kdl.Parser, name: ?[]const u8, hostname: ?
|
||||||
helpers.logWarnInvalidNode(node.name);
|
helpers.logWarnInvalidNode(node.name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.child_block_begin => {
|
.child_block_begin => try helpers.skipChildBlock(parser),
|
||||||
try helpers.skipChildBlock(parser);
|
|
||||||
},
|
|
||||||
.child_block_end => {
|
.child_block_end => {
|
||||||
try config.input_configs.append(utils.gpa, input_config);
|
try config.input_configs.append(utils.gpa, input_config);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,8 @@ const NodeName = enum {
|
||||||
square_inactive_border_color,
|
square_inactive_border_color,
|
||||||
square_inactive_occupied_color,
|
square_inactive_occupied_color,
|
||||||
timeout,
|
timeout,
|
||||||
anchors,
|
|
||||||
margins,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const AnchorsNodeName = enum { top, right, bottom, left };
|
|
||||||
const MarginsNodeName = enum { top, right, bottom, left };
|
|
||||||
|
|
||||||
border_width: u8 = 2,
|
border_width: u8 = 2,
|
||||||
tag_amount: u8 = 9,
|
tag_amount: u8 = 9,
|
||||||
tags_per_row: u8 = 32,
|
tags_per_row: u8 = 32,
|
||||||
|
|
@ -44,14 +39,6 @@ square_inactive_background_color: pixman.Color = utils.parseRgbaPixmanComptime("
|
||||||
square_inactive_border_color: pixman.Color = utils.parseRgbaPixmanComptime("0x6c7086"),
|
square_inactive_border_color: pixman.Color = utils.parseRgbaPixmanComptime("0x6c7086"),
|
||||||
square_inactive_occupied_color: pixman.Color = utils.parseRgbaPixmanComptime("0xcdd6f4"),
|
square_inactive_occupied_color: pixman.Color = utils.parseRgbaPixmanComptime("0xcdd6f4"),
|
||||||
timeout: u32 = 500,
|
timeout: u32 = 500,
|
||||||
anchor_top: bool = false,
|
|
||||||
anchor_right: bool = false,
|
|
||||||
anchor_bottom: bool = false,
|
|
||||||
anchor_left: bool = false,
|
|
||||||
margin_top: i32 = 0,
|
|
||||||
margin_right: i32 = 0,
|
|
||||||
margin_bottom: i32 = 0,
|
|
||||||
margin_left: i32 = 0,
|
|
||||||
|
|
||||||
pub fn toTagOverlayOptions(config: TagOverlayConfig) TagOverlay.Options {
|
pub fn toTagOverlayOptions(config: TagOverlayConfig) TagOverlay.Options {
|
||||||
return .{
|
return .{
|
||||||
|
|
@ -70,18 +57,6 @@ pub fn toTagOverlayOptions(config: TagOverlayConfig) TagOverlay.Options {
|
||||||
.square_inactive_background_color = config.square_inactive_background_color,
|
.square_inactive_background_color = config.square_inactive_background_color,
|
||||||
.square_inactive_border_color = config.square_inactive_border_color,
|
.square_inactive_border_color = config.square_inactive_border_color,
|
||||||
.square_inactive_occupied_color = config.square_inactive_occupied_color,
|
.square_inactive_occupied_color = config.square_inactive_occupied_color,
|
||||||
.anchors = .{
|
|
||||||
.top = config.anchor_top,
|
|
||||||
.right = config.anchor_right,
|
|
||||||
.bottom = config.anchor_bottom,
|
|
||||||
.left = config.anchor_left,
|
|
||||||
},
|
|
||||||
.margins = .{
|
|
||||||
.top = config.margin_top,
|
|
||||||
.right = config.margin_right,
|
|
||||||
.bottom = config.margin_bottom,
|
|
||||||
.left = config.margin_left,
|
|
||||||
},
|
|
||||||
.timeout = config.timeout,
|
.timeout = config.timeout,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -89,16 +64,9 @@ pub fn toTagOverlayOptions(config: TagOverlayConfig) TagOverlay.Options {
|
||||||
pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
||||||
config.tag_overlay_config = .{}; // Presence of block = enabled; initialize with defaults
|
config.tag_overlay_config = .{}; // Presence of block = enabled; initialize with defaults
|
||||||
|
|
||||||
const TagOverlayChild = enum { anchors, margins };
|
|
||||||
var next_child_block: ?TagOverlayChild = null;
|
|
||||||
|
|
||||||
while (try parser.next()) |event| {
|
while (try parser.next()) |event| {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.node => |node| {
|
.node => |node| {
|
||||||
if (next_child_block) |child| {
|
|
||||||
log.warn("Expected child block for tag_overlay.{s}, got node instead. Ignoring", .{@tagName(child)});
|
|
||||||
next_child_block = null;
|
|
||||||
}
|
|
||||||
const node_name = std.meta.stringToEnum(NodeName, node.name);
|
const node_name = std.meta.stringToEnum(NodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
if (!helpers.hostMatches(node, parser, hostname)) {
|
if (!helpers.hostMatches(node, parser, hostname)) {
|
||||||
|
|
@ -107,8 +75,6 @@ pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
||||||
}
|
}
|
||||||
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
|
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
|
||||||
switch (name) {
|
switch (name) {
|
||||||
.anchors => next_child_block = .anchors,
|
|
||||||
.margins => next_child_block = .margins,
|
|
||||||
// These are all u8s, so we can inline the branch
|
// These are all u8s, so we can inline the branch
|
||||||
inline .border_width,
|
inline .border_width,
|
||||||
.tag_amount,
|
.tag_amount,
|
||||||
|
|
@ -152,80 +118,6 @@ pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
||||||
helpers.logWarnInvalidNode(node.name);
|
helpers.logWarnInvalidNode(node.name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.child_block_begin => {
|
|
||||||
if (next_child_block) |child| {
|
|
||||||
switch (child) {
|
|
||||||
.anchors => try loadAnchorsBlock(config, parser, hostname),
|
|
||||||
.margins => try loadMarginsBlock(config, parser, hostname),
|
|
||||||
}
|
|
||||||
next_child_block = null;
|
|
||||||
} else {
|
|
||||||
try helpers.skipChildBlock(parser);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.child_block_end => return,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loadAnchorsBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
|
||||||
while (try parser.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.node => |node| {
|
|
||||||
const node_name = std.meta.stringToEnum(AnchorsNodeName, node.name);
|
|
||||||
if (node_name) |name| {
|
|
||||||
if (!helpers.hostMatches(node, parser, hostname)) {
|
|
||||||
logDebugHostMismatch(name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
|
|
||||||
if (helpers.boolFromKdlStr(val_str)) |val| {
|
|
||||||
switch (name) {
|
|
||||||
.top => config.tag_overlay_config.?.anchor_top = val,
|
|
||||||
.right => config.tag_overlay_config.?.anchor_right = val,
|
|
||||||
.bottom => config.tag_overlay_config.?.anchor_bottom = val,
|
|
||||||
.left => config.tag_overlay_config.?.anchor_left = val,
|
|
||||||
}
|
|
||||||
logDebugSettingNode(name, val_str);
|
|
||||||
} else |_| {
|
|
||||||
logWarnInvalidNodeArg(name, val_str);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
helpers.logWarnInvalidNode(node.name);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.child_block_begin => try helpers.skipChildBlock(parser),
|
|
||||||
.child_block_end => return,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loadMarginsBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
|
||||||
while (try parser.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.node => |node| {
|
|
||||||
const node_name = std.meta.stringToEnum(MarginsNodeName, node.name);
|
|
||||||
if (node_name) |name| {
|
|
||||||
if (!helpers.hostMatches(node, parser, hostname)) {
|
|
||||||
logDebugHostMismatch(name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
|
|
||||||
const val = fmt.parseInt(i32, val_str, 10) catch {
|
|
||||||
logWarnInvalidNodeArg(name, val_str);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
switch (name) {
|
|
||||||
.top => config.tag_overlay_config.?.margin_top = val,
|
|
||||||
.right => config.tag_overlay_config.?.margin_right = val,
|
|
||||||
.bottom => config.tag_overlay_config.?.margin_bottom = val,
|
|
||||||
.left => config.tag_overlay_config.?.margin_left = val,
|
|
||||||
}
|
|
||||||
logDebugSettingNode(name, val_str);
|
|
||||||
} else {
|
|
||||||
helpers.logWarnInvalidNode(node.name);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.child_block_begin => try helpers.skipChildBlock(parser),
|
.child_block_begin => try helpers.skipChildBlock(parser),
|
||||||
.child_block_end => return,
|
.child_block_end => return,
|
||||||
}
|
}
|
||||||
|
|
@ -236,8 +128,6 @@ inline fn logDebugSettingNode(node_name: anytype, node_value: []const u8) void {
|
||||||
const node_name_type = @TypeOf(node_name);
|
const node_name_type = @TypeOf(node_name);
|
||||||
switch (node_name_type) {
|
switch (node_name_type) {
|
||||||
NodeName => log.debug("Setting tag_overlay.{s} to {s}", .{ @tagName(node_name), node_value }),
|
NodeName => log.debug("Setting tag_overlay.{s} to {s}", .{ @tagName(node_name), node_value }),
|
||||||
AnchorsNodeName => log.debug("Setting tag_overlay.anchors.{s} to {s}", .{ @tagName(node_name), node_value }),
|
|
||||||
MarginsNodeName => log.debug("Setting tag_overlay.margins.{s} to {s}", .{ @tagName(node_name), node_value }),
|
|
||||||
else => @compileError("This function does not (yet) support type \"" ++ @typeName(@TypeOf(node_name)) ++ "\""),
|
else => @compileError("This function does not (yet) support type \"" ++ @typeName(@TypeOf(node_name)) ++ "\""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -246,8 +136,6 @@ inline fn logDebugHostMismatch(node_name: anytype) void {
|
||||||
const node_name_type = @TypeOf(node_name);
|
const node_name_type = @TypeOf(node_name);
|
||||||
switch (node_name_type) {
|
switch (node_name_type) {
|
||||||
NodeName => log.debug("Skipping \"tag_overlay.{s}\" (host mismatch)", .{@tagName(node_name)}),
|
NodeName => log.debug("Skipping \"tag_overlay.{s}\" (host mismatch)", .{@tagName(node_name)}),
|
||||||
AnchorsNodeName => log.debug("Skipping \"tag_overlay.anchors.{s}\" (host mismatch)", .{@tagName(node_name)}),
|
|
||||||
MarginsNodeName => log.debug("Skipping \"tag_overlay.margins.{s}\" (host mismatch)", .{@tagName(node_name)}),
|
|
||||||
else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""),
|
else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -256,8 +144,6 @@ inline fn logWarnInvalidNodeArg(node_name: anytype, node_value: []const u8) void
|
||||||
const node_name_type = @TypeOf(node_name);
|
const node_name_type = @TypeOf(node_name);
|
||||||
switch (node_name_type) {
|
switch (node_name_type) {
|
||||||
NodeName => log.warn("Invalid \"tag_overlay.{s}\" ({s}). Using default value", .{ @tagName(node_name), node_value }),
|
NodeName => log.warn("Invalid \"tag_overlay.{s}\" ({s}). Using default value", .{ @tagName(node_name), node_value }),
|
||||||
AnchorsNodeName => log.warn("Invalid \"tag_overlay.anchors.{s}\" ({s}). Using default value", .{ @tagName(node_name), node_value }),
|
|
||||||
MarginsNodeName => log.warn("Invalid \"tag_overlay.margins.{s}\" ({s}). Using default value", .{ @tagName(node_name), node_value }),
|
|
||||||
else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""),
|
else => @compileError("This function does not (yet) support type \"" ++ @typeName(node_name_type) ++ "\""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
src/main.zig
75
src/main.zig
|
|
@ -12,8 +12,6 @@ const Globals = struct {
|
||||||
|
|
||||||
wl_compositor: ?*wl.Compositor = null,
|
wl_compositor: ?*wl.Compositor = null,
|
||||||
wl_shm: ?*wl.Shm = null,
|
wl_shm: ?*wl.Shm = null,
|
||||||
|
|
||||||
zwlr_layer_shell_v1: ?*zwlr.LayerShellV1 = null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const usage: []const u8 =
|
const usage: []const u8 =
|
||||||
|
|
@ -68,8 +66,6 @@ pub fn main() !void {
|
||||||
const river_window_manager_v1 = globals.river_window_manager_v1 orelse utils.interfaceNotAdvertised(river.WindowManagerV1);
|
const river_window_manager_v1 = globals.river_window_manager_v1 orelse utils.interfaceNotAdvertised(river.WindowManagerV1);
|
||||||
const river_xkb_bindings_v1 = globals.river_xkb_bindings_v1 orelse utils.interfaceNotAdvertised(river.XkbBindingsV1);
|
const river_xkb_bindings_v1 = globals.river_xkb_bindings_v1 orelse utils.interfaceNotAdvertised(river.XkbBindingsV1);
|
||||||
|
|
||||||
const zwlr_layer_shell_v1 = globals.zwlr_layer_shell_v1 orelse utils.interfaceNotAdvertised(zwlr.LayerShellV1);
|
|
||||||
|
|
||||||
const config = try Config.create();
|
const config = try Config.create();
|
||||||
defer config.destroy();
|
defer config.destroy();
|
||||||
const context = try Context.create(.{
|
const context = try Context.create(.{
|
||||||
|
|
@ -82,7 +78,6 @@ pub fn main() !void {
|
||||||
.river_layer_shell_v1 = river_layer_shell_v1,
|
.river_layer_shell_v1 = river_layer_shell_v1,
|
||||||
.river_window_manager_v1 = river_window_manager_v1,
|
.river_window_manager_v1 = river_window_manager_v1,
|
||||||
.river_xkb_bindings_v1 = river_xkb_bindings_v1,
|
.river_xkb_bindings_v1 = river_xkb_bindings_v1,
|
||||||
.zwlr_layer_shell_v1 = zwlr_layer_shell_v1,
|
|
||||||
.config = config,
|
.config = config,
|
||||||
});
|
});
|
||||||
defer context.destroy();
|
defer context.destroy();
|
||||||
|
|
@ -102,9 +97,8 @@ fn run(wl_display: *wl.Display, context: *Context) !void {
|
||||||
|
|
||||||
const poll_wayland = 0;
|
const poll_wayland = 0;
|
||||||
const poll_sig = 1;
|
const poll_sig = 1;
|
||||||
const poll_tag_overlay_timer = 2;
|
|
||||||
|
|
||||||
var pollfds: [3]posix.pollfd = undefined;
|
var pollfds: [2]posix.pollfd = undefined;
|
||||||
|
|
||||||
pollfds[poll_wayland] = .{
|
pollfds[poll_wayland] = .{
|
||||||
.fd = wl_display.getFd(),
|
.fd = wl_display.getFd(),
|
||||||
|
|
@ -116,12 +110,6 @@ fn run(wl_display: *wl.Display, context: *Context) !void {
|
||||||
.events = posix.POLL.IN,
|
.events = posix.POLL.IN,
|
||||||
.revents = 0,
|
.revents = 0,
|
||||||
};
|
};
|
||||||
pollfds[poll_tag_overlay_timer] = .{
|
|
||||||
// poll ignores negative fds
|
|
||||||
.fd = context.tag_overlay_timer_fd orelse -1,
|
|
||||||
.events = posix.POLL.IN,
|
|
||||||
.revents = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const flush_errno = wl_display.flush();
|
const flush_errno = wl_display.flush();
|
||||||
|
|
@ -136,18 +124,38 @@ fn run(wl_display: *wl.Display, context: *Context) !void {
|
||||||
pollfds[poll_wayland].events = posix.POLL.IN;
|
pollfds[poll_wayland].events = posix.POLL.IN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the number of milliseconds to the top of the next second
|
// Compute poll timeout: minimum of clock update and tag overlay expiry
|
||||||
const time_ns = std.time.nanoTimestamp();
|
var timeout: i32 = blk: {
|
||||||
const ns_per_sec = std.time.ns_per_s;
|
// Milliseconds until the top of the next second (for clock updates)
|
||||||
const remainder_ns = @mod(time_ns, ns_per_sec);
|
const time_ns = time.nanoTimestamp();
|
||||||
const timeout: i32 = @intCast(@divFloor(ns_per_sec - remainder_ns, std.time.ns_per_ms));
|
const remainder_ns = @mod(time_ns, time.ns_per_s);
|
||||||
|
break :blk @intCast(@divFloor(time.ns_per_s - remainder_ns, time.ns_per_ms));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for expired tag overlays and find the soonest expiry
|
||||||
|
const now = time.Instant.now() catch
|
||||||
|
fatal("System does not support a monotonic or steady clock", .{});
|
||||||
|
{
|
||||||
|
var it = context.wm.outputs.iterator(.forward);
|
||||||
|
while (it.next()) |output| {
|
||||||
|
const tag_overlay = output.tag_overlay orelse continue;
|
||||||
|
const last_shown = tag_overlay.last_shown orelse continue;
|
||||||
|
const elapsed_ns = now.since(last_shown);
|
||||||
|
const timeout_ns: u64 = @as(u64, tag_overlay.options.timeout) * time.ns_per_ms;
|
||||||
|
if (elapsed_ns >= timeout_ns) {
|
||||||
|
output.tag_overlay.?.deinitSurfaces();
|
||||||
|
} else {
|
||||||
|
const remaining_ms: i32 = @intCast((timeout_ns - elapsed_ns) / time.ns_per_ms);
|
||||||
|
timeout = @min(timeout, remaining_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const poll_rc = posix.poll(&pollfds, timeout) catch |err| {
|
const poll_rc = posix.poll(&pollfds, timeout) catch |err| {
|
||||||
fatal("Failed to poll {s}", .{@errorName(err)});
|
fatal("Failed to poll {s}", .{@errorName(err)});
|
||||||
};
|
};
|
||||||
if (poll_rc == 0) {
|
if (poll_rc == 0) {
|
||||||
// If poll returns 0, it timed out, meaning we hit the top of the next second
|
// Update the clock
|
||||||
// and need to update the clock.
|
|
||||||
var it = context.wm.outputs.iterator(.forward);
|
var it = context.wm.outputs.iterator(.forward);
|
||||||
while (it.next()) |output| {
|
while (it.next()) |output| {
|
||||||
if (output.bar) |*bar| {
|
if (output.bar) |*bar| {
|
||||||
|
|
@ -168,8 +176,6 @@ fn run(wl_display: *wl.Display, context: *Context) !void {
|
||||||
@branchHint(.cold);
|
@branchHint(.cold);
|
||||||
fatal("Wayland display dispatch failed", .{});
|
fatal("Wayland display dispatch failed", .{});
|
||||||
}
|
}
|
||||||
// Re-sync in case a config reload created or destroyed the timerfd
|
|
||||||
pollfds[poll_tag_overlay_timer].fd = context.tag_overlay_timer_fd orelse -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollfds[poll_sig].revents & posix.POLL.HUP != 0) {
|
if (pollfds[poll_sig].revents & posix.POLL.HUP != 0) {
|
||||||
|
|
@ -181,24 +187,6 @@ fn run(wl_display: *wl.Display, context: *Context) !void {
|
||||||
log.info("Exiting beansprout", .{});
|
log.info("Exiting beansprout", .{});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollfds[poll_tag_overlay_timer].revents & posix.POLL.HUP != 0) {
|
|
||||||
@branchHint(.cold);
|
|
||||||
fatal("Tag overlay timer fd hung up", .{});
|
|
||||||
}
|
|
||||||
if (pollfds[poll_tag_overlay_timer].revents & posix.POLL.IN != 0) {
|
|
||||||
// Read to consume the timer event
|
|
||||||
var buf: [8]u8 = undefined;
|
|
||||||
_ = posix.read(context.tag_overlay_timer_fd.?, &buf) catch {};
|
|
||||||
|
|
||||||
// Hide all tag overlays by destroying their surfaces
|
|
||||||
var it = context.wm.outputs.iterator(.forward);
|
|
||||||
while (it.next()) |output| {
|
|
||||||
if (output.tag_overlay) |*tag_overlay| {
|
|
||||||
tag_overlay.deinitSurfaces();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,11 +269,6 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, globals: *
|
||||||
globals.river_xkb_bindings_v1 = registry.bind(ev.name, river.XkbBindingsV1, 2) catch |e| {
|
globals.river_xkb_bindings_v1 = registry.bind(ev.name, river.XkbBindingsV1, 2) catch |e| {
|
||||||
fatal("Failed to bind to river_xkb_bindings_v1: {any}", .{@errorName(e)});
|
fatal("Failed to bind to river_xkb_bindings_v1: {any}", .{@errorName(e)});
|
||||||
};
|
};
|
||||||
} else if (mem.orderZ(u8, ev.interface, zwlr.LayerShellV1.interface.name) == .eq) {
|
|
||||||
if (ev.version < 3) utils.versionNotSupported(zwlr.LayerShellV1, ev.version, 3);
|
|
||||||
globals.zwlr_layer_shell_v1 = registry.bind(ev.name, zwlr.LayerShellV1, 3) catch |e| {
|
|
||||||
fatal("Failed to bind to zwlr_layer_shell_v1: {any}", .{@errorName(e)});
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.global_remove => |_| {
|
.global_remove => |_| {
|
||||||
|
|
@ -338,11 +321,11 @@ const mem = std.mem;
|
||||||
const os = std.os;
|
const os = std.os;
|
||||||
const posix = std.posix;
|
const posix = std.posix;
|
||||||
const process = std.process;
|
const process = std.process;
|
||||||
|
const time = std.time;
|
||||||
|
|
||||||
const wayland = @import("wayland");
|
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 fcft = @import("fcft");
|
const fcft = @import("fcft");
|
||||||
|
|
||||||
const flags = @import("flags.zig");
|
const flags = @import("flags.zig");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue