config: use hash sets for filters, clean up code

This commit is contained in:
Isaac Freund 2021-07-12 19:37:56 +02:00
parent 968aef3459
commit 7b18b4944e
5 changed files with 77 additions and 98 deletions

View file

@ -56,11 +56,11 @@ mode_to_id: std.StringHashMap(usize),
/// All user-defined keymap modes, indexed by mode id /// All user-defined keymap modes, indexed by mode id
modes: std.ArrayList(Mode), modes: std.ArrayList(Mode),
/// List of app_ids which will be started floating /// Set of app_ids which will be started floating
float_filter: std.ArrayList([]const u8), float_filter: std.StringHashMapUnmanaged(void) = .{},
/// List of app_ids which are allowed to use client side decorations /// Set of app_ids which are allowed to use client side decorations
csd_filter: std.ArrayList([]const u8), csd_filter: std.StringHashMapUnmanaged(void) = .{},
/// The selected focus_follows_cursor mode /// The selected focus_follows_cursor mode
focus_follows_cursor: FocusFollowsCursorMode = .disabled, focus_follows_cursor: FocusFollowsCursorMode = .disabled,
@ -99,10 +99,7 @@ pub fn init() !Self {
var self = Self{ var self = Self{
.mode_to_id = std.StringHashMap(usize).init(util.gpa), .mode_to_id = std.StringHashMap(usize).init(util.gpa),
.modes = std.ArrayList(Mode).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(); errdefer self.deinit();
// Start with two empty modes, "normal" and "locked" // Start with two empty modes, "normal" and "locked"
@ -124,18 +121,26 @@ pub fn init() !Self {
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
{
var it = self.mode_to_id.keyIterator(); var it = self.mode_to_id.keyIterator();
while (it.next()) |key| util.gpa.free(key.*); while (it.next()) |key| util.gpa.free(key.*);
self.mode_to_id.deinit(); self.mode_to_id.deinit();
}
for (self.modes.items) |mode| mode.deinit(); for (self.modes.items) |mode| mode.deinit();
self.modes.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); util.gpa.free(self.default_layout_namespace);
} }

View file

@ -18,6 +18,7 @@
const Self = @This(); const Self = @This();
const std = @import("std"); const std = @import("std");
const mem = std.mem;
const wlr = @import("wlroots"); const wlr = @import("wlroots");
const wl = @import("wayland").server.wl; const wl = @import("wayland").server.wl;
@ -62,11 +63,9 @@ fn handleRequestMode(
const self = @fieldParentPtr(Self, "request_mode", listener); const self = @fieldParentPtr(Self, "request_mode", listener);
const toplevel = self.xdg_toplevel_decoration.surface.role_data.toplevel; const toplevel = self.xdg_toplevel_decoration.surface.role_data.toplevel;
const app_id: [*:0]const u8 = if (toplevel.app_id) |id| id else "NULL"; if (toplevel.app_id != null and server.config.csd_filter.contains(mem.span(toplevel.app_id.?))) {
_ = self.xdg_toplevel_decoration.setMode(.client_side);
_ = self.xdg_toplevel_decoration.setMode( } else {
for (server.config.csd_filter.items) |filter_app_id| { _ = self.xdg_toplevel_decoration.setMode(.server_side);
if (std.mem.eql(u8, std.mem.span(app_id), filter_app_id)) break .client_side; }
} else .server_side,
);
} }

View file

@ -18,6 +18,7 @@
const Self = @This(); const Self = @This();
const std = @import("std"); const std = @import("std");
const mem = std.mem;
const wlr = @import("wlroots"); const wlr = @import("wlroots");
const wl = @import("wayland").server.wl; 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 state = &toplevel.current;
const has_fixed_size = state.min_width != 0 and state.min_height != 0 and 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); (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 (toplevel.parent != null or has_fixed_size) {
// If the toplevel has a parent or has a fixed size make it float // 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; view.pending.box = view.float_box;
} else { } else {
// Make views with app_ids listed in the float filter float // Make views with app_ids listed in the float filter float
for (server.config.float_filter.items) |filter_app_id| { if (toplevel.app_id) |app_id| {
if (std.mem.eql(u8, std.mem.span(app_id), std.mem.span(filter_app_id))) { if (server.config.float_filter.contains(mem.span(app_id))) {
view.current.float = true; view.current.float = true;
view.pending.float = true; view.pending.float = true;
view.pending.box = view.float_box; view.pending.box = view.float_box;
break;
} }
} }
} }
// If the toplevel has an app_id which is not configured to use client side // If the toplevel has an app_id which is not configured to use client side
// decorations, inform it that it is tiled. // decorations, inform it that it is tiled.
for (server.config.csd_filter.items) |filter_app_id| { if (toplevel.app_id != null and server.config.csd_filter.contains(mem.span(toplevel.app_id.?))) {
if (std.mem.eql(u8, std.mem.span(app_id), filter_app_id)) {
view.draw_borders = false; view.draw_borders = false;
break;
}
} else { } else {
_ = toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true }); _ = toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true });
} }

View file

