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_ *csd-filter-add* _app-id_
Add _app-id_ to the CSD filter list. Views with this _app-id_ are 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 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. existing ones.
*csd-filter-remove* _app-id_ *csd-filter-remove* _app-id_
Remove an _app-id_ from the CSD filter list. Note that this affects only new Remove an _app-id_ from the CSD filter list. Note that this affects both new
views, not already existing ones. views, as well as already existing ones.
*exit* *exit*
Exit the compositor, terminating the Wayland session. Exit the compositor, terminating the Wayland session.

View file

@ -49,7 +49,10 @@ fn handleDestroy(
const self = @fieldParentPtr(Self, "destroy", listener); const self = @fieldParentPtr(Self, "destroy", listener);
self.destroy.link.remove(); self.destroy.link.remove();
self.request_mode.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( fn handleRequestMode(

View file

@ -27,6 +27,10 @@ const util = @import("util.zig");
const Decoration = @import("Decoration.zig"); const Decoration = @import("Decoration.zig");
const Server = @import("Server.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, xdg_decoration_manager: *wlr.XdgDecorationManagerV1,
new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) = new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) =
@ -45,9 +49,10 @@ fn handleNewToplevelDecoration(
xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1, xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1,
) void { ) void {
const self = @fieldParentPtr(Self, "new_toplevel_decoration", listener); 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(); xdg_toplevel_decoration.resource.postNoMemory();
return; 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 server = &@import("../main.zig").server;
const util = @import("../util.zig"); const util = @import("../util.zig");
const View = @import("View.zig");
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");
@ -48,6 +50,7 @@ pub fn csdFilterAdd(
out: *?[]const u8, out: *?[]const u8,
) Error!void { ) Error!void {
try modifyFilter(allocator, &server.config.csd_filter, args, .add); try modifyFilter(allocator, &server.config.csd_filter, args, .add);
csdFilterUpdateViews(args[1], .add);
} }
pub fn csdFilterRemove( pub fn csdFilterRemove(
@ -57,6 +60,7 @@ pub fn csdFilterRemove(
out: *?[]const u8, out: *?[]const u8,
) Error!void { ) Error!void {
try modifyFilter(allocator, &server.config.csd_filter, args, .remove); try modifyFilter(allocator, &server.config.csd_filter, args, .remove);
csdFilterUpdateViews(args[1], .remove);
} }
fn modifyFilter( fn modifyFilter(
@ -80,3 +84,47 @@ fn modifyFilter(
list.appendAssumeCapacity(try std.mem.dupe(allocator, u8, args[1])); 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 });
},
}
}
}
}
}