From 7e02fb679cc5cddb0f8584471576f89ae981acea Mon Sep 17 00:00:00 2001 From: Marten Ringwelski Date: Tue, 15 Sep 2020 00:38:50 +0200 Subject: [PATCH] Implement focus-follows-cursor --- doc/riverctl.1.scd | 7 +++++ river/Config.zig | 11 ++++++++ river/Cursor.zig | 25 +++++++++++++++++ river/command.zig | 1 + river/command/focus_follows_cursor.zig | 39 ++++++++++++++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 river/command/focus_follows_cursor.zig diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index 3632827..f233ad1 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -116,6 +116,13 @@ that tag 1 through 9 are visible. *enter-mode* _name_ Switch to given mode if it exits. +*focus-follows-cursor* _disabled_|_normal_|_strict_ + When _disabled_ moving the cursor will not influence the focus. This is the default setting. + If set to _normal_ moving the cursor over a window will focus that window. + The focus still can be changed and moving the cursor within the (now unfocused) window will not change the focus to that window + but let the currently focused window in focus. + When set to _strict_ this is not the case. The focus will be updated on every cursor movement. + *map* _mode_ _modifiers_ _key_ _command_ _mode_ is either “normal” (the default mode) or a mode created with *declare-mode*. _modifiers_ is a list of one or more of the following diff --git a/river/Config.zig b/river/Config.zig index 900dabc..334c21d 100644 --- a/river/Config.zig +++ b/river/Config.zig @@ -25,6 +25,14 @@ const util = @import("util.zig"); const Server = @import("Server.zig"); const Mode = @import("Mode.zig"); +pub const FocusFollowsCursorMode = enum { + disabled, + /// Only change focus on entering a surface + normal, + /// On cursor movement the focus will be updated to the surface below the cursor + strict, +}; + /// Color of background in RGBA (alpha should only affect nested sessions) background_color: [4]f32 = [_]f32{ 0.0, 0.16862745, 0.21176471, 1.0 }, // Solarized base03 @@ -55,6 +63,9 @@ float_filter: std.ArrayList([]const u8), /// List of app_ids which are allowed to use client side decorations csd_filter: std.ArrayList([]const u8), +/// The selected focus_follows_cursor mode +focus_follows_cursor: FocusFollowsCursorMode = .disabled, + pub fn init() !Self { var self = Self{ .mode_to_id = std.StringHashMap(usize).init(util.gpa), diff --git a/river/Cursor.zig b/river/Cursor.zig index 045d08a..d102f53 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -185,6 +185,9 @@ const Mode = union(enum) { /// Pass an event on to the surface under the cursor, if any. fn passthrough(self: *Self, time: u32) void { + const root = &self.seat.input_manager.server.root; + const config = self.seat.input_manager.server.config; + var sx: f64 = undefined; var sy: f64 = undefined; if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |wlr_surface| { @@ -192,8 +195,30 @@ const Mode = union(enum) { // events. Note that wlroots won't actually send an enter event if // the surface has already been entered. if (self.seat.input_manager.inputAllowed(wlr_surface)) { + // The focus change must be checked before sending enter events + const focus_change = self.seat.wlr_seat.pointer_state.focused_surface != wlr_surface; + c.wlr_seat_pointer_notify_enter(self.seat.wlr_seat, wlr_surface, sx, sy); c.wlr_seat_pointer_notify_motion(self.seat.wlr_seat, time, sx, sy); + if (View.fromWlrSurface(wlr_surface)) |view| { + // Change focus according to config + switch (config.focus_follows_cursor) { + .disabled => {}, + .normal => { + // Only refocus when the cursor entered a new surface + if (focus_change) { + self.seat.focus(view); + root.startTransaction(); + } + }, + .strict => { + self.seat.focus(view); + root.startTransaction(); + }, + } + } + + return; } } else { // There is either no surface under the cursor or input is disallowed diff --git a/river/command.zig b/river/command.zig index cd9489c..bfe0727 100644 --- a/river/command.zig +++ b/river/command.zig @@ -42,6 +42,7 @@ const str_to_impl_fn = [_]struct { .{ .name = "exit", .impl = @import("command/exit.zig").exit }, .{ .name = "float-filter-add", .impl = @import("command/filter.zig").floatFilterAdd }, .{ .name = "focus-output", .impl = @import("command/focus_output.zig").focusOutput }, + .{ .name = "focus-follows-cursor", .impl = @import("command/focus_follows_cursor.zig").focusFollowsCursor }, .{ .name = "focus-view", .impl = @import("command/focus_view.zig").focusView }, .{ .name = "layout", .impl = @import("command/layout.zig").layout }, .{ .name = "map", .impl = @import("command/map.zig").map }, diff --git a/river/command/focus_follows_cursor.zig b/river/command/focus_follows_cursor.zig new file mode 100644 index 0000000..392e69f --- /dev/null +++ b/river/command/focus_follows_cursor.zig @@ -0,0 +1,39 @@ +// This file is part of river, a dynamic tiling wayland compositor. +// +// Copyright 2020 Marten Ringwelski +// +// 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 . + +const std = @import("std"); + +const util = @import("../util.zig"); + +const Config = @import("../Config.zig"); +const Error = @import("../command.zig").Error; +const Seat = @import("../Seat.zig"); + +pub fn focusFollowsCursor( + allocator: *std.mem.Allocator, + seat: *Seat, + args: []const []const u8, + out: *?[]const u8, +) Error!void { + if (args.len < 2) return Error.NotEnoughArguments; + if (args.len > 2) return Error.TooManyArguments; + + const server = seat.input_manager.server; + + server.config.focus_follows_cursor = + std.meta.stringToEnum(Config.FocusFollowsCursorMode, args[1]) orelse return Error.UnknownOption; +}