Skip to content

Commit

Permalink
river: Allow floating based on window titles
Browse files Browse the repository at this point in the history
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'
  • Loading branch information
ThreeFx authored and ifreund committed Sep 6, 2021
1 parent e59c2a7 commit 546252a
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 38 deletions.
16 changes: 8 additions & 8 deletions doc/riverctl.1.scd
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ over the Wayland protocol.
*exit*
Exit the compositor, terminating the Wayland session.

*float-filter-add* _app-id_
Add _app-id_ to the float filter list. Views with this _app-id_
will start floating. Note that this affects only new views, not already
existing ones.

*float-filter-remove* _app-id_
Remove an _app-id_ from the float filter list. Note that this affects only
new views, not already existing ones.
*float-filter-add* *app-id*|*title* _pattern_
Add a pattern to the float filter list. Note that this affects only new
views, not already existing ones. Title updates are also not taken into
account.

*float-filter-remove* *app-id*|*title* _pattern_
Remove an app-id or title from the float filter list. Note that this
affects only new views, not already existing ones.

*focus-output* *next*|*previous*|*up*|*right*|*down*|*left*
Focus the next or previous output or the closest output in any direction.
Expand Down
32 changes: 28 additions & 4 deletions river/Config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const util = @import("util.zig");
const Server = @import("Server.zig");
const Mode = @import("Mode.zig");
const AttachMode = @import("view_stack.zig").AttachMode;
const View = @import("View.zig");

pub const FocusFollowsCursorMode = enum {
disabled,
Expand Down Expand Up @@ -57,8 +58,9 @@ mode_to_id: std.StringHashMap(usize),
/// All user-defined keymap modes, indexed by mode id
modes: std.ArrayList(Mode),

/// Set of app_ids which will be started floating
float_filter: std.StringHashMapUnmanaged(void) = .{},
/// Sets of app_ids and titles which will be started floating
float_filter_app_ids: std.StringHashMapUnmanaged(void) = .{},
float_filter_titles: std.StringHashMapUnmanaged(void) = .{},

/// Set of app_ids which are allowed to use client side decorations
csd_filter: std.StringHashMapUnmanaged(void) = .{},
Expand Down Expand Up @@ -119,9 +121,15 @@ pub fn deinit(self: *Self) void {
self.modes.deinit();

{
var it = self.float_filter.keyIterator();
var it = self.float_filter_app_ids.keyIterator();
while (it.next()) |key| util.gpa.free(key.*);
self.float_filter.deinit(util.gpa);
self.float_filter_app_ids.deinit(util.gpa);
}

{
var it = self.float_filter_titles.keyIterator();
while (it.next()) |key| util.gpa.free(key.*);
self.float_filter_titles.deinit(util.gpa);
}

{
Expand All @@ -132,3 +140,19 @@ pub fn deinit(self: *Self) void {

util.gpa.free(self.default_layout_namespace);
}

pub fn shouldFloat(self: Self, view: *View) bool {
if (view.getAppId()) |app_id| {
if (self.float_filter_app_ids.contains(std.mem.span(app_id))) {
return true;
}
}

if (view.getTitle()) |title| {
if (self.float_filter_titles.contains(std.mem.span(title))) {
return true;
}
}

return false;
}
13 changes: 4 additions & 9 deletions river/XdgToplevel.zig
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,10 @@ fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurfa
view.current.float = true;
view.pending.float = true;
view.pending.box = view.float_box;
} else {
// Make views with app_ids listed in the float filter float
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;
}
}
} else if (server.config.shouldFloat(view)) {
view.current.float = true;
view.pending.float = true;
view.pending.box = view.float_box;
}

// If the toplevel has an app_id which is not configured to use client side
Expand Down
13 changes: 4 additions & 9 deletions river/XwaylandView.zig
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,10 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl
view.current.float = true;
view.pending.float = true;
view.pending.box = view.float_box;
} else {
// Make views with app_ids listed in the float filter float
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;
}
}
} else if (server.config.shouldFloat(view)) {
view.current.float = true;
view.pending.float = true;
view.pending.box = view.float_box;
}

view.map() catch {
Expand Down
35 changes: 27 additions & 8 deletions river/command/filter.zig
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,31 @@ 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 < 2) return Error.NotEnoughArguments;
if (args.len > 2) return Error.TooManyArguments;
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 gop = try server.config.float_filter.getOrPut(util.gpa, args[1]);
const key = args[2];
const gop = try map.getOrPut(util.gpa, key);
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]);
errdefer assert(map.remove(args[1]));
gop.key_ptr.* = try std.mem.dupe(util.gpa, u8, key);
}

pub fn floatFilterRemove(
Expand All @@ -48,10 +60,17 @@ pub fn floatFilterRemove(
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 (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,
};

if (server.config.float_filter.fetchRemove(args[1])) |kv| util.gpa.free(kv.key);
const key = args[2];
if (map.fetchRemove(key)) |kv| util.gpa.free(kv.key);
}

pub fn csdFilterAdd(
Expand Down

0 comments on commit 546252a

Please sign in to comment.