Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
river: remove focus stack
protocol/river-window-management-v1.xml | 4 ++
river/Cursor.zig | 14 -------
river/LayerSurface.zig | 3 +-
river/Output.zig | 15 +------
river/Root.zig | 72 ++++++---------------------------
river/Seat.zig | 40 +-----------------
river/Server.zig | 8 ++--
river/View.zig | 20 +--------
8 files changed, 28 insertions(+), 148 deletions(-)
diff --git a/protocol/river-window-management-v1.xml b/protocol/river-window-management-v1.xml
index fd6ed4b..32a1993 100644
--- a/protocol/river-window-management-v1.xml
+++ b/protocol/river-window-management-v1.xml
@@ -925,6 +925,10 @@
<description summary="a extension of wl_seat for window management">
This object extends the wl_seat object with the features necessary for
window management.
+
+ TODO:
+ - touch input
+ - tablet input
</description>
<request name="destroy" type="destructor">
diff --git a/river/Cursor.zig b/river/Cursor.zig
index c5ee407..6e1778c 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -373,8 +373,6 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
},
};
}
- } else {
- cursor.updateOutputFocus(cursor.wlr_cursor.x, cursor.wlr_cursor.y);
}
server.root.applyPending();
@@ -387,7 +385,6 @@ fn updateKeyboardFocus(cursor: Cursor, result: Root.AtResult) void {
cursor.seat.focus(view);
},
.layer_surface => |layer_surface| {
- cursor.seat.focusOutput(layer_surface.output);
// If a keyboard inteactive layer surface has been clicked on,
// give it keyboard focus.
if (layer_surface.wlr_layer_surface.current.keyboard_interactive != .none) {
@@ -405,15 +402,6 @@ fn updateKeyboardFocus(cursor: Cursor, result: Root.AtResult) void {
}
}
-/// Focus the output at the given layout coordinates, if any
-/// Requires a call to Root.applyPending()
-fn updateOutputFocus(cursor: Cursor, lx: f64, ly: f64) void {
- if (server.root.output_layout.outputAt(lx, ly)) |wlr_output| {
- const output: *Output = @ptrFromInt(wlr_output.data);
- cursor.seat.focusOutput(output);
- }
-}
-
fn handlePinchBegin(
listener: *wl.Listener(*wlr.Pointer.event.PinchBegin),
event: *wlr.Pointer.event.PinchBegin,
@@ -519,8 +507,6 @@ fn handleTouchDown(
result.sy,
);
}
- } else {
- cursor.updateOutputFocus(lx, ly);
}
server.root.applyPending();
diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig
index d20d159..af97e46 100644
--- a/river/LayerSurface.zig
+++ b/river/LayerSurface.zig
@@ -176,7 +176,8 @@ fn handleKeyboardInteractiveExclusive(output: *Output, consider: ?*LayerSurface)
while (it) |node| : (it = node.next) {
const seat = &node.data;
- if (seat.focused_output == output) {
+ if (true) @panic("TODO");
+ if (false) {
if (to_focus) |s| {
// If we found a surface on the output that requires focus, grab the focus of all
// seats that are focusing that output.
diff --git a/river/Output.zig b/river/Output.zig
index 2e561fe..4072609 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -39,11 +39,6 @@ const Config = @import("Config.zig");
const log = std.log.scoped(.output);
pub const PendingState = struct {
- /// The stack of views in focus/rendering order.
- ///
- /// This list is used to update the rendering order of nodes in the scene
- /// graph when the pending state is committed.
- focus_stack: wl.list.Head(View, .pending_focus_stack_link),
/// The stack of views acted upon by window management commands such
/// as focus-view, zoom, etc.
wm_stack: wl.list.Head(View, .pending_wm_stack_link),
@@ -127,8 +122,6 @@ pending: PendingState,
/// This state is immutable until all clients have replied and the transaction
/// is completed, at which point this inflight state is copied to current.
inflight: struct {
- /// See pending.focus_stack
- focus_stack: wl.list.Head(View, .inflight_focus_stack_link),
/// See pending.wm_stack
wm_stack: wl.list.Head(View, .inflight_wm_stack_link),
/// The view to be made fullscreen, if any.
@@ -136,7 +129,7 @@ inflight: struct {
},
/// The current state represented by the scene graph.
-/// There is no need to have a current focus_stack/wm_stack copy as this
+/// There is no need to have a current wm_stack copy as this
/// information is transferred from the inflight state to the scene graph
/// as an inflight transaction completes.
current: struct {
@@ -229,11 +222,9 @@ pub fn create(wlr_output: *wlr.Output) !void {
.popups = try normal_content.createSceneTree(),
},
.pending = .{
- .focus_stack = undefined,
.wm_stack = undefined,
},
.inflight = .{
- .focus_stack = undefined,
.wm_stack = undefined,
},
.usable_box = .{
@@ -245,9 +236,7 @@ pub fn create(wlr_output: *wlr.Output) !void {
};
wlr_output.data = @intFromPtr(output);
- output.pending.focus_stack.init();
output.pending.wm_stack.init();
- output.inflight.focus_stack.init();
output.inflight.wm_stack.init();
_ = try output.layers.fullscreen.createSceneRect(width, height, &[_]f32{ 0, 0, 0, 1.0 });
@@ -355,9 +344,7 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
// Remove the destroyed output from root if it wasn't already removed
server.root.deactivateOutput(output);
- assert(output.pending.focus_stack.empty());
assert(output.pending.wm_stack.empty());
- assert(output.inflight.focus_stack.empty());
assert(output.inflight.wm_stack.empty());
output.all_link.remove();
diff --git a/river/Root.zig b/river/Root.zig
index ad6afa7..82e9839 100644
--- a/river/Root.zig
+++ b/river/Root.zig
@@ -58,12 +58,10 @@ hidden: struct {
tree: *wlr.SceneTree,
pending: struct {
- focus_stack: wl.list.Head(View, .pending_focus_stack_link),
wm_stack: wl.list.Head(View, .pending_wm_stack_link),
},
inflight: struct {
- focus_stack: wl.list.Head(View, .inflight_focus_stack_link),
wm_stack: wl.list.Head(View, .inflight_wm_stack_link),
},
},
@@ -144,16 +142,13 @@ pub fn init(root: *Root) !void {
.hidden = .{
.tree = hidden_tree,
.pending = .{
- .focus_stack = undefined,
.wm_stack = undefined,
},
.inflight = .{
- .focus_stack = undefined,
.wm_stack = undefined,
},
},
.fallback_pending = .{
- .focus_stack = undefined,
.wm_stack = undefined,
},
.views = undefined,
@@ -168,12 +163,9 @@ pub fn init(root: *Root) !void {
.gamma_control_manager = try wlr.GammaControlManagerV1.create(server.wl_server),
.transaction_timeout = transaction_timeout,
};
- root.hidden.pending.focus_stack.init();
root.hidden.pending.wm_stack.init();
- root.hidden.inflight.focus_stack.init();
root.hidden.inflight.wm_stack.init();
- root.fallback_pending.focus_stack.init();
root.fallback_pending.wm_stack.init();
root.views.init();
@@ -268,7 +260,7 @@ pub fn deactivateOutput(root: *Root, output: *Output) void {
output.active_link.init();
{
- var it = output.inflight.focus_stack.safeIterator(.forward);
+ var it = output.inflight.wm_stack.safeIterator(.forward);
while (it.next()) |view| {
view.inflight.output = null;
view.current.output = null;
@@ -276,9 +268,6 @@ pub fn deactivateOutput(root: *Root, output: *Output) void {
view.tree.node.reparent(root.hidden.tree);
view.popup_tree.node.reparent(root.hidden.tree);
- view.inflight_focus_stack_link.remove();
- view.inflight_focus_stack_link.init();
-
view.inflight_wm_stack_link.remove();
view.inflight_wm_stack_link.init();
@@ -303,12 +292,11 @@ pub fn deactivateOutput(root: *Root, output: *Output) void {
break :blk it.next();
};
if (fallback_output) |fallback| {
- var it = output.pending.focus_stack.safeIterator(.reverse);
+ var it = output.pending.wm_stack.safeIterator(.reverse);
while (it.next()) |view| view.setPendingOutput(fallback);
} else {
- var it = output.pending.focus_stack.iterator(.forward);
+ var it = output.pending.wm_stack.iterator(.forward);
while (it.next()) |view| view.pending.output = null;
- root.fallback_pending.focus_stack.prependList(&output.pending.focus_stack);
root.fallback_pending.wm_stack.prependList(&output.pending.wm_stack);
}
@@ -324,15 +312,6 @@ pub fn deactivateOutput(root: *Root, output: *Output) void {
}
}
- // If any seat has the removed output focused, focus the fallback one
- var seat_it = server.input_manager.seats.first;
- while (seat_it) |seat_node| : (seat_it = seat_node.next) {
- const seat = &seat_node.data;
- if (seat.focused_output == output) {
- seat.focusOutput(fallback_output);
- }
- }
-
// We must call reconfigureDevices here to unmap devices that might be mapped to this output
// in order to prevent a segfault in wlroots.
server.input_manager.reconfigureDevices();
@@ -372,14 +351,6 @@ pub fn activateOutput(root: *Root, output: *Output) void {
var it = root.fallback_pending.wm_stack.safeIterator(.reverse);
while (it.next()) |view| view.setPendingOutput(output);
}
- {
- // Focus the new output with all seats
- var it = server.input_manager.seats.first;
- while (it) |seat_node| : (it = seat_node.next) {
- const seat = &seat_node.data;
- seat.focusOutput(output);
- }
- }
} else {
// Otherwise check if any views were previously evacuated from an output
// with the same (connector-)name and move them back.
@@ -395,7 +366,6 @@ pub fn activateOutput(root: *Root, output: *Output) void {
}
}
}
- assert(root.fallback_pending.focus_stack.empty());
assert(root.fallback_pending.wm_stack.empty());
// Enforce map-to-output configuration for the newly active output.
@@ -424,18 +394,10 @@ pub fn applyPending(root: *Root) void {
root.pending_state_dirty = false;
{
- var it = root.hidden.pending.focus_stack.iterator(.forward);
+ var it = root.hidden.pending.wm_stack.iterator(.forward);
while (it.next()) |view| {
assert(view.pending.output == null);
view.inflight.output = null;
- view.inflight_focus_stack_link.remove();
- root.hidden.inflight.focus_stack.append(view);
- }
- }
-
- {
- var it = root.hidden.pending.wm_stack.iterator(.forward);
- while (it.next()) |view| {
view.inflight_wm_stack_link.remove();
root.hidden.inflight.wm_stack.append(view);
}
@@ -448,7 +410,7 @@ pub fn applyPending(root: *Root) void {
// recently focused view that requests fullscreen is given fullscreen.
output.inflight.fullscreen = null;
{
- var it = output.pending.focus_stack.iterator(.forward);
+ var it = output.pending.wm_stack.iterator(.forward);
while (it.next()) |view| {
assert(view.pending.output == output);
@@ -465,18 +427,10 @@ pub fn applyPending(root: *Root) void {
output.inflight.fullscreen = view;
}
- view.inflight_focus_stack_link.remove();
- output.inflight.focus_stack.append(view);
-
- view.inflight = view.pending;
- }
- }
-
- {
- var it = output.pending.wm_stack.iterator(.forward);
- while (it.next()) |view| {
view.inflight_wm_stack_link.remove();
output.inflight.wm_stack.append(view);
+
+ view.inflight = view.pending;
}
}
}
@@ -513,8 +467,8 @@ fn sendConfigures(root: *Root) void {
// Iterate over all views of all outputs
var output_it = root.active_outputs.iterator(.forward);
while (output_it.next()) |output| {
- var focus_stack_it = output.inflight.focus_stack.iterator(.forward);
- while (focus_stack_it.next()) |view| {
+ var wm_stack_it = output.inflight.wm_stack.iterator(.forward);
+ while (wm_stack_it.next()) |view| {
assert(!view.inflight_transaction);
view.inflight_transaction = true;
@@ -573,7 +527,7 @@ fn commitTransaction(root: *Root) void {
std.log.scoped(.transaction).debug("commiting transaction", .{});
{
- var it = root.hidden.inflight.focus_stack.safeIterator(.forward);
+ var it = root.hidden.inflight.wm_stack.safeIterator(.forward);
while (it.next()) |view| {
assert(view.inflight.output == null);
view.current.output = null;
@@ -585,8 +539,8 @@ fn commitTransaction(root: *Root) void {
var output_it = root.active_outputs.iterator(.forward);
while (output_it.next()) |output| {
- var focus_stack_it = output.inflight.focus_stack.iterator(.forward);
- while (focus_stack_it.next()) |view| {
+ var wm_stack_it = output.inflight.wm_stack.iterator(.forward);
+ while (wm_stack_it.next()) |view| {
assert(view.inflight.output == output);
if (view.current.output != view.inflight.output or
@@ -626,7 +580,7 @@ fn commitTransaction(root: *Root) void {
{
// This must be done after updating cursor state in case the view was the target of move/resize.
- var it = root.hidden.inflight.focus_stack.safeIterator(.forward);
+ var it = root.hidden.inflight.wm_stack.safeIterator(.forward);
while (it.next()) |view| {
view.dropSavedSurfaceTree();
if (view.destroying) view.destroy(.assert);
diff --git a/river/Seat.zig b/river/Seat.zig
index b01b5fe..7d5ec0f 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -78,9 +78,6 @@ repeating_mapping: ?*const Mapping = null,
keyboard_groups: std.TailQueue(KeyboardGroup) = .{},
-/// Currently focused output. Null only when there are no outputs at all.
-focused_output: ?*Output = null,
-
focused: FocusTarget = .none,
/// The currently in progress drag operation type.
@@ -145,12 +142,7 @@ pub fn deinit(seat: *Seat) void {
/// Set the current focus. If a visible view is passed it will be focused.
/// If null is passed, the top view in the stack of the focused output will be focused.
/// Requires a call to Root.applyPending()
-pub fn focus(seat: *Seat, _target: ?*View) void {
- var target = _target;
-
- // Don't change focus if there are no outputs.
- if (seat.focused_output == null) return;
-
+pub fn focus(seat: *Seat, target: ?*View) void {
// Views may not receive focus while locked.
if (server.lock_manager.state != .unlocked) return;
@@ -172,25 +164,8 @@ pub fn focus(seat: *Seat, _target: ?*View) void {
}
}
- if (target) |view| {
- if (view.pending.output.? != seat.focused_output.?) {
- // If the view is not on the currently focused output, focus it
- seat.focusOutput(view.pending.output.?);
- }
- }
-
- // If null, set the target to the first currently visible view in the focus stack if any
- if (target == null) {
- var it = seat.focused_output.?.pending.focus_stack.iterator(.forward);
- target = while (it.next()) |view| {
- break view;
- } else null;
- }
-
// Focus the target view or clear the focus if target is null
if (target) |view| {
- view.pending_focus_stack_link.remove();
- seat.focused_output.?.pending.focus_stack.prepend(view);
seat.setFocusRaw(.{ .view = view });
} else {
seat.setFocusRaw(.{ .none = {} });
@@ -222,14 +197,10 @@ pub fn setFocusRaw(seat: *Seat, new_focus: FocusTarget) void {
switch (new_focus) {
.view => |target_view| {
assert(server.lock_manager.state != .locked);
- assert(seat.focused_output == target_view.pending.output);
target_view.pending.focus += 1;
target_view.pending.urgent = false;
},
- .layer => |target_layer| {
- assert(server.lock_manager.state != .locked);
- assert(seat.focused_output == target_layer.output);
- },
+ .layer => assert(server.lock_manager.state != .locked),
.lock_surface => assert(server.lock_manager.state != .unlocked),
.override_redirect, .none => {},
}
@@ -291,13 +262,6 @@ fn keyboardNotifyEnter(seat: *Seat, wlr_surface: *wlr.Surface) void {
}
}
-/// Focus the given output, notifying any listening clients of the change.
-pub fn focusOutput(seat: *Seat, output: ?*Output) void {
- if (seat.focused_output == output) return;
-
- seat.focused_output = output;
-}
-
pub fn handleActivity(seat: Seat) void {
server.input_manager.idle_notifier.notifyActivity(seat.wlr_seat);
}
diff --git a/river/Server.zig b/river/Server.zig
index a9acf82..d46cb4f 100644
--- a/river/Server.zig
+++ b/river/Server.zig
@@ -344,9 +344,7 @@ fn handleNewToplevelDecoration(
XdgDecoration.init(wlr_decoration);
}
-fn handleNewLayerSurface(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void {
- const server: *Server = @fieldParentPtr("new_layer_surface", listener);
-
+fn handleNewLayerSurface(_: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void {
log.debug(
"new layer surface: namespace {s}, layer {s}, anchor {b:0>4}, size {},{}, margin {},{},{},{}, exclusive_zone {}",
.{
@@ -366,7 +364,9 @@ fn handleNewLayerSurface(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_
// If the new layer surface does not have an output assigned to it, use the
// first output or close the surface if none are available.
if (wlr_layer_surface.output == null) {
- const output = server.input_manager.defaultSeat().focused_output orelse {
+ if (true) @panic("TODO");
+
+ const output = null orelse {
log.err("no output available for layer surface '{s}'", .{wlr_layer_surface.namespace});
wlr_layer_surface.destroy();
return;
diff --git a/river/View.zig b/river/View.zig
index 74353df..67d147f 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -142,14 +142,12 @@ destroying: bool = false,
/// Any time pending state is modified Root.applyPending() must be called
/// before yielding back to the event loop.
pending: State = .{},
-pending_focus_stack_link: wl.list.Link,
pending_wm_stack_link: wl.list.Link,
/// The state most recently sent to the layout generator and clients.
/// This state is immutable until all clients have replied and the transaction
/// is completed, at which point this inflight state is copied to current.
inflight: State = .{},
-inflight_focus_stack_link: wl.list.Link,
inflight_wm_stack_link: wl.list.Link,
/// The current state represented by the scene graph.
@@ -195,15 +193,11 @@ pub fn create(impl: Impl) error{OutOfMemory}!*View {
.popup_tree = popup_tree,
.pending_wm_stack_link = undefined,
- .pending_focus_stack_link = undefined,
.inflight_wm_stack_link = undefined,
- .inflight_focus_stack_link = undefined,
};
server.root.views.prepend(view);
- server.root.hidden.pending.focus_stack.prepend(view);
server.root.hidden.pending.wm_stack.prepend(view);
- server.root.hidden.inflight.focus_stack.prepend(view);
server.root.hidden.inflight.wm_stack.prepend(view);
view.tree.node.setEnabled(false);
@@ -233,9 +227,7 @@ pub fn destroy(view: *View, when: enum { lazy, assert }) void {
view.popup_tree.node.destroy();
view.link.remove();
- view.pending_focus_stack_link.remove();
view.pending_wm_stack_link.remove();
- view.inflight_focus_stack_link.remove();
view.inflight_wm_stack_link.remove();
if (view.output_before_evac) |name| util.gpa.free(name);
@@ -504,10 +496,8 @@ fn saveSurfaceTreeIter(
pub fn setPendingOutput(view: *View, output: *Output) void {
view.pending.output = output;
view.pending_wm_stack_link.remove();
- view.pending_focus_stack_link.remove();
output.pending.wm_stack.prepend(view);
- output.pending.focus_stack.prepend(view);
if (view.pending.fullscreen) {
view.pending.box = .{ .x = 0, .y = 0, .width = undefined, .height = undefined };
@@ -566,7 +556,8 @@ pub fn map(view: *View) !void {
view.foreign_toplevel_handle.map();
- const output = server.input_manager.defaultSeat().focused_output;
+ if (true) @panic("TODO");
+ const output = undefined;
if (output) |o| {
// Center the initial pending box on the output
@@ -583,16 +574,11 @@ pub fn map(view: *View) !void {
log.debug("no output available for newly mapped view, adding to fallback stacks", .{});
view.pending_wm_stack_link.remove();
- view.pending_focus_stack_link.remove();
server.root.fallback_pending.wm_stack.prepend(view);
- server.root.fallback_pending.focus_stack.prepend(view);
view.inflight_wm_stack_link.remove();
view.inflight_wm_stack_link.init();
-
- view.inflight_focus_stack_link.remove();
- view.inflight_focus_stack_link.init();
}
view.float_box = view.pending.box;
@@ -608,9 +594,7 @@ pub fn unmap(view: *View) void {
{
view.pending.output = null;
- view.pending_focus_stack_link.remove();
view.pending_wm_stack_link.remove();
- server.root.hidden.pending.focus_stack.prepend(view);
server.root.hidden.pending.wm_stack.prepend(view);
}