build: update to wlroots 0.18.0

This commit is contained in:
Isaac Freund 2024-07-16 14:24:22 +02:00
parent ccd676e5a9
commit 99ef96a389
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
21 changed files with 128 additions and 143 deletions

View File

@ -28,15 +28,17 @@ sources:
tasks:
- install_deps: |
cd wayland
git checkout 1.22.0
git checkout 1.23.0
meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
sudo ninja -C build install
cd ..
cd wlroots
git checkout 0.17.2
meson setup build --auto-features=enabled -Drenderers=gles2 -Dexamples=false \
-Dwerror=false -Db_ndebug=false -Dxcb-errors=disabled --prefix /usr
git checkout 0.18.0
meson setup build --auto-features=enabled -Drenderers=gles2 \
-Dcolor-management=disabled -Dlibliftoff=disabled \
-Dexamples=false -Dwerror=false -Db_ndebug=false \
-Dxcb-errors=disabled --prefix /usr
sudo ninja -C build/ install
cd ..

View File

@ -26,15 +26,17 @@ sources:
tasks:
- install_deps: |
cd wayland
git checkout 1.22.0
git checkout 1.23.0
meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
sudo ninja -C build install
cd ..
cd wlroots
git checkout 0.17.2
meson setup build --auto-features=enabled -Drenderers=gles2 -Dexamples=false \
-Dwerror=false -Db_ndebug=false --prefix /usr
git checkout 0.18.0
meson setup build --auto-features=enabled -Drenderers=gles2 \
-Dcolor-management=disabled -Dlibliftoff=disabled \
-Dexamples=false -Dwerror=false -Db_ndebug=false \
-Dxcb-errors=disabled --prefix /usr
sudo ninja -C build/ install
cd ..

View File

@ -31,15 +31,17 @@ sources:
tasks:
- install_deps: |
cd wayland
git checkout 1.22.0
git checkout 1.23.0
meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
sudo ninja -C build install
cd ..
cd wlroots
git checkout 0.17.2
meson setup build --auto-features=enabled -Drenderers=gles2 -Dexamples=false \
-Dwerror=false -Db_ndebug=false --prefix /usr
git checkout 0.18.0
meson setup build --auto-features=enabled -Drenderers=gles2 \
-Dcolor-management=disabled -Dlibliftoff=disabled \
-Dexamples=false -Dwerror=false -Db_ndebug=false \
-Dxcb-errors=disabled --prefix /usr
sudo ninja -C build/ install
cd ..

View File

