Add spacial output operations

List based output operations are tedious for complex output layouts.
This commit is contained in:
Leon Henrik Plickat 2021-06-14 02:36:07 +00:00 committed by Isaac Freund
parent 3efcfedcf4
commit d3a9e96f7d
5 changed files with 106 additions and 124 deletions

View file

@ -32,8 +32,8 @@ over the Wayland protocol.
Add _app-id_ to the float filter list. Views with this _app-id_ Add _app-id_ to the float filter list. Views with this _app-id_
will start floating. will start floating.
*focus-output* *next*|*previous* *focus-output* *next*|*previous*|*up*|*right*|*down*|*left*
Focus the next or previous output. Focus the next or previous output or the closest output in any direction.
*focus-view* *next*|*previous* *focus-view* *next*|*previous*
Focus the next or previous view in the stack. Focus the next or previous view in the stack.
@ -50,8 +50,9 @@ over the Wayland protocol.
Snap the focused view to the specified screen edge. The view will Snap the focused view to the specified screen edge. The view will
be set to floating. be set to floating.
*send-to-output* *next*|*previous* *send-to-output* *next*|*previous*|*up*|*right*|*down*|*left*
Send the focused view to the next or the previous output. Send the focused view to the next or previous output or the closest
output in any direction.
*spawn* _shell_command_ *spawn* _shell_command_
Run _shell_command_ using _/bin/sh -c_. Put single quotes around Run _shell_command_ using _/bin/sh -c_. Put single quotes around

View file

