river: rework core data structures & transactions
This commit is contained in:
@ -33,8 +33,7 @@ pub fn borderWidth(
|
||||
if (args.len > 2) return Error.TooManyArguments;
|
||||
|
||||
server.config.border_width = try fmt.parseInt(u31, args[1], 10);
|
||||
server.root.arrangeAll();
|
||||
server.root.startTransaction();
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
pub fn backgroundColor(
|
||||
@ -62,7 +61,7 @@ pub fn borderColorFocused(
|
||||
if (args.len > 2) return Error.TooManyArguments;
|
||||
|
||||
server.config.border_color_focused = try parseRgba(args[1]);
|
||||
server.root.startTransaction();
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
pub fn borderColorUnfocused(
|
||||
@ -74,7 +73,7 @@ pub fn borderColorUnfocused(
|
||||
if (args.len > 2) return Error.TooManyArguments;
|
||||
|
||||
server.config.border_color_unfocused = try parseRgba(args[1]);
|
||||
server.root.startTransaction();
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
pub fn borderColorUrgent(
|
||||
@ -86,7 +85,7 @@ pub fn borderColorUrgent(
|
||||
if (args.len > 2) return Error.TooManyArguments;
|
||||
|
||||
server.config.border_color_urgent = try parseRgba(args[1]);
|
||||
server.root.startTransaction();
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
pub fn setCursorWarp(
|
||||
|
@ -15,14 +15,15 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
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");
|
||||
const View = @import("../View.zig");
|
||||
const ViewStack = @import("../view_stack.zig").ViewStack;
|
||||
|
||||
/// Focus either the next or the previous visible view, depending on the enum
|
||||
/// passed. Does nothing if there are 1 or 0 views in the stack.
|
||||
@ -35,40 +36,45 @@ pub fn focusView(
|
||||
if (args.len > 2) return Error.TooManyArguments;
|
||||
|
||||
const direction = std.meta.stringToEnum(Direction, args[1]) orelse return Error.InvalidDirection;
|
||||
const output = seat.focused_output;
|
||||
const output = seat.focused_output orelse return;
|
||||
|
||||
if (seat.focused == .view) {
|
||||
// If the focused view is fullscreen, do nothing
|
||||
if (seat.focused.view.current.fullscreen) return;
|
||||
if (seat.focused != .view) return;
|
||||
if (seat.focused.view.pending.fullscreen) return;
|
||||
|
||||
// If there is a currently focused view, focus the next visible view in the stack.
|
||||
const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", seat.focused.view);
|
||||
var it = switch (direction) {
|
||||
.next => ViewStack(View).iter(focused_node, .forward, output.pending.tags, filter),
|
||||
.previous => ViewStack(View).iter(focused_node, .reverse, output.pending.tags, filter),
|
||||
};
|
||||
|
||||
// Skip past the focused node
|
||||
_ = it.next();
|
||||
// Focus the next visible node if there is one
|
||||
if (it.next()) |view| {
|
||||
seat.focus(view);
|
||||
server.root.startTransaction();
|
||||
return;
|
||||
}
|
||||
if (focusViewTarget(seat, output, direction)) |target| {
|
||||
assert(!target.pending.fullscreen);
|
||||
seat.focus(target);
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
// There is either no currently focused view or the last visible view in the
|
||||
// stack is focused and we need to wrap.
|
||||
var it = switch (direction) {
|
||||
.next => ViewStack(View).iter(output.views.first, .forward, output.pending.tags, filter),
|
||||
.previous => ViewStack(View).iter(output.views.last, .reverse, output.pending.tags, filter),
|
||||
};
|
||||
|
||||
seat.focus(it.next());
|
||||
server.root.startTransaction();
|
||||
}
|
||||
|
||||
fn filter(view: *View, filter_tags: u32) bool {
|
||||
return view.tree.node.enabled and view.pending.tags & filter_tags != 0;
|
||||
fn focusViewTarget(seat: *Seat, output: *Output, direction: Direction) ?*View {
|
||||
switch (direction) {
|
||||
inline else => |dir| {
|
||||
const it_dir = comptime switch (dir) {
|
||||
.next => .forward,
|
||||
.previous => .reverse,
|
||||
};
|
||||
var it = output.pending.wm_stack.iterator(it_dir);
|
||||
while (it.next()) |view| {
|
||||
if (view == seat.focused.view) break;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
// Return the next view in the stack matching the tags if any.
|
||||
while (it.next()) |view| {
|
||||
if (output.pending.tags & view.pending.tags != 0) return view;
|
||||
}
|
||||
|
||||
// Wrap and return the first view in the stack matching the tags if
|
||||
// any is found before completing the loop back to the focused view.
|
||||
while (it.next()) |view| {
|
||||
if (view == seat.focused.view) return null;
|
||||
if (output.pending.tags & view.pending.tags != 0) return view;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ pub fn outputLayout(
|
||||
if (args.len < 2) return Error.NotEnoughArguments;
|
||||
if (args.len > 2) return Error.TooManyArguments;
|
||||
|
||||
const output = seat.focused_output;
|
||||
const output = seat.focused_output orelse return;
|
||||
const old_layout_namespace = output.layout_namespace;
|
||||
output.layout_namespace = try util.gpa.dupe(u8, args[1]);
|
||||
if (old_layout_namespace) |old| util.gpa.free(old);
|
||||
@ -69,7 +69,7 @@ pub fn sendLayoutCmd(
|
||||
if (args.len < 3) return Error.NotEnoughArguments;
|
||||
if (args.len > 3) return Error.TooManyArguments;
|
||||
|
||||
const output = seat.focused_output;
|
||||
const output = seat.focused_output orelse return;
|
||||
const target_namespace = args[1];
|
||||
|
||||
var it = output.layouts.first;
|
||||
@ -82,5 +82,5 @@ pub fn sendLayoutCmd(
|
||||
layout.layout.sendUserCommandTags(output.pending.tags);
|
||||
}
|
||||
layout.layout.sendUserCommand(args[2]);
|
||||
if (layout == output.current.layout) output.arrangeViews();
|
||||
if (layout == output.layout) server.root.applyPending();
|
||||
}
|
||||
|
@ -60,10 +60,11 @@ pub fn snap(
|
||||
return Error.InvalidPhysicalDirection;
|
||||
|
||||
const view = getView(seat) orelse return;
|
||||
const output = view.pending.output orelse return;
|
||||
const border_width = server.config.border_width;
|
||||
var output_width: i32 = undefined;
|
||||
var output_height: i32 = undefined;
|
||||
view.output.wlr_output.effectiveResolution(&output_width, &output_height);
|
||||
output.wlr_output.effectiveResolution(&output_width, &output_height);
|
||||
switch (direction) {
|
||||
.up => view.pending.box.y = border_width,
|
||||
.down => view.pending.box.y = output_height - view.pending.box.height - border_width,
|
||||
@ -87,14 +88,16 @@ pub fn resize(
|
||||
return Error.InvalidOrientation;
|
||||
|
||||
const view = getView(seat) orelse return;
|
||||
var output_width: i32 = undefined;
|
||||
var output_height: i32 = undefined;
|
||||
view.output.wlr_output.effectiveResolution(&output_width, &output_height);
|
||||
var output_width: c_int = math.maxInt(c_int);
|
||||
var output_height: c_int = math.maxInt(c_int);
|
||||
if (view.pending.output) |output| {
|
||||
output.wlr_output.effectiveResolution(&output_width, &output_height);
|
||||
}
|
||||
switch (orientation) {
|
||||
.horizontal => {
|
||||
const prev_width = view.pending.box.width;
|
||||
view.pending.box.width += delta;
|
||||
view.applyConstraints();
|
||||
view.applyConstraints(&view.pending.box);
|
||||
// Get width difference after applying view constraints, so that the
|
||||
// move reflects the actual size difference, but before applying the
|
||||
// output size constraints, to allow growing a view even if it is
|
||||
@ -110,7 +113,7 @@ pub fn resize(
|
||||
.vertical => {
|
||||
const prev_height = view.pending.box.height;
|
||||
view.pending.box.height += delta;
|
||||
view.applyConstraints();
|
||||
view.applyConstraints(&view.pending.box);
|
||||
const diff_height = prev_height - view.pending.box.height;
|
||||
// Do not grow bigger than the output
|
||||
view.pending.box.height = math.min(
|
||||
@ -129,12 +132,11 @@ fn apply(view: *View) void {
|
||||
// dimensions are set by a layout generator. If however the views are
|
||||
// unarranged, leave them as non-floating so the next active layout can
|
||||
// affect them.
|
||||
if (view.output.pending.layout != null)
|
||||
if (view.pending.output == null or view.pending.output.?.layout != null) {
|
||||
view.pending.float = true;
|
||||
}
|
||||
|
||||
view.float_box = view.pending.box;
|
||||
|
||||
view.applyPending();
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
fn getView(seat: *Seat) ?*View {
|
||||
|
@ -37,14 +37,14 @@ pub fn focusOutput(
|
||||
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) {
|
||||
if (seat.focused_output == null) {
|
||||
assert(server.root.outputs.len == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
seat.focusOutput((try getOutput(seat, args[1])) orelse return);
|
||||
seat.focus(null);
|
||||
server.root.startTransaction();
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
pub fn sendToOutput(
|
||||
@ -56,22 +56,21 @@ pub fn sendToOutput(
|
||||
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) {
|
||||
if (seat.focused_output == null) {
|
||||
assert(server.root.outputs.len == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (seat.focused == .view) {
|
||||
const destination_output = (try getOutput(seat, args[1])) orelse return;
|
||||
|
||||
// If the view is already on destination_output, do nothing
|
||||
if (seat.focused.view.output == destination_output) return;
|
||||
seat.focused.view.sendToOutput(destination_output);
|
||||
if (seat.focused.view.pending.output == destination_output) return;
|
||||
seat.focused.view.setPendingOutput(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();
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,19 +79,19 @@ pub fn sendToOutput(
|
||||
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);
|
||||
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
|
||||
var focus_box: wlr.Box = undefined;
|
||||
server.root.output_layout.getBox(seat.focused_output.wlr_output, &focus_box);
|
||||
server.root.output_layout.getBox(seat.focused_output.?.wlr_output, &focus_box);
|
||||
if (focus_box.empty()) return null;
|
||||
|
||||
const wlr_output = server.root.output_layout.adjacentOutput(
|
||||
direction,
|
||||
seat.focused_output.wlr_output,
|
||||
seat.focused_output.?.wlr_output,
|
||||
@intToFloat(f64, focus_box.x + @divTrunc(focus_box.width, 2)),
|
||||
@intToFloat(f64, focus_box.y + @divTrunc(focus_box.height, 2)),
|
||||
) orelse return null;
|
||||
|
@ -15,14 +15,15 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const server = &@import("../main.zig").server;
|
||||
|
||||
const Error = @import("../command.zig").Error;
|
||||
const Direction = @import("../command.zig").Direction;
|
||||
const Error = @import("../command.zig").Error;
|
||||
const Output = @import("../Output.zig");
|
||||
const Seat = @import("../Seat.zig");
|
||||
const View = @import("../View.zig");
|
||||
const ViewStack = @import("../view_stack.zig").ViewStack;
|
||||
|
||||
/// Swap the currently focused view with either the view higher or lower in the visible stack
|
||||
pub fn swap(
|
||||
@ -33,51 +34,47 @@ pub fn swap(
|
||||
if (args.len < 2) return Error.NotEnoughArguments;
|
||||
if (args.len > 2) return Error.TooManyArguments;
|
||||
|
||||
if (seat.focused != .view)
|
||||
return;
|
||||
const direction = std.meta.stringToEnum(Direction, args[1]) orelse return Error.InvalidDirection;
|
||||
const output = seat.focused_output orelse return;
|
||||
|
||||
// Filter out everything that is not part of the current layout
|
||||
if (seat.focused != .view) return;
|
||||
if (seat.focused.view.pending.float or seat.focused.view.pending.fullscreen) return;
|
||||
|
||||
const direction = std.meta.stringToEnum(Direction, args[1]) orelse return Error.InvalidDirection;
|
||||
|
||||
const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", seat.focused.view);
|
||||
const output = seat.focused_output;
|
||||
var it = ViewStack(View).iter(
|
||||
focused_node,
|
||||
if (direction == .next) .forward else .reverse,
|
||||
output.pending.tags,
|
||||
filter,
|
||||
);
|
||||
var it_wrap = ViewStack(View).iter(
|
||||
if (direction == .next) output.views.first else output.views.last,
|
||||
if (direction == .next) .forward else .reverse,
|
||||
output.pending.tags,
|
||||
filter,
|
||||
);
|
||||
|
||||
// skip the first node which is focused_node
|
||||
_ = it.next().?;
|
||||
|
||||
const to_swap = @fieldParentPtr(
|
||||
ViewStack(View).Node,
|
||||
"view",
|
||||
// Wrap around if needed
|
||||
if (it.next()) |next| next else it_wrap.next().?,
|
||||
);
|
||||
|
||||
// Dont swap when only the focused view is part of the layout
|
||||
if (focused_node == to_swap) {
|
||||
return;
|
||||
if (swapTarget(seat, output, direction)) |target| {
|
||||
assert(!target.pending.float);
|
||||
assert(!target.pending.fullscreen);
|
||||
seat.focused.view.pending_wm_stack_link.swapWith(&target.pending_wm_stack_link);
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
output.views.swap(focused_node, to_swap);
|
||||
|
||||
output.arrangeViews();
|
||||
server.root.startTransaction();
|
||||
}
|
||||
|
||||
fn filter(view: *View, filter_tags: u32) bool {
|
||||
return view.tree.node.enabled and !view.pending.float and
|
||||
!view.pending.fullscreen and view.pending.tags & filter_tags != 0;
|
||||
fn swapTarget(seat: *Seat, output: *Output, direction: Direction) ?*View {
|
||||
switch (direction) {
|
||||
inline else => |dir| {
|
||||
const it_dir = comptime switch (dir) {
|
||||
.next => .forward,
|
||||
.previous => .reverse,
|
||||
};
|
||||
var it = output.pending.wm_stack.iterator(it_dir);
|
||||
while (it.next()) |view| {
|
||||
if (view == seat.focused.view) break;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
// Return the next view in the stack matching the tags if any.
|
||||
while (it.next()) |view| {
|
||||
if (output.pending.tags & view.pending.tags != 0 and !view.pending.float) return view;
|
||||
}
|
||||
|
||||
// Wrap and return the first view in the stack matching the tags if
|
||||
// any is found before completing the loop back to the focused view.
|
||||
while (it.next()) |view| {
|
||||
if (view == seat.focused.view) return null;
|
||||
if (output.pending.tags & view.pending.tags != 0 and !view.pending.float) return view;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -30,12 +30,12 @@ pub fn setFocusedTags(
|
||||
out: *?[]const u8,
|
||||
) Error!void {
|
||||
const tags = try parseTags(args, out);
|
||||
if (seat.focused_output.pending.tags != tags) {
|
||||
seat.focused_output.previous_tags = seat.focused_output.pending.tags;
|
||||
seat.focused_output.pending.tags = tags;
|
||||
seat.focused_output.arrangeViews();
|
||||
const output = seat.focused_output orelse return;
|
||||
if (output.pending.tags != tags) {
|
||||
output.previous_tags = output.pending.tags;
|
||||
output.pending.tags = tags;
|
||||
seat.focus(null);
|
||||
server.root.startTransaction();
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ pub fn setViewTags(
|
||||
const view = seat.focused.view;
|
||||
view.pending.tags = tags;
|
||||
seat.focus(null);
|
||||
view.applyPending();
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,14 +70,13 @@ pub fn toggleFocusedTags(
|
||||
out: *?[]const u8,
|
||||
) Error!void {
|
||||
const tags = try parseTags(args, out);
|
||||
const output = seat.focused_output;
|
||||
const output = seat.focused_output orelse return;
|
||||
const new_focused_tags = output.pending.tags ^ tags;
|
||||
if (new_focused_tags != 0) {
|
||||
output.previous_tags = output.pending.tags;
|
||||
output.pending.tags = new_focused_tags;
|
||||
output.arrangeViews();
|
||||
seat.focus(null);
|
||||
server.root.startTransaction();
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +93,7 @@ pub fn toggleViewTags(
|
||||
const view = seat.focused.view;
|
||||
view.pending.tags = new_tags;
|
||||
seat.focus(null);
|
||||
view.applyPending();
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,13 +105,13 @@ pub fn focusPreviousTags(
|
||||
_: *?[]const u8,
|
||||
) Error!void {
|
||||
if (args.len > 1) return error.TooManyArguments;
|
||||
const previous_tags = seat.focused_output.previous_tags;
|
||||
if (seat.focused_output.pending.tags != previous_tags) {
|
||||
seat.focused_output.previous_tags = seat.focused_output.pending.tags;
|
||||
seat.focused_output.pending.tags = previous_tags;
|
||||
seat.focused_output.arrangeViews();
|
||||
const output = seat.focused_output orelse return;
|
||||
const previous_tags = output.previous_tags;
|
||||
if (output.pending.tags != previous_tags) {
|
||||
output.previous_tags = output.pending.tags;
|
||||
output.pending.tags = previous_tags;
|
||||
seat.focus(null);
|
||||
server.root.startTransaction();
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,12 +122,13 @@ pub fn sendToPreviousTags(
|
||||
_: *?[]const u8,
|
||||
) Error!void {
|
||||
if (args.len > 1) return error.TooManyArguments;
|
||||
const previous_tags = seat.focused_output.previous_tags;
|
||||
|
||||
const output = seat.focused_output orelse return;
|
||||
if (seat.focused == .view) {
|
||||
const view = seat.focused.view;
|
||||
view.pending.tags = previous_tags;
|
||||
view.pending.tags = output.previous_tags;
|
||||
seat.focus(null);
|
||||
view.applyPending();
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,13 +36,12 @@ pub fn toggleFloat(
|
||||
// If views are unarranged, don't allow changing the views float status.
|
||||
// It would just lead to confusing because this state would not be
|
||||
// visible immediately, only after a layout is connected.
|
||||
if (view.output.pending.layout == null)
|
||||
return;
|
||||
if (view.pending.output == null or view.pending.output.?.layout == null) return;
|
||||
|
||||
// Don't float fullscreen views
|
||||
if (view.pending.fullscreen) return;
|
||||
|
||||
view.pending.float = !view.pending.float;
|
||||
view.applyPending();
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,6 @@ pub fn toggleFullscreen(
|
||||
const view = seat.focused.view;
|
||||
|
||||
view.pending.fullscreen = !view.pending.fullscreen;
|
||||
view.applyPending();
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
|
@ -15,13 +15,13 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const server = &@import("../main.zig").server;
|
||||
|
||||
const Error = @import("../command.zig").Error;
|
||||
const Seat = @import("../Seat.zig");
|
||||
const View = @import("../View.zig");
|
||||
const ViewStack = @import("../view_stack.zig").ViewStack;
|
||||
|
||||
/// Bump the focused view to the top of the stack. If the view on the top of
|
||||
/// the stack is focused, bump the second view to the top.
|
||||
@ -32,33 +32,50 @@ pub fn zoom(
|
||||
) Error!void {
|
||||
if (args.len > 1) return Error.TooManyArguments;
|
||||
|
||||
if (seat.focused == .view) {
|
||||
// Only zoom views that are part of the layout
|
||||
if (seat.focused.view.pending.float or seat.focused.view.pending.fullscreen) return;
|
||||
if (seat.focused != .view) return;
|
||||
if (seat.focused.view.pending.float or seat.focused.view.pending.fullscreen) return;
|
||||
|
||||
// If the first view that is part of the layout is focused, zoom
|
||||
// the next view in the layout. Otherwise zoom the focused view.
|
||||
const output = seat.focused_output;
|
||||
var it = ViewStack(View).iter(output.views.first, .forward, output.pending.tags, filter);
|
||||
const layout_first = @fieldParentPtr(ViewStack(View).Node, "view", it.next().?);
|
||||
const output = seat.focused_output orelse return;
|
||||
|
||||
const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", seat.focused.view);
|
||||
const zoom_node = if (focused_node == layout_first)
|
||||
if (it.next()) |view| @fieldParentPtr(ViewStack(View).Node, "view", view) else null
|
||||
else
|
||||
focused_node;
|
||||
|
||||
if (zoom_node) |to_bump| {
|
||||
output.views.remove(to_bump);
|
||||
output.views.push(to_bump);
|
||||
seat.focus(&to_bump.view);
|
||||
output.arrangeViews();
|
||||
server.root.startTransaction();
|
||||
const layout_first = blk: {
|
||||
var it = output.pending.wm_stack.iterator(.forward);
|
||||
while (it.next()) |view| {
|
||||
if (view.pending.tags & output.pending.tags != 0 and !view.pending.float) break :blk view;
|
||||
} else {
|
||||
// If we are focusing a view that is not fullscreen or floating
|
||||
// it must be visible and in the layout.
|
||||
unreachable;
|
||||
}
|
||||
};
|
||||
|
||||
// If the first view that is part of the layout is focused, zoom
|
||||
// the next view in the layout if any. Otherwise zoom the focused view.
|
||||
const zoom_target = blk: {
|
||||
if (seat.focused.view == layout_first) {
|
||||
var it = output.pending.wm_stack.iterator(.forward);
|
||||
while (it.next()) |view| {
|
||||
if (view == seat.focused.view) break;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
while (it.next()) |view| {
|
||||
if (view.pending.tags & output.pending.tags != 0 and !view.pending.float) break :blk view;
|
||||
} else {
|
||||
break :blk null;
|
||||
}
|
||||
} else {
|
||||
break :blk seat.focused.view;
|
||||
}
|
||||
};
|
||||
|
||||
if (zoom_target) |target| {
|
||||
assert(!target.pending.float);
|
||||
assert(!target.pending.fullscreen);
|
||||
|
||||
target.pending_wm_stack_link.remove();
|
||||
output.pending.wm_stack.prepend(target);
|
||||
seat.focus(target);
|
||||
server.root.applyPending();
|
||||
}
|
||||
}
|
||||
|
||||
fn filter(view: *View, filter_tags: u32) bool {
|
||||
return view.tree.node.enabled and !view.pending.float and
|
||||
!view.pending.fullscreen and view.pending.tags & filter_tags != 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user