This extends the `float-filter-add` command to allow matching on window titles as well, using a `float-filter-add kind pattern` syntax. The following kinds are supported: * `title`, which matches window titles * `app-id`, which matches app ids Only exact matches are considered. As an example following configuration floats all windows with the title 'asdf with spaces'. riverctl float-filter-add title 'asdf with spaces'
132 lines
4.5 KiB
Zig
132 lines
4.5 KiB
Zig
// This file is part of river, a dynamic tiling wayland compositor.
|
|
//
|
|
// Copyright 2020 The River Developers
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
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 ViewStack = @import("view_stack.zig").ViewStack;
|
|
const Error = @import("../command.zig").Error;
|
|
const Seat = @import("../Seat.zig");
|
|
|
|
const FilterKind = enum {
|
|
@"app-id",
|
|
title,
|
|
};
|
|
|
|
pub fn floatFilterAdd(
|
|
allocator: *mem.Allocator,
|
|
seat: *Seat,
|
|
args: []const [:0]const u8,
|
|
out: *?[]const u8,
|
|
) Error!void {
|
|
if (args.len < 3) return Error.NotEnoughArguments;
|
|
if (args.len > 3) return Error.TooManyArguments;
|
|
|
|
const kind = std.meta.stringToEnum(FilterKind, args[1]) orelse return Error.UnknownOption;
|
|
const map = switch (kind) {
|
|
.@"app-id" => &server.config.float_filter_app_ids,
|
|
.title => &server.config.float_filter_titles,
|
|
};
|
|
|
|
const key = args[2];
|
|
const gop = try map.getOrPut(util.gpa, key);
|
|
if (gop.found_existing) return;
|
|
errdefer assert(map.remove(args[1]));
|
|
gop.key_ptr.* = try std.mem.dupe(util.gpa, u8, key);
|
|
}
|
|
|
|
pub fn floatFilterRemove(
|
|
allocator: *mem.Allocator,
|
|
seat: *Seat,
|
|
args: []const [:0]const u8,
|
|
out: *?[]const u8,
|
|
) Error!void {
|
|
if (args.len < 3) return Error.NotEnoughArguments;
|
|
if (args.len > 3) return Error.TooManyArguments;
|
|
|
|
const kind = std.meta.stringToEnum(FilterKind, args[1]) orelse return Error.UnknownOption;
|
|
const map = switch (kind) {
|
|
.@"app-id" => &server.config.float_filter_app_ids,
|
|
.title => &server.config.float_filter_titles,
|
|
};
|
|
|
|
const key = args[2];
|
|
if (map.fetchRemove(key)) |kv| util.gpa.free(kv.key);
|
|
}
|
|
|
|
pub fn csdFilterAdd(
|
|
allocator: *mem.Allocator,
|
|
seat: *Seat,
|
|
args: []const [:0]const u8,
|
|
out: *?[]const u8,
|
|
) Error!void {
|
|
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: *mem.Allocator,
|
|
seat: *Seat,
|
|
args: []const [:0]const u8,
|
|
out: *?[]const u8,
|
|
) Error!void {
|
|
if (args.len < 2) return Error.NotEnoughArguments;
|
|
if (args.len > 2) return Error.TooManyArguments;
|
|
|
|
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 {
|
|
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;
|
|
const view = @intToPtr(*View, xdg_toplevel_decoration.surface.data);
|
|
const view_app_id = mem.span(view.getAppId()) orelse continue;
|
|
|
|
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 });
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|