@ -60,7 +60,7 @@ distribution.
- [zig](https://ziglang.org/download/) 0.13
- wayland
- wayland-protocols
- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.17.2
- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.18
- xkbcommon
- libevdev
- pixman

View File

@ -146,7 +146,7 @@ pub fn build(b: *Build) !void {
// exposed to the wlroots module for @cImport() to work. This seems to be
// the best way to do so with the current std.Build API.
wlroots.resolved_target = target;
wlroots.linkSystemLibrary("wlroots", .{});
wlroots.linkSystemLibrary("wlroots-0.18", .{});
const flags = b.createModule(.{ .root_source_file = b.path("common/flags.zig") });
const globber = b.createModule(.{ .root_source_file = b.path("common/globber.zig") });
@ -167,7 +167,7 @@ pub fn build(b: *Build) !void {
river.linkSystemLibrary("libevdev");
river.linkSystemLibrary("libinput");
river.linkSystemLibrary("wayland-server");
river.linkSystemLibrary("wlroots");
river.linkSystemLibrary("wlroots-0.18");
river.linkSystemLibrary("xkbcommon");
river.linkSystemLibrary("pixman-1");

View File

@ -12,8 +12,8 @@
.hash = "1220687c8c47a48ba285d26a05600f8700d37fc637e223ced3aa8324f3650bf52242",
},
.@"zig-wlroots" = .{
.url = "https://codeberg.org/ifreund/zig-wlroots/archive/084736cd92364b5fa7d8161611d085ce272fa707.tar.gz",
.hash = "12208383c1cf42e9b932b90f68cd4f378582cf966355a6377fd8f913852e7bc2d7c6",
.url = "https://codeberg.org/ifreund/zig-wlroots/archive/ae6151f22ceb4ccd7efb1291dea573785918a7ec.tar.gz",
.hash = "12204d99aebfbf88f1ff3ab197362937b3d4bef4f45fde9c4ee0d569e095a2a25889",
},
.@"zig-xkbcommon" = .{
.url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.2.0.tar.gz",

View File

@ -324,6 +324,7 @@ fn handleAxis(listener: *wl.Listener(*wlr.Pointer.event.Axis), event: *wlr.Point
math.maxInt(i32) / 2,
)),
event.source,
event.relative_direction,
);
}
@ -568,7 +569,7 @@ fn handleTouchUp(
cursor.seat.handleActivity();
if (cursor.touch_points.remove(event.touch_id)) {
cursor.seat.wlr_seat.touchNotifyUp(event.time_msec, event.touch_id);
_ = cursor.seat.wlr_seat.touchNotifyUp(event.time_msec, event.touch_id);
}
}
@ -582,32 +583,9 @@ fn handleTouchCancel(
cursor.touch_points.clearRetainingCapacity();
// We can't call touchNotifyCancel() from inside the loop over touch points as it also loops
// over touch points and may destroy multiple touch points in a single call.
//
// What we should do here is `while (touch_points.first()) |point| cancel()` but since the
// surface may be null we can't rely on the fact tha all touch points will be destroyed
// and risk an infinite loop if the surface of any wlr_touch_point is null.
//
// This is all really silly and totally unnecessary since all touchNotifyCancel() does with
// the surface argument is obtain a seat client and touch_point.seat_client is never null.
// TODO(wlroots) clean this up after the wlroots MR fixing this is merged:
// https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4613
// The upper bound of 32 comes from an implementation detail of libinput which uses
// a 32-bit integer as a map to keep track of touch points.
var surfaces: std.BoundedArray(*wlr.Surface, 32) = .{};
{
var it = cursor.seat.wlr_seat.touch_state.touch_points.iterator(.forward);
while (it.next()) |touch_point| {
if (touch_point.surface) |surface| {
surfaces.append(surface) catch break;
}
}
}
for (surfaces.slice()) |surface| {
cursor.seat.wlr_seat.touchNotifyCancel(surface);
const wlr_seat = cursor.seat.wlr_seat;
while (wlr_seat.touch_state.touch_points.first()) |touch_point| {
wlr_seat.touchNotifyCancel(touch_point.client);
}
}

View File

@ -232,7 +232,7 @@ pub const MapToOutput = struct {
};
switch (device.wlr_device.type) {
.pointer, .touch, .tablet_tool => {
.pointer, .touch, .tablet => {
log.debug("mapping input '{s}' -> '{s}'", .{
device.identifier,
if (wlr_output) |o| o.name else "<no output>",
@ -240,14 +240,14 @@ pub const MapToOutput = struct {
device.seat.cursor.wlr_cursor.mapInputToOutput(device.wlr_device, wlr_output);
if (device.wlr_device.type == .tablet_tool) {
if (device.wlr_device.type == .tablet) {
const tablet: *Tablet = @fieldParentPtr("device", device);
tablet.output_mapping = wlr_output;
}
},
// These devices do not support being mapped to outputs.
.keyboard, .tablet_pad, .switch_device => {},
.keyboard, .tablet_pad, .@"switch" => {},
}
}
};

View File

@ -24,6 +24,7 @@ const wl = @import("wayland").server.wl;
const globber = @import("globber");
const c = @import("c.zig");
const server = &@import("main.zig").server;
const util = @import("util.zig");
@ -52,19 +53,21 @@ config: struct {
link: wl.list.Link,
pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !void {
const device_type: []const u8 = switch (wlr_device.type) {
.switch_device => "switch",
.tablet_tool => "tablet",
else => @tagName(wlr_device.type),
};
var vendor: c_uint = 0;
var product: c_uint = 0;
if (wlr_device.getLibinputDevice()) |d| {
vendor = c.libinput_device_get_id_vendor(@ptrCast(d));
product = c.libinput_device_get_id_product(@ptrCast(d));
}
const identifier = try std.fmt.allocPrint(
util.gpa,
"{s}-{}-{}-{s}",
.{
device_type,
wlr_device.vendor,
wlr_device.product,
@tagName(wlr_device.type),
vendor,
product,
mem.trim(u8, mem.sliceTo(wlr_device.name orelse "unknown", 0), &ascii.whitespace),
},
);
@ -139,11 +142,11 @@ fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice)
device.deinit();
util.gpa.destroy(device);
},
.tablet_tool => {
.tablet => {
const tablet: *Tablet = @fieldParentPtr("device", device);
tablet.destroy();
},
.switch_device => {
.@"switch" => {
const switch_device: *Switch = @fieldParentPtr("device", device);
switch_device.deinit();
util.gpa.destroy(switch_device);

View File

@ -66,11 +66,6 @@ pub fn create(wlr_layer_surface: *wlr.LayerSurfaceV1) error{OutOfMemory}!void {
wlr_layer_surface.surface.events.unmap.add(&layer_surface.unmap);
wlr_layer_surface.surface.events.commit.add(&layer_surface.commit);
wlr_layer_surface.events.new_popup.add(&layer_surface.new_popup);
// wlroots only informs us of the new surface after the first commit,
// so our listener does not get called for this first commit. However,
// we do want our listener called in order to send the initial configure.
handleCommit(&layer_surface.commit, wlr_layer_surface.surface);
}
pub fn destroyPopups(layer_surface: *LayerSurface) void {

View File

@ -556,8 +556,6 @@ fn renderAndCommit(output: *Output, scene_output: *wlr.SceneOutput) !void {
if (!output.wlr_output.commitState(&state)) return error.CommitFailed;
// TODO(wlroots) remove this rotate() call when updating to wlroots 0.18
scene_output.damage_ring.rotate();
output.gamma_dirty = false;
} else {
if (!scene_output.commit(null)) return error.CommitFailed;

View File

@ -169,7 +169,7 @@ pub fn deactivate(constraint: *PointerConstraint) void {
fn warpToHintIfSet(constraint: *PointerConstraint) void {
const seat: *Seat = @ptrFromInt(constraint.wlr_constraint.seat.data);
if (constraint.wlr_constraint.current.committed.cursor_hint) {
if (constraint.wlr_constraint.current.cursor_hint.enabled) {
var lx: i32 = undefined;
var ly: i32 = undefined;
_ = constraint.state.active.node.coords(&lx, &ly);

View File

@ -117,7 +117,7 @@ transaction_timeout: *wl.EventSource,
pending_state_dirty: bool = false,
pub fn init(root: *Root) !void {
const output_layout = try wlr.OutputLayout.create();
const output_layout = try wlr.OutputLayout.create(server.wl_server);
errdefer output_layout.destroy();
const scene = try wlr.Scene.create();
@ -131,9 +131,6 @@ pub fn init(root: *Root) !void {
const outputs = try interactive_content.createSceneTree();
const override_redirect = if (build_options.xwayland) try interactive_content.createSceneTree();
const presentation = try wlr.Presentation.create(server.wl_server, server.backend);
scene.setPresentation(presentation);
const event_loop = server.wl_server.getEventLoop();
const transaction_timeout = try event_loop.addTimer(*Root, handleTransactionTimeout, root);
errdefer transaction_timeout.remove();
@ -166,7 +163,7 @@ pub fn init(root: *Root) !void {
.all_outputs = undefined,
.active_outputs = undefined,
.presentation = presentation,
.presentation = try wlr.Presentation.create(server.wl_server, server.backend),
.xdg_output_manager = try wlr.XdgOutputManagerV1.create(server.wl_server, output_layout),
.output_manager = try wlr.OutputManagerV1.create(server.wl_server),
.power_manager = try wlr.OutputPowerManagerV1.create(server.wl_server),

View File

@ -507,11 +507,11 @@ fn tryAddDevice(seat: *Seat, wlr_device: *wlr.InputDevice) !void {
seat.cursor.wlr_cursor.attachInputDevice(wlr_device);
},
.tablet_tool => {
.tablet => {
try Tablet.create(seat, wlr_device);
seat.cursor.wlr_cursor.attachInputDevice(wlr_device);
},
.switch_device => {
.@"switch" => {
const switch_device = try util.gpa.create(Switch);
errdefer util.gpa.destroy(switch_device);
@ -534,7 +534,7 @@ pub fn updateCapabilities(seat: *Seat) void {
switch (device.wlr_device.type) {
.keyboard => capabilities.keyboard = true,
.touch => capabilities.touch = true,
.pointer, .switch_device, .tablet_tool => {},
.pointer, .@"switch", .tablet => {},
.tablet_pad => unreachable,
}
}

View File

@ -38,6 +38,7 @@ const Root = @import("Root.zig");
const Seat = @import("Seat.zig");
const SceneNodeData = @import("SceneNodeData.zig");
const StatusManager = @import("StatusManager.zig");
const TabletTool = @import("TabletTool.zig");
const XdgDecoration = @import("XdgDecoration.zig");
const XdgToplevel = @import("XdgToplevel.zig");
const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig");
@ -96,8 +97,8 @@ xwayland: if (build_options.xwayland) ?*wlr.Xwayland else void = if (build_optio
new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void =
if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface).init(handleNewXwaylandSurface),
new_xdg_surface: wl.Listener(*wlr.XdgSurface) =
wl.Listener(*wlr.XdgSurface).init(handleNewXdgSurface),
new_xdg_toplevel: wl.Listener(*wlr.XdgToplevel) =
wl.Listener(*wlr.XdgToplevel).init(handleNewXdgToplevel),
new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) =
wl.Listener(*wlr.XdgToplevelDecorationV1).init(handleNewToplevelDecoration),
new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1) =
@ -113,14 +114,14 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
// This keeps the code simpler and more readable.
const wl_server = try wl.Server.create();
const loop = wl_server.getEventLoop();
var session: ?*wlr.Session = undefined;
const backend = try wlr.Backend.autocreate(wl_server, &session);
const backend = try wlr.Backend.autocreate(loop, &session);
const renderer = try wlr.Renderer.autocreate(backend);
const compositor = try wlr.Compositor.create(wl_server, 6, renderer);
const loop = wl_server.getEventLoop();
server.* = .{
.wl_server = wl_server,
.sigint_source = try loop.addSignal(*wl.Server, posix.SIG.INT, terminate, wl_server),
@ -167,7 +168,7 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
.lock_manager = undefined,
};
if (renderer.getDmabufFormats() != null and renderer.getDrmFd() >= 0) {
if (renderer.getTextureFormats(@intFromEnum(wlr.BufferCap.dmabuf)) != null) {
// wl_drm is a legacy interface and all clients should switch to linux_dmabuf.
// However, enough widely used clients still rely on wl_drm that the pragmatic option
// is to keep it around for the near future.
@ -190,7 +191,7 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
try server.idle_inhibit_manager.init();
try server.lock_manager.init();
server.xdg_shell.events.new_surface.add(&server.new_xdg_surface);
server.xdg_shell.events.new_toplevel.add(&server.new_xdg_toplevel);
server.xdg_decoration_manager.events.new_toplevel_decoration.add(&server.new_toplevel_decoration);
server.layer_shell.events.new_surface.add(&server.new_layer_surface);
server.xdg_activation.events.request_activate.add(&server.request_activate);
@ -204,7 +205,7 @@ pub fn deinit(server: *Server) void {
server.sigint_source.remove();
server.sigterm_source.remove();
server.new_xdg_surface.link.remove();
server.new_xdg_toplevel.link.remove();
server.new_toplevel_decoration.link.remove();
server.new_layer_surface.link.remove();
server.request_activate.link.remove();
@ -277,14 +278,6 @@ fn globalFilter(client: *const wl.Client, global: *const wl.Global, server: *Ser
}
}
fn hackGlobal(ptr: *anyopaque) *wl.Global {
// TODO(wlroots) MR that eliminates the need for this hack:
// https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4612
if (wlr.version.major != 0 or wlr.version.minor != 17) @compileError("FIXME");
return @as(*extern struct { global: *wl.Global }, @alignCast(@ptrCast(ptr))).global;
}
/// Returns true if the global is allowlisted for security contexts
fn allowlist(server: *Server, global: *const wl.Global) bool {
if (server.drm) |drm| if (global == drm.global) return true;
@ -300,8 +293,8 @@ fn allowlist(server: *Server, global: *const wl.Global) bool {
// with an assertion failure.
return global.getInterface() == wl.Output.getInterface() or
global.getInterface() == wl.Seat.getInterface() or
global == hackGlobal(server.shm) or
global == hackGlobal(server.single_pixel_buffer_manager) or
global == server.shm.global or
global == server.single_pixel_buffer_manager.global or
global == server.viewporter.global or
global == server.fractional_scale_manager.global or
global == server.compositor.global or
@ -336,7 +329,7 @@ fn blocklist(server: *Server, global: *const wl.Global) bool {
global == server.root.output_manager.global or
global == server.root.power_manager.global or
global == server.root.gamma_control_manager.global or
global == hackGlobal(server.input_manager.idle_notifier) or
global == server.input_manager.idle_notifier.global or
global == server.input_manager.virtual_pointer_manager.global or
global == server.input_manager.virtual_keyboard_manager.global or
global == server.input_manager.input_method_manager.global or
@ -349,17 +342,12 @@ fn terminate(_: c_int, wl_server: *wl.Server) c_int {
return 0;
}
fn handleNewXdgSurface(_: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
if (xdg_surface.role == .popup) {
log.debug("new xdg_popup", .{});
return;
}
fn handleNewXdgToplevel(_: *wl.Listener(*wlr.XdgToplevel), xdg_toplevel: *wlr.XdgToplevel) void {
log.debug("new xdg_toplevel", .{});
XdgToplevel.create(xdg_surface.role_data.toplevel.?) catch {
XdgToplevel.create(xdg_toplevel) catch {
log.err("out of memory", .{});
xdg_surface.resource.postNoMemory();
xdg_toplevel.resource.postNoMemory();
return;
};
}
@ -450,17 +438,27 @@ fn handleRequestSetCursorShape(
_: *wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape),
event: *wlr.CursorShapeManagerV1.event.RequestSetShape,
) void {
// Ignore requests to set a tablet tool's cursor shape for now
// TODO(wlroots): https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3821
if (event.device_type == .tablet_tool) return;
const seat: *Seat = @ptrFromInt(event.seat_client.seat.data);
if (event.tablet_tool) |wp_tool| {
assert(event.device_type == .tablet_tool);
const tool = TabletTool.get(event.seat_client.seat, wp_tool.wlr_tool) catch return;
if (tool.allowSetCursor(event.seat_client, event.serial)) {
const name = wlr.CursorShapeManagerV1.shapeName(event.shape);
tool.wlr_cursor.setXcursor(seat.cursor.xcursor_manager, name);
}
} else {
assert(event.device_type == .pointer);
const focused_client = event.seat_client.seat.pointer_state.focused_client;
// This can be sent by any client, so we check to make sure this one is
// actually has pointer focus first.
if (focused_client == event.seat_client) {
const seat: *Seat = @ptrFromInt(event.seat_client.seat.data);
const name = wlr.CursorShapeManagerV1.shapeName(event.shape);
seat.cursor.setXcursor(name);
}
}
}

View File

@ -33,7 +33,7 @@ wp_tablet: *wlr.TabletV2Tablet,
output_mapping: ?*wlr.Output = null,
pub fn create(seat: *Seat, wlr_device: *wlr.InputDevice) !void {
assert(wlr_device.type == .tablet_tool);
assert(wlr_device.type == .tablet);
const tablet = try util.gpa.create(Tablet);
errdefer util.gpa.destroy(tablet);

View File

@ -102,24 +102,29 @@ fn handleDestroy(listener: *wl.Listener(*wlr.TabletTool), _: *wlr.TabletTool) vo
util.gpa.destroy(tool);
}
pub fn allowSetCursor(tool: *TabletTool, seat_client: *wlr.Seat.Client, serial: u32) bool {
if (tool.wp_tool.focused_surface == null or
tool.wp_tool.focused_surface.?.resource.getClient() != seat_client.client)
{
log.debug("client tried to set cursor without focus", .{});
return false;
}
if (serial != tool.wp_tool.proximity_serial) {
log.debug("focused client tried to set cursor with incorrect serial", .{});
return false;
}
return true;
}
fn handleSetCursor(
listener: *wl.Listener(*wlr.TabletV2TabletTool.event.SetCursor),
event: *wlr.TabletV2TabletTool.event.SetCursor,
) void {
const tool: *TabletTool = @fieldParentPtr("set_cursor", listener);
if (tool.wp_tool.focused_surface == null or
tool.wp_tool.focused_surface.?.resource.getClient() != event.seat_client.client)
{
log.debug("client tried to set cursor without focus", .{});
return;
}
if (event.serial != tool.wp_tool.proximity_serial) {
log.debug("focused client tried to set cursor with incorrect serial", .{});
return;
}
if (tool.allowSetCursor(event.seat_client, event.serial)) {
tool.wlr_cursor.setSurface(event.surface, event.hotspot_x, event.hotspot_y);
}
}
pub fn axis(tool: *TabletTool, tablet: *Tablet, event: *wlr.Tablet.event.Axis) void {

View File

@ -42,14 +42,9 @@ pub fn init(wlr_decoration: *wlr.XdgToplevelDecorationV1) void {
wlr_decoration.events.destroy.add(&decoration.destroy);
wlr_decoration.events.request_mode.add(&decoration.request_mode);
const ssd = server.config.rules.ssd.match(toplevel.view) orelse
(decoration.wlr_decoration.requested_mode != .client_side);
// TODO(wlroots): make sure this is properly batched in a single configure
// with all other initial state when wlroots makes this possible.
_ = wlr_decoration.setMode(if (ssd) .server_side else .client_side);
toplevel.view.pending.ssd = ssd;
if (toplevel.wlr_toplevel.base.initialized) {
handleRequestMode(&decoration.request_mode, wlr_decoration);
}
}
pub fn deinit(decoration: *XdgDecoration) void {

View File

@ -54,7 +54,7 @@ pub fn create(
.tree = try parent.createSceneXdgSurface(wlr_xdg_popup.base),
};
wlr_xdg_popup.base.events.destroy.add(&xdg_popup.destroy);
wlr_xdg_popup.events.destroy.add(&xdg_popup.destroy);
wlr_xdg_popup.base.surface.events.commit.add(&xdg_popup.commit);
wlr_xdg_popup.base.events.new_popup.add(&xdg_popup.new_popup);
wlr_xdg_popup.events.reposition.add(&xdg_popup.reposition);

View File

@ -62,12 +62,12 @@ configure_state: union(enum) {
destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy),
map: wl.Listener(void) = wl.Listener(void).init(handleMap),
unmap: wl.Listener(void) = wl.Listener(void).init(handleUnmap),
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
// Listeners that are only active while the view is mapped
ack_configure: wl.Listener(*wlr.XdgSurface.Configure) =
wl.Listener(*wlr.XdgSurface.Configure).init(handleAckConfigure),
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
request_fullscreen: wl.Listener(void) = wl.Listener(void).init(handleRequestFullscreen),
request_move: wl.Listener(*wlr.XdgToplevel.event.Move) =
wl.Listener(*wlr.XdgToplevel.event.Move).init(handleRequestMove),
@ -104,11 +104,10 @@ pub fn create(wlr_toplevel: *wlr.XdgToplevel) error{OutOfMemory}!void {
wlr_toplevel.base.surface.data = @intFromPtr(&view.tree.node);
// Add listeners that are active over the toplevel's entire lifetime
wlr_toplevel.base.events.destroy.add(&toplevel.destroy);
wlr_toplevel.events.destroy.add(&toplevel.destroy);
wlr_toplevel.base.surface.events.map.add(&toplevel.map);
wlr_toplevel.base.surface.events.commit.add(&toplevel.commit);
wlr_toplevel.base.events.new_popup.add(&toplevel.new_popup);
_ = wlr_toplevel.setWmCapabilities(.{ .fullscreen = true });
}
/// Send a configure event, applying the inflight state of the view.
@ -213,8 +212,10 @@ fn handleDestroy(listener: *wl.Listener(void)) void {
toplevel.destroy.link.remove();
toplevel.map.link.remove();
toplevel.unmap.link.remove();
toplevel.commit.link.remove();
toplevel.new_popup.link.remove();
// The wlr_surface may outlive the wlr_xdg_surface so we must clean up the user data.
// The wlr_surface may outlive the wlr_xdg_toplevel so we must clean up the user data.
toplevel.wlr_toplevel.base.surface.data = 0;
const view = toplevel.view;
@ -228,7 +229,6 @@ fn handleMap(listener: *wl.Listener(void)) void {
// Add listeners that are only active while mapped
toplevel.wlr_toplevel.base.events.ack_configure.add(&toplevel.ack_configure);
toplevel.wlr_toplevel.base.surface.events.commit.add(&toplevel.commit);
toplevel.wlr_toplevel.events.request_fullscreen.add(&toplevel.request_fullscreen);
toplevel.wlr_toplevel.events.request_move.add(&toplevel.request_move);
toplevel.wlr_toplevel.events.request_resize.add(&toplevel.request_resize);
@ -270,7 +270,6 @@ fn handleUnmap(listener: *wl.Listener(void)) void {
// Remove listeners that are only active while mapped
toplevel.ack_configure.link.remove();
toplevel.commit.link.remove();
toplevel.request_fullscreen.link.remove();
toplevel.request_move.link.remove();
toplevel.request_resize.link.remove();
@ -309,6 +308,23 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
const toplevel: *XdgToplevel = @fieldParentPtr("commit", listener);
const view = toplevel.view;
if (toplevel.wlr_toplevel.base.initial_commit) {
_ = toplevel.wlr_toplevel.setWmCapabilities(.{ .fullscreen = true });
if (toplevel.decoration) |decoration| {
const ssd = server.config.rules.ssd.match(toplevel.view) orelse
(decoration.wlr_decoration.requested_mode != .client_side);
_ = decoration.wlr_decoration.setMode(if (ssd) .server_side else .client_side);
toplevel.view.pending.ssd = ssd;
}
return;
}
if (!view.mapped) {
return;
}
{
const state = &toplevel.wlr_toplevel.current;
view.constraints = .{

View File

@ -31,12 +31,6 @@ const process = @import("process.zig");
const Server = @import("Server.zig");
comptime {
if (wlr.version.major != 0 or wlr.version.minor != 17 or wlr.version.micro < 2) {
@compileError("river requires at least wlroots version 0.17.2 due to bugs in wlroots 0.17.0/0.17.1");
}
}
const usage: []const u8 =
\\usage: river [options]
\\