river: make CSD-filters apply to existing views

This commit is contained in:
Leon Henrik Plickat 2021-06-22 13:13:08 +02:00 committed by Isaac Freund
parent 9ec04c764e
commit 968aef3459
4 changed files with 62 additions and 6 deletions

View file

@ -23,12 +23,12 @@ over the Wayland protocol.
*csd-filter-add* _app-id_
Add _app-id_ to the CSD filter list. Views with this _app-id_ are
told to use client side decoration instead of the default server
side decoration. Note that this affects only new views, not already
side decoration. Note that this affects both new views, as well as already
existing ones.
*csd-filter-remove* _app-id_
Remove an _app-id_ from the CSD filter list. Note that this affects only new
views, not already existing ones.
Remove an _app-id_ from the CSD filter list. Note that this affects both new
views, as well as already existing ones.
*exit*
Exit the compositor, terminating the Wayland session.

View file

@ -49,7 +49,10 @@ fn handleDestroy(
const self = @fieldParentPtr(Self, "destroy", listener);
self.destroy.link.remove();
self.request_mode.link.remove();
util.gpa.destroy(self);
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
server.decoration_manager.decorations.remove(node);
util.gpa.destroy(node);
}
fn handleRequestMode(

View file

@ -27,6 +27,10 @@ const util = @import("util.zig");
const Decoration = @import("Decoration.zig");
const Server = @import("Server.zig");
/// List of all Decoration objects. This will clean itself up on exit through
/// the wlr.XdgToplevelDecorationV1.events.destroy event.
decorations: std.TailQueue(Decoration) = .{},
xdg_decoration_manager: *wlr.XdgDecorationManagerV1,
new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) =
@ -45,9 +49,10 @@ fn handleNewToplevelDecoration(
xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1,
) void {
const self = @fieldParentPtr(Self, "new_toplevel_decoration", listener);
const decoration = util.gpa.create(Decoration) catch {
const decoration_node = util.gpa.create(std.TailQueue(Decoration).Node) catch {
xdg_toplevel_decoration.resource.postNoMemory();
return;
};
decoration.init(xdg_toplevel_decoration);
decoration_node.data.init(xdg_toplevel_decoration);
self.decorations.append(decoration_node);
}

View file

@ -20,6 +20,8 @@ const std = @import("std");
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");
@ -48,6 +50,7 @@ pub fn csdFilterAdd(
out: *?[]const u8,
) Error!void {
try modifyFilter(allocator, &server.config.csd_filter, args, .add);
csdFilterUpdateViews(args[1], .add);
}
pub fn csdFilterRemove(
@ -57,6 +60,7 @@ pub fn csdFilterRemove(
out: *?[]const u8,
) Error!void {
try modifyFilter(allocator, &server.config.csd_filter, args, .remove);
csdFilterUpdateViews(args[1], .remove);
}
fn modifyFilter(
@ -80,3 +84,47 @@ fn modifyFilter(
list.appendAssumeCapacity(try std.mem.dupe(allocator, u8, args[1]));
}
}
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,
});
}
}
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 });
},
}
}
}
}
}