@ -55,7 +55,7 @@ const str_to_impl_fn = [_]struct {
.{ .name = "exit", .impl = @import("command/exit.zig").exit }, .{ .name = "exit", .impl = @import("command/exit.zig").exit },
.{ .name = "float-filter-add", .impl = @import("command/filter.zig").floatFilterAdd }, .{ .name = "float-filter-add", .impl = @import("command/filter.zig").floatFilterAdd },
.{ .name = "focus-follows-cursor", .impl = @import("command/focus_follows_cursor.zig").focusFollowsCursor }, .{ .name = "focus-follows-cursor", .impl = @import("command/focus_follows_cursor.zig").focusFollowsCursor },
.{ .name = "focus-output", .impl = @import("command/focus_output.zig").focusOutput }, .{ .name = "focus-output", .impl = @import("command/output.zig").focusOutput },
.{ .name = "focus-view", .impl = @import("command/focus_view.zig").focusView }, .{ .name = "focus-view", .impl = @import("command/focus_view.zig").focusView },
.{ .name = "input", .impl = @import("command/input.zig").input }, .{ .name = "input", .impl = @import("command/input.zig").input },
.{ .name = "list-inputs", .impl = @import("command/input.zig").listInputs }, .{ .name = "list-inputs", .impl = @import("command/input.zig").listInputs },
@ -67,7 +67,7 @@ const str_to_impl_fn = [_]struct {
.{ .name = "opacity", .impl = @import("command/opacity.zig").opacity }, .{ .name = "opacity", .impl = @import("command/opacity.zig").opacity },
.{ .name = "output-layout", .impl = @import("command/layout.zig").outputLayout }, .{ .name = "output-layout", .impl = @import("command/layout.zig").outputLayout },
.{ .name = "resize", .impl = @import("command/move.zig").resize }, .{ .name = "resize", .impl = @import("command/move.zig").resize },
.{ .name = "send-to-output", .impl = @import("command/send_to_output.zig").sendToOutput }, .{ .name = "send-to-output", .impl = @import("command/output.zig").sendToOutput },
.{ .name = "set-focused-tags", .impl = @import("command/tags.zig").setFocusedTags }, .{ .name = "set-focused-tags", .impl = @import("command/tags.zig").setFocusedTags },
.{ .name = "set-layout-value", .impl = @import("command/layout.zig").setLayoutValue }, .{ .name = "set-layout-value", .impl = @import("command/layout.zig").setLayoutValue },
.{ .name = "set-repeat", .impl = @import("command/set_repeat.zig").setRepeat }, .{ .name = "set-repeat", .impl = @import("command/set_repeat.zig").setRepeat },

View file

@ -1,55 +0,0 @@
// This file is part of river, a dynamic tiling wayland compositor.
//
// Copyright 2020 The River Developers
//
// 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 <https://www.gnu.org/licenses/>.
const std = @import("std");
const server = &@import("../main.zig").server;
const Direction = @import("../command.zig").Direction;
const Error = @import("../command.zig").Error;
const Output = @import("../Output.zig");
const Seat = @import("../Seat.zig");
/// Focus either the next or the previous output, depending on the bool passed.
/// Does nothing if there is only one output.
pub fn focusOutput(
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 direction = std.meta.stringToEnum(Direction, args[1]) orelse return Error.InvalidDirection;
// If the noop output is focused, there are no other outputs to switch to
if (seat.focused_output == &server.root.noop_output) {
std.debug.assert(server.root.outputs.len == 0);
return;
}
// Focus the next/prev output in the list if there is one, else wrap
const focused_node = @fieldParentPtr(std.TailQueue(Output).Node, "data", seat.focused_output);
seat.focusOutput(switch (direction) {
.next => if (focused_node.next) |node| &node.data else &server.root.outputs.first.?.data,
.previous => if (focused_node.prev) |node| &node.data else &server.root.outputs.last.?.data,
});
seat.focus(null);
server.root.startTransaction();
}

99
river/command/output.zig Normal file
View file

@ -0,0 +1,99 @@
// This file is part of river, a dynamic tiling wayland compositor.
//
// Copyright 2021 The River Developers
//
// 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 <https://www.gnu.org/licenses/>.
const std = @import("std");
const wlr = @import("wlroots");
const server = &@import("../main.zig").server;
const Direction = @import("../command.zig").Direction;
const PhysicalDirectionDirection = @import("../command.zig").PhysicalDirection;
const Error = @import("../command.zig").Error;
const Output = @import("../Output.zig");
const Seat = @import("../Seat.zig");
pub fn focusOutput(
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;
// If the noop output is focused, there are no other outputs to switch to
if (seat.focused_output == &server.root.noop_output) {
std.debug.assert(server.root.outputs.len == 0);
return;
}
seat.focusOutput((try getOutput(seat, args[1])) orelse return);
seat.focus(null);
server.root.startTransaction();
}
pub fn sendToOutput(
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;
// If the noop output is focused, there is nowhere to send the view
if (seat.focused_output == &server.root.noop_output) {
std.debug.assert(server.root.outputs.len == 0);
return;
}
if (seat.focused == .view) {
const destination_output = (try getOutput(seat, args[1])) orelse return;
seat.focused.view.sendToOutput(destination_output);
// Handle the change and focus whatever's next in the focus stack
seat.focus(null);
seat.focused_output.arrangeViews();
destination_output.arrangeViews();
server.root.startTransaction();
}
}
/// Find an output adjacent to the currently focused based on either logical or
/// spacial direction
fn getOutput(seat: *Seat, str: []const u8) !?*Output {
if (std.meta.stringToEnum(Direction, str)) |direction| { // Logical direction
// Return the next/prev output in the list if there is one, else wrap
const focused_node = @fieldParentPtr(std.TailQueue(Output).Node, "data", seat.focused_output);
return switch (direction) {
.next => if (focused_node.next) |node| &node.data else &server.root.outputs.first.?.data,
.previous => if (focused_node.prev) |node| &node.data else &server.root.outputs.last.?.data,
};
} else if (std.meta.stringToEnum(wlr.OutputLayout.Direction, str)) |direction| { // Spacial direction
const focus_box = server.root.output_layout.getBox(seat.focused_output.wlr_output) orelse return null;
const wlr_output = server.root.output_layout.adjacentOutput(
direction,
seat.focused_output.wlr_output,
@intToFloat(f64, focus_box.x + @divFloor(focus_box.width, 2)),
@intToFloat(f64, focus_box.y + @divFloor(focus_box.height, 2)),
) orelse return null;
return @intToPtr(*Output, wlr_output.data);
} else {
return Error.InvalidDirection;
}
}

View file

@ -1,63 +0,0 @@
// This file is part of river, a dynamic tiling wayland compositor.
//
// Copyright 2020 The River Developers
//
// 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 <https://www.gnu.org/licenses/>.
const std = @import("std");
const server = &@import("../main.zig").server;
const Direction = @import("../command.zig").Direction;
const Error = @import("../command.zig").Error;
const Output = @import("../Output.zig");
const Seat = @import("../Seat.zig");
/// Send the focused view to the the next or the previous output, depending on
/// the bool passed. Does nothing if there is only one output.
pub fn sendToOutput(
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 direction = std.meta.stringToEnum(Direction, args[1]) orelse return Error.InvalidDirection;
if (seat.focused == .view) {
// If the noop output is focused, there is nowhere to send the view
if (seat.focused_output == &server.root.noop_output) {
std.debug.assert(server.root.outputs.len == 0);
return;
}
// Send to the next/prev output in the list if there is one, else wrap
const current_node = @fieldParentPtr(std.TailQueue(Output).Node, "data", seat.focused_output);
const destination_output = switch (direction) {
.next => if (current_node.next) |node| &node.data else &server.root.outputs.first.?.data,
.previous => if (current_node.prev) |node| &node.data else &server.root.outputs.last.?.data,
};
// Move the view to the target output
seat.focused.view.sendToOutput(destination_output);
// Handle the change and focus whatever's next in the focus stack
seat.focus(null);
seat.focused_output.arrangeViews();
destination_output.arrangeViews();
server.root.startTransaction();
}
}