diff --git a/README.md b/README.md index 0121c6d..2b60008 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ installed: - [zig](https://ziglang.org/download/) 0.7.1 - wayland - wayland-protocols -- [wlroots](https://github.com/swaywm/wlroots) 0.12.0 +- [wlroots](https://github.com/swaywm/wlroots) 0.13.0 - xkbcommon - libevdev - pixman diff --git a/deps/zig-wlroots b/deps/zig-wlroots index 35e5676..6c62568 160000 --- a/deps/zig-wlroots +++ b/deps/zig-wlroots @@ -1 +1 @@ -Subproject commit 35e5676b1a77b2a44f370280b78b0d38598d97ee +Subproject commit 6c62568adfe92a1852566b7d091e99f4b28cbabb diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml index fa67001..d62fd51 100644 --- a/protocol/wlr-layer-shell-unstable-v1.xml +++ b/protocol/wlr-layer-shell-unstable-v1.xml @@ -25,7 +25,7 @@ THIS SOFTWARE. - + Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and @@ -47,6 +47,12 @@ or manipulate a buffer prior to the first layer_surface.configure call must also be treated as errors. + After creating a layer_surface object and setting it up, the client + must perform an initial commit without any buffer attached. + The compositor will reply with a layer_surface.configure event. + The client must acknowledge it and is then allowed to attach a buffer + to map the surface. + You may pass NULL for output to allow the compositor to decide which output to use. Generally this will be the one that the user most recently interacted with. @@ -94,7 +100,7 @@ - + An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like @@ -103,6 +109,14 @@ Layer surface state (layer, size, anchor, exclusive zone, margin, interactivity) is double-buffered, and will be applied at the time wl_surface.commit of the corresponding wl_surface is called. + + Attaching a null buffer to a layer surface unmaps it. + + Unmapping a layer_surface means that the surface cannot be shown by the + compositor until it is explicitly mapped again. The layer_surface + returns to the state it had right after layer_shell.get_layer_surface. + The client can re-map the surface by performing a commit without any + buffer attached, waiting for a configure event and handling it as usual. @@ -189,21 +203,85 @@ + + + Types of keyboard interaction possible for layer shell surfaces. The + rationale for this is twofold: (1) some applications are not interested + in keyboard events and not allowing them to be focused can improve the + desktop experience; (2) some applications will want to take exclusive + keyboard focus. + + + + + This value indicates that this surface is not interested in keyboard + events and the compositor should never assign it the keyboard focus. + + This is the default value, set for newly created layer shell surfaces. + + This is useful for e.g. desktop widgets that display information or + only have interaction with non-keyboard input devices. + + + + + Request exclusive keyboard focus if this surface is above the shell surface layer. + + For the top and overlay layers, the seat will always give + exclusive keyboard focus to the top-most layer which has keyboard + interactivity set to exclusive. If this layer contains multiple + surfaces with keyboard interactivity set to exclusive, the compositor + determines the one receiving keyboard events in an implementation- + defined manner. In this case, no guarantee is made when this surface + will receive keyboard focus (if ever). + + For the bottom and background layers, the compositor is allowed to use + normal focus semantics. + + This setting is mainly intended for applications that need to ensure + they receive all keyboard events, such as a lock screen or a password + prompt. + + + + + This requests the compositor to allow this surface to be focused and + unfocused by the user in an implementation-defined manner. The user + should be able to unfocus this surface even regardless of the layer + it is on. + + Typically, the compositor will want to use its normal mechanism to + manage keyboard focus between layer shell surfaces with this setting + and regular toplevels on the desktop layer (e.g. click to focus). + Nevertheless, it is possible for a compositor to require a special + interaction to focus or unfocus layer shell surfaces (e.g. requiring + a click even if focus follows the mouse normally, or providing a + keybinding to switch focus between layers). + + This setting is mainly intended for desktop shell components (e.g. + panels) that allow keyboard interaction. Using this option can allow + implementing a desktop shell that can be fully usable without the + mouse. + + + + - Set to 1 to request that the seat send keyboard events to this layer - surface. For layers below the shell surface layer, the seat will use - normal focus semantics. For layers above the shell surface layers, the - seat will always give exclusive keyboard focus to the top-most layer - which has keyboard interactivity set to true. + Set how keyboard events are delivered to this surface. By default, + layer shell surfaces do not receive keyboard events; this request can + be used to change this. + + This setting is inherited by child surfaces set by the get_popup + request. Layer surfaces receive pointer, touch, and tablet events normally. If you do not want to receive them, set the input region on your surface to an empty region. - Events is double-buffered, see wl_surface.commit. + Keyboard interactivity is double-buffered, see wl_surface.commit. - + @@ -288,6 +366,7 @@ + diff --git a/river/Cursor.zig b/river/Cursor.zig index d759dc7..081da03 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -239,7 +239,7 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P // give it keyboard focus. if (surface.isLayerSurface()) { const wlr_layer_surface = wlr.LayerSurfaceV1.fromWlrSurface(surface); - if (wlr_layer_surface.current.keyboard_interactive) { + if (wlr_layer_surface.current.keyboard_interactive == .exclusive) { const layer_surface = @intToPtr(*LayerSurface, wlr_layer_surface.data); self.seat.focusOutput(layer_surface.output); self.seat.setFocusRaw(.{ .layer = layer_surface }); diff --git a/river/Output.zig b/river/Output.zig index 3de9631..166b3c0 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -348,7 +348,7 @@ pub fn arrangeLayers(self: *Self) void { var it = self.getLayer(layer).last; while (it) |node| : (it = node.prev) { const layer_surface = &node.data; - if (layer_surface.wlr_layer_surface.current.keyboard_interactive) { + if (layer_surface.wlr_layer_surface.current.keyboard_interactive == .exclusive) { break :outer layer_surface; } } @@ -368,7 +368,7 @@ pub fn arrangeLayers(self: *Self) void { } else if (seat.focused == .layer) { // If the seat is currently focusing a layer without keyboard // interactivity, stop focusing that layer. - if (!seat.focused.layer.wlr_layer_surface.current.keyboard_interactive) { + if (seat.focused.layer.wlr_layer_surface.current.keyboard_interactive != .exclusive) { seat.setFocusRaw(.{ .none = {} }); seat.focus(null); } diff --git a/river/Root.zig b/river/Root.zig index 4c70a58..e05495f 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -517,14 +517,10 @@ fn applyHeadToOutput(head: *wlr.OutputConfigurationV1.Head, wlr_output: *wlr.Out if (head.state.mode) |mode| { wlr_output.setMode(mode); } else { - std.log.scoped(.output_manager).info("custom modes are not supported until the next wlroots release: ignoring", .{}); - // TODO(wlroots) uncomment the following lines when wlroots 0.13.0 is released - // See https://github.com/swaywm/wlroots/pull/2517 - //const custom_mode = &head.state.custom_mode; - //wlr_output.setCustomMode(custom_mode.width, custom_mode.height, custom_mode.refresh); + const custom_mode = &head.state.custom_mode; + wlr_output.setCustomMode(custom_mode.width, custom_mode.height, custom_mode.refresh); } - // TODO(wlroots) Figure out if this conversion is needed or if that is a bug in wlroots - wlr_output.setScale(@floatCast(f32, head.state.scale)); + wlr_output.setScale(head.state.scale); wlr_output.setTransform(head.state.transform); } } diff --git a/river/Server.zig b/river/Server.zig index 5a6377e..5cc612b 100644 --- a/river/Server.zig +++ b/river/Server.zig @@ -78,7 +78,7 @@ pub fn init(self: *Self) !void { errdefer self.sigterm_source.remove(); // This frees itself when the wl.Server is destroyed - self.backend = try wlr.Backend.autocreate(self.wl_server, null); + self.backend = try wlr.Backend.autocreate(self.wl_server); // This backend is used to create a noop output for use when no actual // outputs are available. This frees itself when the wl.Server is destroyed. @@ -140,7 +140,6 @@ pub fn deinit(self: *Self) void { self.root.deinit(); - self.noop_backend.destroy(); self.wl_server.destroy(); self.input_manager.deinit(); diff --git a/river/View.zig b/river/View.zig index 82b0b40..91f7e1d 100644 --- a/river/View.zig +++ b/river/View.zig @@ -333,14 +333,14 @@ pub fn close(self: Self) void { } } -pub inline fn forEachPopup( +pub inline fn forEachPopupSurface( self: Self, comptime T: type, iterator: fn (surface: *wlr.Surface, sx: c_int, sy: c_int, data: T) callconv(.C) void, user_data: T, ) void { switch (self.impl) { - .xdg_toplevel => |xdg_toplevel| xdg_toplevel.forEachPopup(T, iterator, user_data), + .xdg_toplevel => |xdg_toplevel| xdg_toplevel.forEachPopupSurface(T, iterator, user_data), .xwayland_view => {}, } } diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig index f4385e7..11389a8 100644 --- a/river/XdgToplevel.zig +++ b/river/XdgToplevel.zig @@ -104,13 +104,13 @@ pub fn close(self: Self) void { self.xdg_surface.role_data.toplevel.sendClose(); } -pub inline fn forEachPopup( +pub inline fn forEachPopupSurface( self: Self, comptime T: type, iterator: fn (surface: *wlr.Surface, sx: c_int, sy: c_int, data: T) callconv(.C) void, user_data: T, ) void { - self.xdg_surface.forEachPopup(T, iterator, user_data); + self.xdg_surface.forEachPopupSurface(T, iterator, user_data); } /// Return the surface at output coordinates ox, oy and set sx, sy to the diff --git a/river/render.zig b/river/render.zig index 1878c6b..578a685 100644 --- a/river/render.zig +++ b/river/render.zig @@ -54,7 +54,7 @@ pub fn renderOutput(output: *Output) void { output.wlr_output.attachRender(null) catch return; - renderer.begin(output.wlr_output.width, output.wlr_output.height); + renderer.begin(@intCast(u32, output.wlr_output.width), @intCast(u32, output.wlr_output.height)); // Find the first visible fullscreen view in the stack if there is one var it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, renderFilter); @@ -130,18 +130,6 @@ pub fn renderOutput(output: *Output) void { // on-screen. renderer.end(); - // TODO(wlroots): remove this with the next release. It is here due to - // a wlroots bug in the screencopy damage implementation - { - var w: c_int = undefined; - var h: c_int = undefined; - output.wlr_output.transformedResolution(&w, &h); - var damage: pixman.Region32 = undefined; - damage.init(); - _ = damage.unionRect(&damage, 0, 0, @intCast(c_uint, w), @intCast(c_uint, h)); - output.wlr_output.setDamage(&damage); - } - // TODO: handle failure output.wlr_output.commit() catch log.err("output commit failed for {}", .{output.wlr_output.name}); @@ -178,7 +166,7 @@ fn renderLayer( renderSurfaceIterator, &rdata, ), - .popups => layer_surface.wlr_layer_surface.forEachPopup( + .popups => layer_surface.wlr_layer_surface.forEachPopupSurface( *SurfaceRenderData, renderSurfaceIterator, &rdata, @@ -227,24 +215,7 @@ fn renderViewPopups(output: *const Output, view: *View, now: *os.timespec) void .when = now, .opacity = view.opacity, }; - view.forEachPopup(*SurfaceRenderData, renderPopupSurfaceIterator, &rdata); -} - -// TODO(wlroots): replace with wlr_xdg_surface_for_each_popup_surface() -fn renderPopupSurfaceIterator( - surface: *wlr.Surface, - surface_x: c_int, - surface_y: c_int, - rdata: *SurfaceRenderData, -) callconv(.C) void { - var new_rdata = SurfaceRenderData{ - .output = rdata.output, - .output_x = rdata.output_x + surface_x, - .output_y = rdata.output_y + surface_y, - .when = rdata.when, - .opacity = rdata.opacity, - }; - surface.forEachSurface(*SurfaceRenderData, renderSurfaceIterator, &new_rdata); + view.forEachPopupSurface(*SurfaceRenderData, renderSurfaceIterator, &rdata); } fn renderDragIcons(output: *const Output, now: *os.timespec) void {