@ -191,8 +191,6 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl
else else
false; 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 (self.xwayland_surface.parent != null or has_fixed_size) {
// If the toplevel has a parent or has a fixed size make it float // If the toplevel has a parent or has a fixed size make it float
view.current.float = true; view.current.float = true;
@ -200,12 +198,11 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl
view.pending.box = view.float_box; view.pending.box = view.float_box;
} else { } else {
// Make views with app_ids listed in the float filter float // Make views with app_ids listed in the float filter float
for (server.config.float_filter.items) |filter_app_id| { if (self.xwayland_surface.class) |app_id| {
if (std.mem.eql(u8, std.mem.span(app_id), std.mem.span(filter_app_id))) { if (server.config.float_filter.contains(std.mem.span(app_id))) {
view.current.float = true; view.current.float = true;
view.pending.float = true; view.pending.float = true;
view.pending.box = view.float_box; view.pending.box = view.float_box;
break;
} }
} }
} }

View file

@ -16,115 +16,97 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert;
const mem = std.mem;
const server = &@import("../main.zig").server; const server = &@import("../main.zig").server;
const util = @import("../util.zig"); const util = @import("../util.zig");
const View = @import("View.zig"); const View = @import("../View.zig");
const ViewStack = @import("view_stack.zig").ViewStack; const ViewStack = @import("view_stack.zig").ViewStack;
const Error = @import("../command.zig").Error; const Error = @import("../command.zig").Error;
const Seat = @import("../Seat.zig"); const Seat = @import("../Seat.zig");
pub fn floatFilterAdd( pub fn floatFilterAdd(
allocator: *std.mem.Allocator, allocator: *mem.Allocator,
seat: *Seat, seat: *Seat,
args: []const []const u8, args: []const []const u8,
out: *?[]const u8, out: *?[]const u8,
) Error!void { ) 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( pub fn floatFilterRemove(
allocator: *std.mem.Allocator, allocator: *mem.Allocator,
seat: *Seat, seat: *Seat,
args: []const []const u8, args: []const []const u8,
out: *?[]const u8, out: *?[]const u8,
) Error!void { ) 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( pub fn csdFilterAdd(
allocator: *std.mem.Allocator, allocator: *mem.Allocator,
seat: *Seat, seat: *Seat,
args: []const []const u8, args: []const []const u8,
out: *?[]const u8, out: *?[]const u8,
) Error!void { ) 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); csdFilterUpdateViews(args[1], .add);
} }
pub fn csdFilterRemove( pub fn csdFilterRemove(
allocator: *std.mem.Allocator, allocator: *mem.Allocator,
seat: *Seat, seat: *Seat,
args: []const []const u8, args: []const []const u8,
out: *?[]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 { ) Error!void {
if (args.len < 2) return Error.NotEnoughArguments; if (args.len < 2) return Error.NotEnoughArguments;
if (args.len > 2) return Error.TooManyArguments; if (args.len > 2) return Error.TooManyArguments;
for (list.items) |*filter, i| {
if (std.mem.eql(u8, filter.*, args[1])) { if (server.config.csd_filter.fetchRemove(args[1])) |kv| {
if (operation == .remove) { util.gpa.free(kv.key);
allocator.free(list.orderedRemove(i)); csdFilterUpdateViews(args[1], .remove);
}
return;
}
}
if (operation == .add) {
try list.ensureUnusedCapacity(1);
list.appendAssumeCapacity(try std.mem.dupe(allocator, u8, args[1]));
} }
} }
fn csdFilterUpdateViews(app_id: []const u8, operation: enum { add, remove }) void { 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; var decoration_it = server.decoration_manager.decorations.first;
while (decoration_it) |decoration_node| : (decoration_it = decoration_node.next) { while (decoration_it) |decoration_node| : (decoration_it = decoration_node.next) {
const xdg_toplevel_decoration = decoration_node.data.xdg_toplevel_decoration; const xdg_toplevel_decoration = decoration_node.data.xdg_toplevel_decoration;
if (std.mem.eql( const view = @intToPtr(*View, xdg_toplevel_decoration.surface.data);
u8, const view_app_id = mem.span(view.getAppId()) orelse continue;
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,
});
}
}
var output_it = server.root.outputs.first; if (mem.eql(u8, app_id, view_app_id)) {
while (output_it) |output_node| : (output_it = output_node.next) { const toplevel = view.impl.xdg_toplevel.xdg_surface.role_data.toplevel;
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) { switch (operation) {
.add => { .add => {
view_node.view.draw_borders = false; _ = xdg_toplevel_decoration.setMode(.client_side);
view.draw_borders = false;
_ = toplevel.setTiled(.{ .top = false, .bottom = false, .left = false, .right = false }); _ = toplevel.setTiled(.{ .top = false, .bottom = false, .left = false, .right = false });
}, },
.remove => { .remove => {
view_node.view.draw_borders = true; _ = xdg_toplevel_decoration.setMode(.server_side);
view.draw_borders = true;
_ = toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true }); _ = toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true });
}, },
} }
} }
} }
} }
}