tearing-control-v1: implement
Implement the wp-tearing-control-v1 protocol allowing window to hint the compositor that they prefer async "tearing" page flips. Add tearing/no-tearing rules to allow the user to manually enabled/disable tearing for a window. Use async "tearing" page flips when a window that should be allowed to tear is fullscreen. This still requires several kernel patches to work with the wlroots atomic DRM backend. For now, either set WLR_DRM_NO_ATOMIC=1 or use a custom kernel that includes the unmerged patches (such as CachyOS). Closes: https://codeberg.org/river/river/issues/1094
This commit is contained in:
		
				
					committed by
					
						
						Isaac Freund
					
				
			
			
				
	
			
			
			
						parent
						
							db7de8151c
						
					
				
				
					commit
					066baa5753
				
			@ -94,6 +94,7 @@ pub fn build(b: *Build) !void {
 | 
				
			|||||||
    scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
 | 
					    scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
 | 
				
			||||||
    scanner.addSystemProtocol("staging/cursor-shape/cursor-shape-v1.xml");
 | 
					    scanner.addSystemProtocol("staging/cursor-shape/cursor-shape-v1.xml");
 | 
				
			||||||
    scanner.addSystemProtocol("staging/ext-session-lock/ext-session-lock-v1.xml");
 | 
					    scanner.addSystemProtocol("staging/ext-session-lock/ext-session-lock-v1.xml");
 | 
				
			||||||
 | 
					    scanner.addSystemProtocol("staging/tearing-control/tearing-control-v1.xml");
 | 
				
			||||||
    scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
 | 
					    scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
 | 
				
			||||||
    scanner.addSystemProtocol("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml");
 | 
					    scanner.addSystemProtocol("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml");
 | 
				
			||||||
    scanner.addSystemProtocol("unstable/tablet/tablet-unstable-v2.xml");
 | 
					    scanner.addSystemProtocol("unstable/tablet/tablet-unstable-v2.xml");
 | 
				
			||||||
@ -124,6 +125,7 @@ pub fn build(b: *Build) !void {
 | 
				
			|||||||
    scanner.generate("zxdg_decoration_manager_v1", 1);
 | 
					    scanner.generate("zxdg_decoration_manager_v1", 1);
 | 
				
			||||||
    scanner.generate("ext_session_lock_manager_v1", 1);
 | 
					    scanner.generate("ext_session_lock_manager_v1", 1);
 | 
				
			||||||
    scanner.generate("wp_cursor_shape_manager_v1", 1);
 | 
					    scanner.generate("wp_cursor_shape_manager_v1", 1);
 | 
				
			||||||
 | 
					    scanner.generate("wp_tearing_control_manager_v1", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    scanner.generate("zriver_control_v1", 1);
 | 
					    scanner.generate("zriver_control_v1", 1);
 | 
				
			||||||
    scanner.generate("zriver_status_manager_v1", 4);
 | 
					    scanner.generate("zriver_status_manager_v1", 4);
 | 
				
			||||||
 | 
				
			|||||||
@ -12,8 +12,8 @@
 | 
				
			|||||||
            .hash = "1220687c8c47a48ba285d26a05600f8700d37fc637e223ced3aa8324f3650bf52242",
 | 
					            .hash = "1220687c8c47a48ba285d26a05600f8700d37fc637e223ced3aa8324f3650bf52242",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        .@"zig-wlroots" = .{
 | 
					        .@"zig-wlroots" = .{
 | 
				
			||||||
            .url = "https://codeberg.org/ifreund/zig-wlroots/archive/ae6151f22ceb4ccd7efb1291dea573785918a7ec.tar.gz",
 | 
					            .url = "https://codeberg.org/ifreund/zig-wlroots/archive/e486223799648d27e8b91c5fe0ea4c088b74b707.tar.gz",
 | 
				
			||||||
            .hash = "12204d99aebfbf88f1ff3ab197362937b3d4bef4f45fde9c4ee0d569e095a2a25889",
 | 
					            .hash = "1220aeb3317e16c38583839961c9d695fa60d23a3d506c8275fb0e8fa9849844f2f7",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        .@"zig-xkbcommon" = .{
 | 
					        .@"zig-xkbcommon" = .{
 | 
				
			||||||
            .url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.2.0.tar.gz",
 | 
					            .url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.2.0.tar.gz",
 | 
				
			||||||
 | 
				
			|||||||
@ -307,11 +307,17 @@ matches everything while _\*\*_ and the empty string are invalid.
 | 
				
			|||||||
	- *fullscreen*: Make the view fullscreen. Applies only to new views.
 | 
						- *fullscreen*: Make the view fullscreen. Applies only to new views.
 | 
				
			||||||
	- *no-fullscreen*: Don't make the view fullscreen. Applies only to
 | 
						- *no-fullscreen*: Don't make the view fullscreen. Applies only to
 | 
				
			||||||
	  new views.
 | 
						  new views.
 | 
				
			||||||
 | 
						- *tearing*: Enable tearing for view when fullscreen regardless of the
 | 
				
			||||||
 | 
						  supplied tearing hint. Note that tearing additionally requires setting the
 | 
				
			||||||
 | 
						  *allow-tearing* server option. Applies to new and existing views.
 | 
				
			||||||
 | 
						- *no-tearing*: Disable tearing for view regardless of the supplied
 | 
				
			||||||
 | 
						  tearing hint. Applies to new and existing views.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Both *float* and *no-float* rules are added to the same list,
 | 
						Both *float* and *no-float* rules are added to the same list,
 | 
				
			||||||
	which means that adding a *no-float* rule with the same arguments
 | 
						which means that adding a *no-float* rule with the same arguments
 | 
				
			||||||
	as a *float* rule will overwrite it. The same holds for *ssd* and
 | 
						as a *float* rule will overwrite it. The same holds for *ssd* and
 | 
				
			||||||
	*csd*, *fullscreen* and *no-fullscreen* rules.
 | 
						*csd*, *fullscreen* and *no-fullscreen*, *tearing* and
 | 
				
			||||||
 | 
						*no-tearing* rules.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	If multiple rules in a list match a given view the most specific
 | 
						If multiple rules in a list match a given view the most specific
 | 
				
			||||||
	rule will be applied. For example with the following rules
 | 
						rule will be applied. For example with the following rules
 | 
				
			||||||
@ -364,6 +370,9 @@ matches everything while _\*\*_ and the empty string are invalid.
 | 
				
			|||||||
	Set the attach mode of the currently focused output, overriding the value of
 | 
						Set the attach mode of the currently focused output, overriding the value of
 | 
				
			||||||
	default-attach-mode if any.
 | 
						default-attach-mode if any.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*allow-tearing* *enabled*|*disabled*
 | 
				
			||||||
 | 
						Allow windows to tear if requested by either the program or the user.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*background-color* _0xRRGGBB_|_0xRRGGBBAA_
 | 
					*background-color* _0xRRGGBB_|_0xRRGGBBAA_
 | 
				
			||||||
	Set the background color.
 | 
						Set the background color.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -31,6 +31,11 @@ const Mode = @import("Mode.zig");
 | 
				
			|||||||
const RuleList = @import("rule_list.zig").RuleList;
 | 
					const RuleList = @import("rule_list.zig").RuleList;
 | 
				
			||||||
const View = @import("View.zig");
 | 
					const View = @import("View.zig");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub const AllowTearing = enum {
 | 
				
			||||||
 | 
					    disabled,
 | 
				
			||||||
 | 
					    enabled,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const AttachMode = union(enum) {
 | 
					pub const AttachMode = union(enum) {
 | 
				
			||||||
    top,
 | 
					    top,
 | 
				
			||||||
    bottom,
 | 
					    bottom,
 | 
				
			||||||
@ -68,6 +73,9 @@ pub const Dimensions = struct {
 | 
				
			|||||||
    height: u31,
 | 
					    height: u31,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Whether to allow tearing page flips if a view requests it.
 | 
				
			||||||
 | 
					allow_tearing: AllowTearing = .disabled,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Color of background in RGBA with premultiplied alpha (alpha should only affect nested sessions)
 | 
					/// Color of background in RGBA with premultiplied alpha (alpha should only affect nested sessions)
 | 
				
			||||||
background_color: [4]f32 = [_]f32{ 0.0, 0.16862745, 0.21176471, 1.0 }, // Solarized base03
 | 
					background_color: [4]f32 = [_]f32{ 0.0, 0.16862745, 0.21176471, 1.0 }, // Solarized base03
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -98,6 +106,7 @@ rules: struct {
 | 
				
			|||||||
    position: RuleList(Position) = .{},
 | 
					    position: RuleList(Position) = .{},
 | 
				
			||||||
    dimensions: RuleList(Dimensions) = .{},
 | 
					    dimensions: RuleList(Dimensions) = .{},
 | 
				
			||||||
    fullscreen: RuleList(bool) = .{},
 | 
					    fullscreen: RuleList(bool) = .{},
 | 
				
			||||||
 | 
					    tearing: RuleList(bool) = .{},
 | 
				
			||||||
} = .{},
 | 
					} = .{},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The selected focus_follows_cursor mode
 | 
					/// The selected focus_follows_cursor mode
 | 
				
			||||||
 | 
				
			|||||||
@ -538,10 +538,12 @@ fn handleFrame(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn renderAndCommit(output: *Output, scene_output: *wlr.SceneOutput) !void {
 | 
					fn renderAndCommit(output: *Output, scene_output: *wlr.SceneOutput) !void {
 | 
				
			||||||
    if (output.gamma_dirty) {
 | 
					 | 
				
			||||||
    var state = wlr.Output.State.init();
 | 
					    var state = wlr.Output.State.init();
 | 
				
			||||||
    defer state.finish();
 | 
					    defer state.finish();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!scene_output.buildState(&state, null)) return error.CommitFailed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (output.gamma_dirty) {
 | 
				
			||||||
        const control = server.root.gamma_control_manager.getControl(output.wlr_output);
 | 
					        const control = server.root.gamma_control_manager.getControl(output.wlr_output);
 | 
				
			||||||
        if (!wlr.GammaControlV1.apply(control, &state)) return error.OutOfMemory;
 | 
					        if (!wlr.GammaControlV1.apply(control, &state)) return error.OutOfMemory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -553,15 +555,20 @@ fn renderAndCommit(output: *Output, scene_output: *wlr.SceneOutput) !void {
 | 
				
			|||||||
            // has a null LUT. The wayland backend for example has this behavior.
 | 
					            // has a null LUT. The wayland backend for example has this behavior.
 | 
				
			||||||
            state.committed.gamma_lut = false;
 | 
					            state.committed.gamma_lut = false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!scene_output.buildState(&state, null)) return error.CommitFailed;
 | 
					    if (output.allowTearing() and server.config.allow_tearing == .enabled) {
 | 
				
			||||||
 | 
					        state.tearing_page_flip = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!output.wlr_output.testState(&state)) {
 | 
				
			||||||
 | 
					            log.debug("tearing page flip test failed for {s}, retrying without tearing", .{output.wlr_output.name});
 | 
				
			||||||
 | 
					            state.tearing_page_flip = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!output.wlr_output.commitState(&state)) return error.CommitFailed;
 | 
					    if (!output.wlr_output.commitState(&state)) return error.CommitFailed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        output.gamma_dirty = false;
 | 
					    if (output.gamma_dirty) output.gamma_dirty = false;
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        if (!scene_output.commit(null)) return error.CommitFailed;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (server.lock_manager.state == .locked or
 | 
					    if (server.lock_manager.state == .locked or
 | 
				
			||||||
        (server.lock_manager.state == .waiting_for_lock_surfaces and output.locked_content.node.enabled) or
 | 
					        (server.lock_manager.state == .waiting_for_lock_surfaces and output.locked_content.node.enabled) or
 | 
				
			||||||
@ -635,6 +642,14 @@ fn setTitle(output: Output) void {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn allowTearing(output: *Output) bool {
 | 
				
			||||||
 | 
					    if (output.current.fullscreen) |fullscreen_view| {
 | 
				
			||||||
 | 
					        return fullscreen_view.allowTearing();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn handleLayoutNamespaceChange(output: *Output) void {
 | 
					pub fn handleLayoutNamespaceChange(output: *Output) void {
 | 
				
			||||||
    // The user changed the layout namespace of this output. Try to find a
 | 
					    // The user changed the layout namespace of this output. Try to find a
 | 
				
			||||||
    // matching layout.
 | 
					    // matching layout.
 | 
				
			||||||
 | 
				
			|||||||
@ -84,6 +84,8 @@ screencopy_manager: *wlr.ScreencopyManagerV1,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1,
 | 
					foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tearing_control_manager: *wlr.TearingControlManagerV1,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
input_manager: InputManager,
 | 
					input_manager: InputManager,
 | 
				
			||||||
root: Root,
 | 
					root: Root,
 | 
				
			||||||
config: Config,
 | 
					config: Config,
 | 
				
			||||||
@ -159,6 +161,8 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        .foreign_toplevel_manager = try wlr.ForeignToplevelManagerV1.create(wl_server),
 | 
					        .foreign_toplevel_manager = try wlr.ForeignToplevelManagerV1.create(wl_server),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .tearing_control_manager = try wlr.TearingControlManagerV1.create(wl_server, 1),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .config = try Config.init(),
 | 
					        .config = try Config.init(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .root = undefined,
 | 
					        .root = undefined,
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,7 @@ const math = std.math;
 | 
				
			|||||||
const posix = std.posix;
 | 
					const posix = std.posix;
 | 
				
			||||||
const wlr = @import("wlroots");
 | 
					const wlr = @import("wlroots");
 | 
				
			||||||
const wl = @import("wayland").server.wl;
 | 
					const wl = @import("wayland").server.wl;
 | 
				
			||||||
 | 
					const wp = @import("wayland").server.wp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const server = &@import("main.zig").server;
 | 
					const server = &@import("main.zig").server;
 | 
				
			||||||
const util = @import("util.zig");
 | 
					const util = @import("util.zig");
 | 
				
			||||||
@ -59,6 +60,12 @@ const AttachRelativeMode = enum {
 | 
				
			|||||||
    below,
 | 
					    below,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const TearingMode = enum {
 | 
				
			||||||
 | 
					    override_false,
 | 
				
			||||||
 | 
					    override_true,
 | 
				
			||||||
 | 
					    window_hint,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const State = struct {
 | 
					pub const State = struct {
 | 
				
			||||||
    /// The output the view is currently assigned to.
 | 
					    /// The output the view is currently assigned to.
 | 
				
			||||||
    /// May be null if there are no outputs or for newly created views.
 | 
					    /// May be null if there are no outputs or for newly created views.
 | 
				
			||||||
@ -177,6 +184,9 @@ foreign_toplevel_handle: ForeignToplevelHandle = .{},
 | 
				
			|||||||
/// Connector name of the output this view occupied before an evacuation.
 | 
					/// Connector name of the output this view occupied before an evacuation.
 | 
				
			||||||
output_before_evac: ?[]const u8 = null,
 | 
					output_before_evac: ?[]const u8 = null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Current tearing mode of the view. Defaults to using the window tearing hint.
 | 
				
			||||||
 | 
					tearing_mode: TearingMode = .window_hint,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn create(impl: Impl) error{OutOfMemory}!*View {
 | 
					pub fn create(impl: Impl) error{OutOfMemory}!*View {
 | 
				
			||||||
    assert(impl != .none);
 | 
					    assert(impl != .none);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -572,6 +582,17 @@ pub fn getAppId(view: View) ?[*:0]const u8 {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Return true if the view can currently tear.
 | 
				
			||||||
 | 
					pub fn allowTearing(view: *View) bool {
 | 
				
			||||||
 | 
					    switch (view.tearing_mode) {
 | 
				
			||||||
 | 
					        TearingMode.override_false => return false,
 | 
				
			||||||
 | 
					        TearingMode.override_true => return true,
 | 
				
			||||||
 | 
					        TearingMode.window_hint => if (view.rootSurface()) |root_surface| {
 | 
				
			||||||
 | 
					            return server.tearing_control_manager.hintFromSurface(root_surface) == .@"async";
 | 
				
			||||||
 | 
					        } else return false,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Clamp the width/height of the box to the constraints of the view
 | 
					/// Clamp the width/height of the box to the constraints of the view
 | 
				
			||||||
pub fn applyConstraints(view: *View, box: *wlr.Box) void {
 | 
					pub fn applyConstraints(view: *View, box: *wlr.Box) void {
 | 
				
			||||||
    box.width = math.clamp(box.width, view.constraints.min_width, view.constraints.max_width);
 | 
					    box.width = math.clamp(box.width, view.constraints.min_width, view.constraints.max_width);
 | 
				
			||||||
@ -640,6 +661,10 @@ pub fn map(view: *View) !void {
 | 
				
			|||||||
        view.pending.ssd = ssd;
 | 
					        view.pending.ssd = ssd;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (server.config.rules.tearing.match(view)) |tearing| {
 | 
				
			||||||
 | 
					        view.tearing_mode = if (tearing) .override_true else .override_false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (server.config.rules.dimensions.match(view)) |dimensions| {
 | 
					    if (server.config.rules.dimensions.match(view)) |dimensions| {
 | 
				
			||||||
        view.pending.box.width = dimensions.width;
 | 
					        view.pending.box.width = dimensions.width;
 | 
				
			||||||
        view.pending.box.height = dimensions.height;
 | 
					        view.pending.box.height = dimensions.height;
 | 
				
			||||||
 | 
				
			|||||||
@ -41,6 +41,7 @@ const command_impls = std.StaticStringMap(
 | 
				
			|||||||
).initComptime(
 | 
					).initComptime(
 | 
				
			||||||
    .{
 | 
					    .{
 | 
				
			||||||
        // zig fmt: off
 | 
					        // zig fmt: off
 | 
				
			||||||
 | 
					        .{ "allow-tearing",             @import("command/config.zig").allowTearing },
 | 
				
			||||||
        .{ "attach-mode",               @import("command/attach_mode.zig").defaultAttachMode },
 | 
					        .{ "attach-mode",               @import("command/attach_mode.zig").defaultAttachMode },
 | 
				
			||||||
        .{ "background-color",          @import("command/config.zig").backgroundColor },
 | 
					        .{ "background-color",          @import("command/config.zig").backgroundColor },
 | 
				
			||||||
        .{ "border-color-focused",      @import("command/config.zig").borderColorFocused },
 | 
					        .{ "border-color-focused",      @import("command/config.zig").borderColorFocused },
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,17 @@ const Error = @import("../command.zig").Error;
 | 
				
			|||||||
const Seat = @import("../Seat.zig");
 | 
					const Seat = @import("../Seat.zig");
 | 
				
			||||||
const Config = @import("../Config.zig");
 | 
					const Config = @import("../Config.zig");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn allowTearing(
 | 
				
			||||||
 | 
					    _: *Seat,
 | 
				
			||||||
 | 
					    args: []const [:0]const u8,
 | 
				
			||||||
 | 
					    _: *?[]const u8,
 | 
				
			||||||
 | 
					) Error!void {
 | 
				
			||||||
 | 
					    if (args.len < 2) return Error.NotEnoughArguments;
 | 
				
			||||||
 | 
					    if (args.len > 2) return Error.TooManyArguments;
 | 
				
			||||||
 | 
					    server.config.allow_tearing = std.meta.stringToEnum(Config.AllowTearing, args[1]) orelse
 | 
				
			||||||
 | 
					        return Error.UnknownOption;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn borderWidth(
 | 
					pub fn borderWidth(
 | 
				
			||||||
    _: *Seat,
 | 
					    _: *Seat,
 | 
				
			||||||
    args: []const [:0]const u8,
 | 
					    args: []const [:0]const u8,
 | 
				
			||||||
 | 
				
			|||||||
@ -38,6 +38,8 @@ const Action = enum {
 | 
				
			|||||||
    dimensions,
 | 
					    dimensions,
 | 
				
			||||||
    fullscreen,
 | 
					    fullscreen,
 | 
				
			||||||
    @"no-fullscreen",
 | 
					    @"no-fullscreen",
 | 
				
			||||||
 | 
					    tearing,
 | 
				
			||||||
 | 
					    @"no-tearing",
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void {
 | 
					pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void {
 | 
				
			||||||
@ -53,7 +55,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
 | 
				
			|||||||
    const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption;
 | 
					    const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const positional_arguments_count: u8 = switch (action) {
 | 
					    const positional_arguments_count: u8 = switch (action) {
 | 
				
			||||||
        .float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen" => 1,
 | 
					        .float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen", .tearing, .@"no-tearing" => 1,
 | 
				
			||||||
        .tags, .output => 2,
 | 
					        .tags, .output => 2,
 | 
				
			||||||
        .position, .dimensions => 3,
 | 
					        .position, .dimensions => 3,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@ -83,6 +85,14 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
 | 
				
			|||||||
            apply_ssd_rules();
 | 
					            apply_ssd_rules();
 | 
				
			||||||
            server.root.applyPending();
 | 
					            server.root.applyPending();
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        .tearing, .@"no-tearing" => {
 | 
				
			||||||
 | 
					            try server.config.rules.tearing.add(.{
 | 
				
			||||||
 | 
					                .app_id_glob = app_id_glob,
 | 
				
			||||||
 | 
					                .title_glob = title_glob,
 | 
				
			||||||
 | 
					                .value = (action == .tearing),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            apply_tearing_rules();
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        .tags => {
 | 
					        .tags => {
 | 
				
			||||||
            const tags = try fmt.parseInt(u32, result.args[1], 10);
 | 
					            const tags = try fmt.parseInt(u32, result.args[1], 10);
 | 
				
			||||||
            try server.config.rules.tags.add(.{
 | 
					            try server.config.rules.tags.add(.{
 | 
				
			||||||
@ -177,6 +187,10 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
 | 
				
			|||||||
        .fullscreen, .@"no-fullscreen" => {
 | 
					        .fullscreen, .@"no-fullscreen" => {
 | 
				
			||||||
            _ = server.config.rules.fullscreen.del(rule);
 | 
					            _ = server.config.rules.fullscreen.del(rule);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        .tearing, .@"no-tearing" => {
 | 
				
			||||||
 | 
					            _ = server.config.rules.tearing.del(rule);
 | 
				
			||||||
 | 
					            apply_tearing_rules();
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -191,6 +205,17 @@ fn apply_ssd_rules() void {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn apply_tearing_rules() void {
 | 
				
			||||||
 | 
					    var it = server.root.views.iterator(.forward);
 | 
				
			||||||
 | 
					    while (it.next()) |view| {
 | 
				
			||||||
 | 
					        if (view.destroying) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (server.config.rules.tearing.match(view)) |tearing| {
 | 
				
			||||||
 | 
					            view.tearing_mode = if (tearing) .override_true else .override_false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void {
 | 
					pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) 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;
 | 
				
			||||||
@ -203,6 +228,7 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!
 | 
				
			|||||||
        position,
 | 
					        position,
 | 
				
			||||||
        dimensions,
 | 
					        dimensions,
 | 
				
			||||||
        fullscreen,
 | 
					        fullscreen,
 | 
				
			||||||
 | 
					        tearing,
 | 
				
			||||||
    }, args[1]) orelse return Error.UnknownOption;
 | 
					    }, args[1]) orelse return Error.UnknownOption;
 | 
				
			||||||
    const max_glob_len = switch (rule_list) {
 | 
					    const max_glob_len = switch (rule_list) {
 | 
				
			||||||
        inline else => |list| @field(server.config.rules, @tagName(list)).getMaxGlobLen(),
 | 
					        inline else => |list| @field(server.config.rules, @tagName(list)).getMaxGlobLen(),
 | 
				
			||||||
@ -218,12 +244,13 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!
 | 
				
			|||||||
    try writer.writeAll("action\n");
 | 
					    try writer.writeAll("action\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (rule_list) {
 | 
					    switch (rule_list) {
 | 
				
			||||||
        inline .float, .ssd, .output, .fullscreen => |list| {
 | 
					        inline .float, .ssd, .output, .fullscreen, .tearing => |list| {
 | 
				
			||||||
            const rules = switch (list) {
 | 
					            const rules = switch (list) {
 | 
				
			||||||
                .float => server.config.rules.float.rules.items,
 | 
					                .float => server.config.rules.float.rules.items,
 | 
				
			||||||
                .ssd => server.config.rules.ssd.rules.items,
 | 
					                .ssd => server.config.rules.ssd.rules.items,
 | 
				
			||||||
                .output => server.config.rules.output.rules.items,
 | 
					                .output => server.config.rules.output.rules.items,
 | 
				
			||||||
                .fullscreen => server.config.rules.fullscreen.rules.items,
 | 
					                .fullscreen => server.config.rules.fullscreen.rules.items,
 | 
				
			||||||
 | 
					                .tearing => server.config.rules.tearing.rules.items,
 | 
				
			||||||
                else => unreachable,
 | 
					                else => unreachable,
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            for (rules) |rule| {
 | 
					            for (rules) |rule| {
 | 
				
			||||||
@ -234,6 +261,7 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!
 | 
				
			|||||||
                    .ssd => if (rule.value) "ssd" else "csd",
 | 
					                    .ssd => if (rule.value) "ssd" else "csd",
 | 
				
			||||||
                    .output => rule.value,
 | 
					                    .output => rule.value,
 | 
				
			||||||
                    .fullscreen => if (rule.value) "fullscreen" else "no-fullscreen",
 | 
					                    .fullscreen => if (rule.value) "fullscreen" else "no-fullscreen",
 | 
				
			||||||
 | 
					                    .tearing => if (rule.value) "tearing" else "no-tearing",
 | 
				
			||||||
                    else => unreachable,
 | 
					                    else => unreachable,
 | 
				
			||||||
                }});
 | 
					                }});
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user