git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit3b4b737f77fdfc8bb3af309fc2564a84b3c4311d
parent36da704ea5
authorIsaac Freund <[email protected]>
date2026-03-27 18:09
build: update to wlroots 0.20

 .builds/alpine.yml                                 |   2 +-
 .builds/archlinux.yml                              |   2 +-
 .builds/freebsd.yml                                |   2 +-
 README.md                                          |   2 +-
 build.zig                                          |   4 +-
 build.zig.zon                                      |   4 +-
 protocol/upstream/virtual-keyboard-unstable-v1.xml | 114 +++++++++++++++++++++
 river/Cursor.zig                                   |   5 +-
 river/InputManager.zig                             |   4 +-
 river/InputRelay.zig                               |  17 +--
 river/TextInput.zig                                |  16 +--
 11 files changed, 139 insertions(+), 33 deletions(-)

diff --git a/.builds/alpine.yml b/.builds/alpine.yml
index dde76c2..16f1257 100644
--- a/.builds/alpine.yml
+++ b/.builds/alpine.yml
@@ -36,7 +36,7 @@ sources:
 tasks:
   - install_deps: |
       cd wlroots
-      git checkout 0.19.0
+      git checkout 0.20.0
       meson setup build --auto-features=enabled -Drenderers=gles2 \
             -Dcolor-management=disabled -Dlibliftoff=disabled \
             -Dexamples=false -Dwerror=false -Db_ndebug=false \
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml
index 4627e20..521daca 100644
--- a/.builds/archlinux.yml
+++ b/.builds/archlinux.yml
@@ -33,7 +33,7 @@ sources:
 tasks:
   - install_deps: |
       cd wlroots
-      git checkout 0.19.0
+      git checkout 0.20.0
       meson setup build --auto-features=enabled -Drenderers=gles2 \
             -Dcolor-management=disabled -Dlibliftoff=disabled \
             -Dexamples=false -Dwerror=false -Db_ndebug=false \
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
index c179cc7..ac7d064 100644
--- a/.builds/freebsd.yml
+++ b/.builds/freebsd.yml
@@ -38,7 +38,7 @@ sources:
 tasks:
   - install_deps: |
       cd wlroots
-      git checkout 0.19.0
+      git checkout 0.20.0
       meson setup build --auto-features=enabled -Drenderers=gles2 \
             -Dallocators=gbm \
             -Dcolor-management=disabled -Dlibliftoff=disabled \
