From 7b18b4944e8534733a5d65c343976064532333b4 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 12 Jul 2021 19:37:56 +0200 Subject: [PATCH] config: use hash sets for filters, clean up code --- river/Config.zig | 33 +++++++----- river/Decoration.zig | 13 +++-- river/XdgToplevel.zig | 14 ++--- river/XwaylandView.zig | 7 +-- river/command/filter.zig | 108 ++++++++++++++++----------------------- 5 files changed, 77 insertions(+), 98 deletions(-) diff --git a/river/Config.zig b/river/Config.zig index 606dc6b..dbc7c39 100644 --- a/river/Config.zig +++ b/river/Config.zig @@ -56,11 +56,11 @@ mode_to_id: std.StringHashMap(usize), /// All user-defined keymap modes, indexed by mode id modes: std.ArrayList(Mode), -/// List of app_ids which will be started floating -float_filter: std.ArrayList([]const u8), +/// Set of app_ids which will be started floating +float_filter: std.StringHashMapUnmanaged(void) = .{}, -/// List of app_ids which are allowed to use client side decorations -csd_filter: std.ArrayList([]const u8), +/// Set of app_ids which are allowed to use client side decorations +csd_filter: std.StringHashMapUnmanaged(void) = .{}, /// The selected focus_follows_cursor mode focus_follows_cursor: FocusFollowsCursorMode = .disabled, @@ -99,10 +99,7 @@ pub fn init() !Self { var self = Self{ .mode_to_id = std.StringHashMap(usize).init(util.gpa), .modes = std.ArrayList(Mode).init(util.gpa), - .float_filter = std.ArrayList([]const u8).init(util.gpa), - .csd_filter = std.ArrayList([]const u8).init(util.gpa), }; - errdefer self.deinit(); // Start with two empty modes, "normal" and "locked" @@ -124,18 +121,26 @@ pub fn init() !Self { } pub fn deinit(self: *Self) void { - var it = self.mode_to_id.keyIterator(); - while (it.next()) |key| util.gpa.free(key.*); - self.mode_to_id.deinit(); + { + var it = self.mode_to_id.keyIterator(); + while (it.next()) |key| util.gpa.free(key.*); + self.mode_to_id.deinit(); + } for (self.modes.items) |mode| mode.deinit(); self.modes.deinit(); - for (self.float_filter.items) |s| util.gpa.free(s); - self.float_filter.deinit(); + { + var it = self.float_filter.keyIterator(); + while (it.next()) |key| util.gpa.free(key.*); + self.float_filter.deinit(util.gpa); + } - for (self.csd_filter.items) |s| util.gpa.free(s); - self.csd_filter.deinit(); + { + var it = self.csd_filter.keyIterator(); + while (it.next()) |key| util.gpa.free(key.*); + self.csd_filter.deinit(util.gpa); + } util.gpa.free(self.default_layout_namespace); } diff --git a/river/Decoration.zig b/river/Decoration.zig index 2af5405..4159bc6 100644 --- a/river/Decoration.zig +++ b/river/Decoration.zig @@ -18,6 +18,7 @@ const Self = @This(); const std = @import("std"); +const mem = std.mem; const wlr = @import("wlroots"); const wl = @import("wayland").server.wl; @@ -62,11 +63,9 @@ fn handleRequestMode( const self = @fieldParentPtr(Self, "request_mode", listener); const toplevel = self.xdg_toplevel_decoration.surface.role_data.toplevel; - const app_id: [*:0]const u8 = if (toplevel.app_id) |id| id else "NULL"; - - _ = self.xdg_toplevel_decoration.setMode( - for (server.config.csd_filter.items) |filter_app_id| { - if (std.mem.eql(u8, std.mem.span(app_id), filter_app_id)) break .client_side; - } else .server_side, - ); + if (toplevel.app_id != null and server.config.csd_filter.contains(mem.span(toplevel.app_id.?))) { + _ = self.xdg_toplevel_decoration.setMode(.client_side); + } else { + _ = self.xdg_toplevel_decoration.setMode(.server_side); + } } diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig index 0b9dcaa..4bd8cc3 100644 --- a/river/XdgToplevel.zig +++ b/river/XdgToplevel.zig @@ -18,6 +18,7 @@ const Self = @This(); const std = @import("std"); +const mem = std.mem; const wlr = @import("wlroots"); const wl = @import("wayland").server.wl; @@ -208,7 +209,6 @@ fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurfa const state = &toplevel.current; const has_fixed_size = state.min_width != 0 and state.min_height != 0 and (state.min_width == state.max_width or state.min_height == state.max_height); - const app_id: [*:0]const u8 = if (toplevel.app_id) |id| id else "NULL"; if (toplevel.parent != null or has_fixed_size) { // If the toplevel has a parent or has a fixed size make it float @@ -217,23 +217,19 @@ fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurfa view.pending.box = view.float_box; } else { // Make views with app_ids listed in the float filter float - for (server.config.float_filter.items) |filter_app_id| { - if (std.mem.eql(u8, std.mem.span(app_id), std.mem.span(filter_app_id))) { + if (toplevel.app_id) |app_id| { + if (server.config.float_filter.contains(mem.span(app_id))) { view.current.float = true; view.pending.float = true; view.pending.box = view.float_box; - break; } } } // If the toplevel has an app_id which is not configured to use client side // decorations, inform it that it is tiled. - for (server.config.csd_filter.items) |filter_app_id| { - if (std.mem.eql(u8, std.mem.span(app_id), filter_app_id)) { - view.draw_borders = false; - break; - } + if (toplevel.app_id != null and server.config.csd_filter.contains(mem.span(toplevel.app_id.?))) { + view.draw_borders = false; } else { _ = toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true }); } diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig index ecd1747..089aafc 100644 --- a/river/XwaylandView.zig +++ b/river/XwaylandView.zig @@ -191,8 +191,6 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl else false; - const app_id: [*:0]const u8 = if (self.xwayland_surface.class) |id| id else "NULL"; - if (self.xwayland_surface.parent != null or has_fixed_size) { // If the toplevel has a parent or has a fixed size make it float view.current.float = true; @@ -200,12 +198,11 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl view.pending.box = view.float_box; } else { // Make views with app_ids listed in the float filter float - for (server.config.float_filter.items) |filter_app_id| { - if (std.mem.eql(u8, std.mem.span(app_id), std.mem.span(filter_app_id))) { + if (self.xwayland_surface.class) |app_id| { + if (server.config.float_filter.contains(std.mem.span(app_id))) { view.current.float = true; view.pending.float = true; view.pending.box = view.float_box; - break; } } } diff --git a/river/command/filter.zig b/river/command/filter.zig index 2818c15..911eb09 100644 --- a/river/command/filter.zig +++ b/river/command/filter.zig @@ -16,114 +16,96 @@ // along with this program. If not, see . const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; const server = &@import("../main.zig").server; const util = @import("../util.zig"); -const View = @import("View.zig"); +const View = @import("../View.zig"); const ViewStack = @import("view_stack.zig").ViewStack; const Error = @import("../command.zig").Error; const Seat = @import("../Seat.zig"); pub fn floatFilterAdd( - allocator: *std.mem.Allocator, + allocator: *mem.Allocator, seat: *Seat, args: []const []const u8, out: *?[]const u8, ) Error!void { - try modifyFilter(allocator, &server.config.float_filter, args, .add); + if (args.len < 2) return Error.NotEnoughArguments; + if (args.len > 2) return Error.TooManyArguments; + + const gop = try server.config.float_filter.getOrPut(util.gpa, args[1]); + if (gop.found_existing) return; + errdefer assert(server.config.float_filter.remove(args[1])); + gop.key_ptr.* = try std.mem.dupe(util.gpa, u8, args[1]); } pub fn floatFilterRemove( - allocator: *std.mem.Allocator, + allocator: *mem.Allocator, seat: *Seat, args: []const []const u8, out: *?[]const u8, ) Error!void { - try modifyFilter(allocator, &server.config.float_filter, args, .remove); + if (args.len < 2) return Error.NotEnoughArguments; + if (args.len > 2) return Error.TooManyArguments; + + if (server.config.float_filter.fetchRemove(args[1])) |kv| util.gpa.free(kv.key); } pub fn csdFilterAdd( - allocator: *std.mem.Allocator, + allocator: *mem.Allocator, seat: *Seat, args: []const []const u8, out: *?[]const u8, ) Error!void { - try modifyFilter(allocator, &server.config.csd_filter, args, .add); + if (args.len < 2) return Error.NotEnoughArguments; + if (args.len > 2) return Error.TooManyArguments; + + const gop = try server.config.csd_filter.getOrPut(util.gpa, args[1]); + if (gop.found_existing) return; + errdefer assert(server.config.csd_filter.remove(args[1])); + gop.key_ptr.* = try std.mem.dupe(util.gpa, u8, args[1]); + csdFilterUpdateViews(args[1], .add); } pub fn csdFilterRemove( - allocator: *std.mem.Allocator, + allocator: *mem.Allocator, seat: *Seat, args: []const []const u8, out: *?[]const u8, -) Error!void { - try modifyFilter(allocator, &server.config.csd_filter, args, .remove); - csdFilterUpdateViews(args[1], .remove); -} - -fn modifyFilter( - allocator: *std.mem.Allocator, - list: *std.ArrayList([]const u8), - args: []const []const u8, - operation: enum { add, remove }, ) Error!void { if (args.len < 2) return Error.NotEnoughArguments; if (args.len > 2) return Error.TooManyArguments; - for (list.items) |*filter, i| { - if (std.mem.eql(u8, filter.*, args[1])) { - if (operation == .remove) { - allocator.free(list.orderedRemove(i)); - } - return; - } - } - if (operation == .add) { - try list.ensureUnusedCapacity(1); - list.appendAssumeCapacity(try std.mem.dupe(allocator, u8, args[1])); + + if (server.config.csd_filter.fetchRemove(args[1])) |kv| { + util.gpa.free(kv.key); + csdFilterUpdateViews(args[1], .remove); } } fn csdFilterUpdateViews(app_id: []const u8, operation: enum { add, remove }) void { - // There is no link between Decoration and View, so we need to iterate over - // both separately. Note that we do not need to arrange the outputs here; If - // the clients decoration mode changes, it will receive a configure event. var decoration_it = server.decoration_manager.decorations.first; while (decoration_it) |decoration_node| : (decoration_it = decoration_node.next) { const xdg_toplevel_decoration = decoration_node.data.xdg_toplevel_decoration; - if (std.mem.eql( - u8, - std.mem.span(xdg_toplevel_decoration.surface.role_data.toplevel.app_id orelse return), - app_id, - )) { - _ = xdg_toplevel_decoration.setMode(switch (operation) { - .add => .client_side, - .remove => .server_side, - }); - } - } + const view = @intToPtr(*View, xdg_toplevel_decoration.surface.data); + const view_app_id = mem.span(view.getAppId()) orelse continue; - var output_it = server.root.outputs.first; - while (output_it) |output_node| : (output_it = output_node.next) { - var view_it = output_node.data.views.first; - while (view_it) |view_node| : (view_it = view_node.next) { - // CSD mode is not supported for XWayland views. - if (view_node.view.impl == .xwayland_view) continue; - - const view_app_id = std.mem.span(view_node.view.getAppId() orelse continue); - if (std.mem.eql(u8, app_id, view_app_id)) { - const toplevel = view_node.view.impl.xdg_toplevel.xdg_surface.role_data.toplevel; - switch (operation) { - .add => { - view_node.view.draw_borders = false; - _ = toplevel.setTiled(.{ .top = false, .bottom = false, .left = false, .right = false }); - }, - .remove => { - view_node.view.draw_borders = true; - _ = toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true }); - }, - } + if (mem.eql(u8, app_id, view_app_id)) { + const toplevel = view.impl.xdg_toplevel.xdg_surface.role_data.toplevel; + switch (operation) { + .add => { + _ = xdg_toplevel_decoration.setMode(.client_side); + view.draw_borders = false; + _ = toplevel.setTiled(.{ .top = false, .bottom = false, .left = false, .right = false }); + }, + .remove => { + _ = xdg_toplevel_decoration.setMode(.server_side); + view.draw_borders = true; + _ = toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true }); + }, } } }