diff --git a/README.md b/README.md
index aabd407..bf898af 100644
--- a/README.md
+++ b/README.md
@@ -69,7 +69,7 @@ distribution.
 - [zig](https://ziglang.org/download/) 0.15
 - wayland
 - wayland-protocols
-- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.19
+- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.20
 - xkbcommon 1.12 or newer
 - libevdev
 - pixman
diff --git a/build.zig b/build.zig
index a03318a..1433d42 100644
--- a/build.zig
+++ b/build.zig
@@ -92,6 +92,7 @@ pub fn build(b: *Build) !void {
 
     scanner.addCustomProtocol(b.path("protocol/upstream/wlr-layer-shell-unstable-v1.xml"));
     scanner.addCustomProtocol(b.path("protocol/upstream/wlr-output-power-management-unstable-v1.xml"));
+    scanner.addCustomProtocol(b.path("protocol/upstream/virtual-keyboard-unstable-v1.xml"));
 
     // Some of these versions may be out of date with what wlroots implements.
     // This is not a problem in practice though as long as river successfully compiles.
@@ -123,6 +124,7 @@ pub fn build(b: *Build) !void {
 
     scanner.generate("zwlr_output_power_manager_v1", 1);
     scanner.generate("zwlr_layer_shell_v1", 4);
+    scanner.generate("zwp_virtual_keyboard_manager_v1", 1);
 
     const wayland = b.createModule(.{ .root_source_file = scanner.result });
 
@@ -138,7 +140,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;
-    const wlroots_pkgconf = "wlroots-0.19";
+    const wlroots_pkgconf = "wlroots-0.20";
     wlroots.linkSystemLibrary(wlroots_pkgconf, .{});
 
     const flags = b.createModule(.{ .root_source_file = b.path("common/flags.zig") });
diff --git a/build.zig.zon b/build.zig.zon
index ba3ea2e..1f10100 100644
--- a/build.zig.zon
+++ b/build.zig.zon
@@ -20,8 +20,8 @@
             .hash = "wayland-0.5.0-lQa1knz8AQCh08NA8BeQrwJB9U3CfqcVAdHZYGRKIGuu",
         },
         .wlroots = .{
-            .url = "https://codeberg.org/ifreund/zig-wlroots/archive/v0.19.4.tar.gz",
-            .hash = "wlroots-0.19.4-jmOlcqQMBABhKYH6NMSnoK1sohTbhc97_JP-hGg2UZaK",
+            .url = "git+https://codeberg.org/ifreund/zig-wlroots?ref=master#6ffee8b21d24e0e4aadcf204f65ebe7ad3c3bacf",
+            .hash = "wlroots-0.20.0-dev-jmOlclseBABjKSxVH4TVkN4Fm-moidbybTStGsujJU_m",
         },
         .xkbcommon = .{
             .url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.4.0.tar.gz",
diff --git a/protocol/upstream/virtual-keyboard-unstable-v1.xml b/protocol/upstream/virtual-keyboard-unstable-v1.xml
new file mode 100644
index 0000000..1ac03fe
--- /dev/null
+++ b/protocol/upstream/virtual-keyboard-unstable-v1.xml
@@ -0,0 +1,114 @@
+<!-- SPDX-License-Identifier: MIT -->
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="virtual_keyboard_unstable_v1">
+  <copyright>
+    Copyright © 2008-2011  Kristian Høgsberg
+    Copyright © 2010-2013  Intel Corporation
+    Copyright © 2012-2013  Collabora, Ltd.
+    Copyright © 2018       Purism SPC
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <interface name="zwp_virtual_keyboard_v1" version="1">
+    <description summary="virtual keyboard">
+      The virtual keyboard provides an application with requests which emulate
+      the behaviour of a physical keyboard.
+
+      This interface can be used by clients on its own to provide raw input
+      events, or it can accompany the input method protocol.
+    </description>
+
+    <request name="keymap">
+      <description summary="keyboard mapping">
+        Provide a file descriptor to the compositor which can be
+        memory-mapped to provide a keyboard mapping description.
+
+        Format carries a value from the keymap_format enumeration.
+      </description>
+      <arg name="format" type="uint" summary="keymap format"/>
+      <arg name="fd" type="fd" summary="keymap file descriptor"/>
+      <arg name="size" type="uint" summary="keymap size, in bytes"/>
+    </request>
+
+    <enum name="error">
+      <entry name="no_keymap" value="0" summary="No keymap was set"/>
+    </enum>
+
+    <request name="key">
+      <description summary="key event">
+        A key was pressed or released.
+        The time argument is a timestamp with millisecond granularity, with an
+        undefined base. All requests regarding a single object must share the
+        same clock.
+
+        Keymap must be set before issuing this request.
+
+        State carries a value from the key_state enumeration.
+      </description>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="key" type="uint" summary="key that produced the event"/>
+      <arg name="state" type="uint" summary="physical state of the key"/>
+    </request>
+
+    <request name="modifiers">
+      <description summary="modifier and group state">
+        Notifies the compositor that the modifier and/or group state has
+        changed, and it should update state.
+
+        The client should use wl_keyboard.modifiers event to synchronize its
+        internal state with seat state.
+
+        Keymap must be set before issuing this request.
+      </description>
+      <arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
+      <arg name="mods_latched" type="uint" summary="latched modifiers"/>
+      <arg name="mods_locked" type="uint" summary="locked modifiers"/>
+      <arg name="group" type="uint" summary="keyboard layout"/>
+    </request>
+
+    <request name="destroy" type="destructor" since="1">
+      <description summary="destroy the virtual keyboard keyboard object"/>
+    </request>
+  </interface>
+
+  <interface name="zwp_virtual_keyboard_manager_v1" version="1">
+    <description summary="virtual keyboard manager">
+      A virtual keyboard manager allows an application to provide keyboard
+      input events as if they came from a physical keyboard.
+    </description>
+
+    <enum name="error">
+      <entry name="unauthorized" value="0" summary="client not authorized to use the interface"/>
+    </enum>
+
+    <request name="create_virtual_keyboard">
+      <description summary="Create a new virtual keyboard">
+        Creates a new virtual keyboard associated to a seat.
+
+        If the compositor enables a keyboard to perform arbitrary actions, it
+        should present an error when an untrusted client requests a new
+        keyboard.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat"/>
+      <arg name="id" type="new_id" interface="zwp_virtual_keyboard_v1"/>
+    </request>
+  </interface>
+</protocol>
diff --git a/river/Cursor.zig b/river/Cursor.zig
index 3aad15b..46aad3d 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -232,10 +232,7 @@ pub fn setTheme(cursor: *Cursor, theme: ?[*:0]const u8, _size: ?u32) !void {
                 const wlr_xcursor = xcursor_manager.getXcursor("default", 1).?;
                 const image = wlr_xcursor.images[0];
                 xwayland.setCursor(
-                    image.buffer,
-                    image.width * 4,
-                    image.width,
-                    image.height,
+                    image.getBuffer(),
                     @intCast(image.hotspot_x),
                     @intCast(image.hotspot_y),
                 );
diff --git a/river/InputManager.zig b/river/InputManager.zig
index c7852a1..b5bb9e3 100644
--- a/river/InputManager.zig
+++ b/river/InputManager.zig
@@ -83,8 +83,8 @@ pub fn init(input_manager: *InputManager) !void {
     input_manager.virtual_pointer_manager.events.new_virtual_pointer.add(&input_manager.new_virtual_pointer);
     input_manager.virtual_keyboard_manager.events.new_virtual_keyboard.add(&input_manager.new_virtual_keyboard);
     input_manager.pointer_constraints.events.new_constraint.add(&input_manager.new_constraint);
-    input_manager.input_method_manager.events.input_method.add(&input_manager.new_input_method);
-    input_manager.text_input_manager.events.text_input.add(&input_manager.new_text_input);
+    input_manager.input_method_manager.events.new_input_method.add(&input_manager.new_input_method);
+    input_manager.text_input_manager.events.new_text_input.add(&input_manager.new_text_input);
 }
 
 pub fn deinit(input_manager: *InputManager) void {
diff --git a/river/InputRelay.zig b/river/InputRelay.zig
index 08c7961..007a7bf 100644
--- a/river/InputRelay.zig
+++ b/river/InputRelay.zig
@@ -32,9 +32,9 @@ input_popups: wl.list.Head(InputPopup, .link),
 /// Always null if there is no input method.
 text_input: ?*TextInput = null,
 
-input_method_commit: wl.Listener(*wlr.InputMethodV2) = .init(handleInputMethodCommit),
+input_method_commit: wl.Listener(void) = .init(handleInputMethodCommit),
 grab_keyboard: wl.Listener(*wlr.InputMethodV2.KeyboardGrab) = .init(handleInputMethodGrabKeyboard),
-input_method_destroy: wl.Listener(*wlr.InputMethodV2) = .init(handleInputMethodDestroy),
+input_method_destroy: wl.Listener(void) = .init(handleInputMethodDestroy),
 input_method_new_popup: wl.Listener(*wlr.InputPopupSurfaceV2) = .init(handleInputMethodNewPopup),
 
 grab_keyboard_destroy: wl.Listener(*wlr.InputMethodV2.KeyboardGrab) = .init(handleInputMethodGrabKeyboardDestroy),
@@ -70,12 +70,9 @@ pub fn newInputMethod(relay: *InputRelay, input_method: *wlr.InputMethodV2) void
     }
 }
 
-fn handleInputMethodCommit(
-    listener: *wl.Listener(*wlr.InputMethodV2),
-    input_method: *wlr.InputMethodV2,
-) void {
+fn handleInputMethodCommit(listener: *wl.Listener(void)) void {
     const relay: *InputRelay = @fieldParentPtr("input_method_commit", listener);
-    assert(input_method == relay.input_method);
+    const input_method = relay.input_method.?;
 
     if (!input_method.client_active) return;
     const text_input = relay.text_input orelse return;
@@ -104,12 +101,8 @@ fn handleInputMethodCommit(
     text_input.wlr_text_input.sendDone();
 }
 
-fn handleInputMethodDestroy(
-    listener: *wl.Listener(*wlr.InputMethodV2),
-    input_method: *wlr.InputMethodV2,
-) void {
+fn handleInputMethodDestroy(listener: *wl.Listener(void)) void {
     const relay: *InputRelay = @fieldParentPtr("input_method_destroy", listener);
-    assert(input_method == relay.input_method);
 
     relay.input_method_commit.link.remove();
     relay.grab_keyboard.link.remove();
diff --git a/river/TextInput.zig b/river/TextInput.zig
index 0eb71df..3a73936 100644
--- a/river/TextInput.zig
+++ b/river/TextInput.zig
@@ -19,10 +19,10 @@ link: wl.list.Link,
 
 wlr_text_input: *wlr.TextInputV3,
 
-enable: wl.Listener(*wlr.TextInputV3) = .init(handleEnable),
-commit: wl.Listener(*wlr.TextInputV3) = .init(handleCommit),
-disable: wl.Listener(*wlr.TextInputV3) = .init(handleDisable),
-destroy: wl.Listener(*wlr.TextInputV3) = .init(handleDestroy),
+enable: wl.Listener(void) = .init(handleEnable),
+commit: wl.Listener(void) = .init(handleCommit),
+disable: wl.Listener(void) = .init(handleDisable),
+destroy: wl.Listener(void) = .init(handleDestroy),
 
 pub fn create(wlr_text_input: *wlr.TextInputV3) !void {
     const seat: *Seat = @ptrCast(@alignCast(wlr_text_input.seat.data));
@@ -44,7 +44,7 @@ pub fn create(wlr_text_input: *wlr.TextInputV3) !void {
     wlr_text_input.events.destroy.add(&text_input.destroy);
 }
 
-fn handleEnable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void {
+fn handleEnable(listener: *wl.Listener(void)) void {
     const text_input: *TextInput = @fieldParentPtr("enable", listener);
     const seat: *Seat = @ptrCast(@alignCast(text_input.wlr_text_input.seat.data));
 
@@ -71,7 +71,7 @@ fn handleEnable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) v
     }
 }
 
-fn handleCommit(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void {
+fn handleCommit(listener: *wl.Listener(void)) void {
     const text_input: *TextInput = @fieldParentPtr("commit", listener);
     const seat: *Seat = @ptrCast(@alignCast(text_input.wlr_text_input.seat.data));
 
@@ -85,7 +85,7 @@ fn handleCommit(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) v
     }
 }
 
-fn handleDisable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void {
+fn handleDisable(listener: *wl.Listener(void)) void {
     const text_input: *TextInput = @fieldParentPtr("disable", listener);
     const seat: *Seat = @ptrCast(@alignCast(text_input.wlr_text_input.seat.data));
 
@@ -94,7 +94,7 @@ fn handleDisable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3)
     }
 }
 
-fn handleDestroy(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void {
+fn handleDestroy(listener: *wl.Listener(void)) void {
     const text_input: *TextInput = @fieldParentPtr("destroy", listener);
     const seat: *Seat = @ptrCast(@alignCast(text_input.wlr_text_input.seat.data));