Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package wayland for openSUSE:Factory checked in at 2026-07-17 18:47:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/wayland (Old) and /work/SRC/openSUSE:Factory/.wayland.new.24530 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "wayland" Fri Jul 17 18:47:42 2026 rev:56 rq:1366220 version:1.26.0 Changes: -------- --- /work/SRC/openSUSE:Factory/wayland/wayland.changes 2026-03-22 14:11:38.256039657 +0100 +++ /work/SRC/openSUSE:Factory/.wayland.new.24530/wayland.changes 2026-07-17 18:48:31.991336481 +0200 @@ -1,0 +2,14 @@ +Thu Jul 16 16:01:24 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 1.26 + * A new wl_pointer.warp event, to notify a new pointer position + without an end-user-initiated motion event. + * A new wl_fixes.ack_global_remove request, to address races + related to global remove events. + * A new wl_display_remove_socket_fd() function to remove sockets + previously added via wl_display_add_socket_fd(). + * WAYLAND_DEBUG timestamps now use the "HH:MM:ss.xxxxxx" format, + making them easier to read and compare with other logs. +- Drop pre-Leap-16.x build logic + +------------------------------------------------------------------- Old: ---- wayland-1.25.0.tar.xz wayland-1.25.0.tar.xz.sig New: ---- wayland-1.26.0.tar.xz wayland-1.26.0.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ wayland.spec ++++++ --- /var/tmp/diff_new_pack.EwpGtt/_old 2026-07-17 18:48:33.027371412 +0200 +++ /var/tmp/diff_new_pack.EwpGtt/_new 2026-07-17 18:48:33.047372087 +0200 @@ -16,12 +16,6 @@ # -%define _version 1.25.0 -%if 0%{?suse_version} >= 1500 && 0%{?suse_version} < 1550 -%define eglversion 99~%_version -%else -%define eglversion %_version -%endif %define lname libwayland0 %define enable_test 1 %if 0%{?qemu_user_space_build} @@ -30,7 +24,7 @@ %endif %bcond_with doc Name: wayland -Version: %_version +Version: 1.26.0 Release: 0 Summary: Wayland Compositor Infrastructure License: MIT @@ -89,8 +83,7 @@ to get buffer for each cursor image. %package -n libwayland-egl1 -Version: %eglversion -Summary: Additional egl functions for wayland +Summary: Additional EGL functions for Wayland Group: System/Libraries %description -n libwayland-egl1 @@ -113,20 +106,12 @@ %package devel Summary: Development files for the Wayland Compositor Infrastructure Group: Development/Libraries/C and C++ -Requires: libwayland-client0 = %_version -Requires: libwayland-cursor0 = %_version -Requires: libwayland-egl1 = %eglversion -Requires: libwayland-server0 = %_version -%if 0%{?suse_version} >= 1500 -# egl1 was once provided by Mesa 17.x. -%if (0%{?suse_version} >= 1550 || 0%{?sle_version} >= 150400) +Requires: libwayland-client0 = %version +Requires: libwayland-cursor0 = %version +Requires: libwayland-egl1 = %version +Requires: libwayland-server0 = %version Provides: libwayland-egl-devel = 18.1.5 Obsoletes: libwayland-egl-devel < 18.1.5 -%else -Provides: libwayland-egl-devel = 18.0.2 -Obsoletes: libwayland-egl-devel < 18.0.2 -%endif -%endif %description devel Wayland is a protocol for a compositor to talk to its clients as well @@ -149,7 +134,6 @@ %prep %autosetup -p1 -sed -i 's/<eglversion>/%eglversion/' "%_sourcedir/baselibs.conf" %build # includedir intentional, cf. bugzilla.opensuse.org/795968 @@ -170,21 +154,10 @@ %meson_test %endif -%if 0%{?suse_version} >= 1550 %ldconfig_scriptlets -n libwayland-client0 %ldconfig_scriptlets -n libwayland-cursor0 %ldconfig_scriptlets -n libwayland-egl1 %ldconfig_scriptlets -n libwayland-server0 -%else -%post -n libwayland-client0 -p /sbin/ldconfig -%postun -n libwayland-client0 -p /sbin/ldconfig -%post -n libwayland-cursor0 -p /sbin/ldconfig -%postun -n libwayland-cursor0 -p /sbin/ldconfig -%post -n libwayland-egl1 -p /sbin/ldconfig -%postun -n libwayland-egl1 -p /sbin/ldconfig -%post -n libwayland-server0 -p /sbin/ldconfig -%postun -n libwayland-server0 -p /sbin/ldconfig -%endif %files -n libwayland-client0 %_libdir/libwayland-client.so.0* ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.EwpGtt/_old 2026-07-17 18:48:33.359382606 +0200 +++ /var/tmp/diff_new_pack.EwpGtt/_new 2026-07-17 18:48:33.411384359 +0200 @@ -1,5 +1,5 @@ -mtime: 1773950034 -commit: 5107b46cec55f16ffdb1ff231a8c6c39ce965976dcfdfa2fcc466867373056af +mtime: 1784217721 +commit: cba0cf7f7d93c560472b620ee6fd5488bf120bf166eeaccf1b6d981b39a4bdb1 url: https://src.opensuse.org/jengelh/wayland revision: master ++++++ baselibs.conf ++++++ --- /var/tmp/diff_new_pack.EwpGtt/_old 2026-07-17 18:48:33.655392586 +0200 +++ /var/tmp/diff_new_pack.EwpGtt/_new 2026-07-17 18:48:33.687393666 +0200 @@ -6,7 +6,7 @@ requires -wayland-<targettype> requires "libwayland-client0-<targettype> = <version>" requires "libwayland-cursor0-<targettype> = <version>" - requires "libwayland-egl1-<targettype> = <eglversion>" + requires "libwayland-egl1-<targettype> = <version>" requires "libwayland-server0-<targettype> = <version>" provides "libwayland-egl-devel-<targettype> = 18.1.5" obsoletes "libwayland-egl-devel-<targettype> < 18.1.5" ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-07-16 18:02:01.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ wayland-1.25.0.tar.xz -> wayland-1.26.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/.gitlab-ci.yml new/wayland-1.26.0/.gitlab-ci.yml --- old/wayland-1.25.0/.gitlab-ci.yml 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/.gitlab-ci.yml 2026-07-16 17:24:51.000000000 +0200 @@ -305,11 +305,11 @@ .os-freebsd: variables: BUILD_OS: freebsd - FDO_DISTRIBUTION_VERSION: "14.3" + FDO_DISTRIBUTION_VERSION: "14.4" FDO_DISTRIBUTION_PACKAGES: 'libxslt meson ninja pkgconf expat libffi libepoll-shim libxml2' # bump this tag every time you change something which requires rebuilding the # base image - FDO_DISTRIBUTION_TAG: "2025-07-20.0" + FDO_DISTRIBUTION_TAG: "2026-03-10.0" # Don't build documentation since installing the required tools massively # increases the VM image (and therefore container) size. MESON_ARGS: "--fatal-meson-warnings -Dwerror=true -Ddocumentation=false" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/doc/book/src/Protocol.md new/wayland-1.26.0/doc/book/src/Protocol.md --- old/wayland-1.25.0/doc/book/src/Protocol.md 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/doc/book/src/Protocol.md 2026-07-16 17:24:51.000000000 +0200 @@ -170,9 +170,9 @@ ## Connect Time -There is no fixed connection setup information, the server emits multiple events -at connect time, to indicate the presence and properties of global objects: -outputs, compositor, input devices. +When a client connects, the server sends no data but silently provides access to +the singleton `wl_display` under object ID 1. Global objects are managed through +an instance of [wl_registry](#registry). ## Security and Authentication @@ -187,19 +187,50 @@ Each object has a unique ID. The IDs are allocated by the entity creating the object (either client or server). IDs allocated by the client are in the range -[1, 0xfeffffff] while IDs allocated by the server are in the range [0xff000000, -0xffffffff]. The 0 ID is reserved to represent a null or non-existent object. -For efficiency purposes, the IDs are densely packed in the sense that the ID N -will not be used until N-1 has been used. This ordering is not merely a -guideline, but a strict requirement, and there are implementations of the -protocol that rigorously enforce this rule, including the ubiquitous libwayland. +[2, 0xfeffffff] while IDs allocated by the server are in the range [0xff000000, +0xffffffff]. The 0 ID is reserved to represent a null or non-existent object and +ID 1 is automatically assigned to the singleton `wl_display` instance when a +client connects. For efficiency purposes, the IDs are densely packed in the +sense that the ID N will not be used until N-1 has been used. This ordering is +not merely a guideline, but a strict requirement, and there are implementations +of the protocol that rigorously enforce this rule, including the ubiquitous +libwayland. + +## Display + +The display is a singleton global object and always has the ID 1. Clients often +call its `get_registry` method after connecting to the server to obtain the +object registry. + +See [wl_display](https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_display) +for the protocol description. + +## Registry + +The registry is a global object, instantiated using +[`wl_display.get_registry`](https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_display-request-get_registry), +that acts as the interface to Wayland's object system between client and server: +it initially informs the client of all globals available on the server, +announces to the client whenever objects are added to or removed from the +server, and allows the client to bind to the objects to interact with them. + +After instantiating a new registry, the server advertises all available objects +to the client using +[`wl_registry.global`](https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_registry-event-global). +Thereafter, whenever a new object is added, the server advertises it to its +clients using `wl_registry.global`, and whenever an object is removed, this fact +is advertised using +[`wl_registry.global_remove`](https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_registry-event-global_remove). + +See [wl_registry](https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_registry) +for the protocol description. ## Compositor -The compositor is a global object, advertised at connect time. +The compositor is a global object, advertised through the registry. -See [wl_compositor](https://wayland.app/protocols/wayland#wl_compositor) for the -protocol description. +See [wl_compositor](https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_compositor) +for the protocol description. ## Surfaces @@ -275,8 +306,8 @@ ## Output -An output is a global object, advertised at connect time or as it comes and -goes. +An output is a global object, advertised through the registry. Outputs may be +added or removed during the lifetime of the server. See [wl_output](https://wayland.app/protocols/wayland#wl_output) for the protocol description. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/meson.build new/wayland-1.26.0/meson.build --- old/wayland-1.25.0/meson.build 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/meson.build 2026-07-16 17:24:51.000000000 +0200 @@ -1,6 +1,6 @@ project( 'wayland', 'c', - version: '1.25.0', + version: '1.26.0', license: 'MIT', meson_version: '>= 0.64.0', default_options: [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/protocol/wayland.xml new/wayland-1.26.0/protocol/wayland.xml --- old/wayland-1.25.0/protocol/wayland.xml 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/protocol/wayland.xml 2026-07-16 17:24:51.000000000 +0200 @@ -100,11 +100,15 @@ <event name="delete_id"> <description summary="acknowledge object ID deletion"> - This event is used internally by the object ID management - logic. When a client deletes an object that it had created, - the server will send this event to acknowledge that it has - seen the delete request. When the client receives this event, - it will know that it can safely reuse the object ID. + This event is used internally by the object ID management logic. + + When the server stops using an object created by the client, the server + sends this event. In particular, after sending this event, the server + will no longer send any events that contain the object as the receiver + or as an argument. + + When the client receives this event, it knows that it can reuse the + object ID. </description> <arg name="id" type="uint" summary="deleted object ID"/> </event> @@ -140,7 +144,7 @@ specified name as the identifier. </description> <arg name="name" type="uint" summary="unique numeric name of the object"/> - <arg name="id" type="new_id" summary="bounded object"/> + <arg name="id" type="new_id" summary="bound object"/> </request> <event name="global"> @@ -220,7 +224,7 @@ </request> </interface> - <interface name="wl_shm_pool" version="2"> + <interface name="wl_shm_pool" version="3"> <description summary="a shared memory pool"> The wl_shm_pool object encapsulates a piece of memory shared between the compositor and client. Through the wl_shm_pool @@ -231,6 +235,14 @@ a surface or for many small buffers. </description> + <enum name="error" since="3"> + <description summary="wl_shm_pool error values"> + These errors can be emitted in response to wl_shm_pool requests. + </description> + <entry name="invalid_format" value="0" summary="buffer format is not known"/> + <entry name="invalid_stride" value="1" summary="invalid size or stride during buffer creation"/> + </enum> + <request name="create_buffer"> <description summary="create a buffer from the pool"> Create a wl_buffer object from the pool. @@ -280,7 +292,7 @@ </request> </interface> - <interface name="wl_shm" version="2"> + <interface name="wl_shm" version="3"> <description summary="shared memory support"> A singleton global object that provides support for shared memory. @@ -293,12 +305,12 @@ that can be used for buffers. </description> - <enum name="error"> + <enum name="error" since="3"> <description summary="wl_shm error values"> These errors can be emitted in response to wl_shm requests. </description> - <entry name="invalid_format" value="0" summary="buffer format is not known"/> - <entry name="invalid_stride" value="1" summary="invalid size or stride during pool or buffer creation"/> + <entry name="invalid_format" value="0" summary="buffer format is not known" deprecated-since="3"/> + <entry name="invalid_stride" value="1" summary="invalid size or stride during pool creation"/> <entry name="invalid_fd" value="2" summary="mmapping the file descriptor failed"/> </enum> @@ -331,7 +343,7 @@ <entry name="bgrx4444" value="0x32315842" summary="16-bit BGRx format, [15:0] B:G:R:x 4:4:4:4 little endian"/> <entry name="argb4444" value="0x32315241" summary="16-bit ARGB format, [15:0] A:R:G:B 4:4:4:4 little endian"/> <entry name="abgr4444" value="0x32314241" summary="16-bit ABGR format, [15:0] A:B:G:R 4:4:4:4 little endian"/> - <entry name="rgba4444" value="0x32314152" summary="16-bit RBGA format, [15:0] R:G:B:A 4:4:4:4 little endian"/> + <entry name="rgba4444" value="0x32314152" summary="16-bit RGBA format, [15:0] R:G:B:A 4:4:4:4 little endian"/> <entry name="bgra4444" value="0x32314142" summary="16-bit BGRA format, [15:0] B:G:R:A 4:4:4:4 little endian"/> <entry name="xrgb1555" value="0x35315258" summary="16-bit xRGB format, [15:0] x:R:G:B 1:5:5:5 little endian"/> <entry name="xbgr1555" value="0x35314258" summary="16-bit xBGR 1555 format, [15:0] x:B:G:R 1:5:5:5 little endian"/> @@ -449,9 +461,9 @@ <entry name="gr1616f" value="0x48205247" summary="[31:0] G:R 16:16 little endian"/> <entry name="bgr161616f" value="0x48524742" summary="[47:0] B:G:R 16:16:16 little endian"/> <entry name="r32f" value="0x46202052" summary="[31:0] R 32 little endian"/> - <entry name="gr3232f" value="0x46205247" summary="[63:0] R:G 32:32 little endian"/> - <entry name="bgr323232f" value="0x46524742" summary="[95:0] R:G:B 32:32:32 little endian"/> - <entry name="abgr32323232f" value="0x46384241" summary="[127:0] R:G:B:A 32:32:32:32 little endian"/> + <entry name="gr3232f" value="0x46205247" summary="[63:0] G:R 32:32 little endian"/> + <entry name="bgr323232f" value="0x46524742" summary="[95:0] B:G:R 32:32:32 little endian"/> + <entry name="abgr32323232f" value="0x46384241" summary="[127:0] A:B:G:R 32:32:32:32 little endian"/> <entry name="nv20" value="0x3032564e" summary="2x1 subsampled Cr:Cb plane"/> <entry name="nv30" value="0x3033564e" summary="non-subsampled Cr:Cb plane"/> <entry name="s010" value="0x30313053" summary="2x2 subsampled Cb (1) and Cr (2) planes 10 bits per channel"/> @@ -463,6 +475,11 @@ <entry name="s016" value="0x36313053" summary="2x2 subsampled Cb (1) and Cr (2) planes 16 bits per channel"/> <entry name="s216" value="0x36313253" summary="2x1 subsampled Cb (1) and Cr (2) planes 16 bits per channel"/> <entry name="s416" value="0x36313453" summary="non-subsampled Cb (1) and Cr (2) planes 16 bits per channel"/> + <entry name="xvuy2101010" value="0x30335958" summary="[31:0] x:Cr:Cb:Y 2:10:10:10 little endian"/> + <entry name="p230" value="0x30333250" summary="2x1 subsampled Cr:Cb plane 10 bits per channel packed"/> + <entry name="t430" value="0x30333454"/> + <entry name="y8" value="0x59455247" summary="8-bit Y-only"/> + <entry name="xyyy2101010" value="0x34415059" summary="[31:0] x:Y2:Y1:Y0 2:10:10:10 little endian"/> </enum> <request name="create_pool"> @@ -847,8 +864,8 @@ acceptance, wl_data_source.cancelled may still be emitted afterwards if the drop destination does not accept any mime type. - However, this event might however not be received if the compositor - cancelled the drag-and-drop operation before this event could happen. + However, this event might not be received if the compositor cancelled + the drag-and-drop operation before this event could happen. Note that the data_source may still be used in the future and should not be destroyed here. @@ -889,7 +906,7 @@ The most recent action received is always the valid one. The chosen action may change alongside negotiation (e.g. an "ask" action can turn into a "move" operation), so the effects of the final action must - always be applied in wl_data_offer.dnd_finished. + always be applied in wl_data_source.dnd_finished. Clients can trigger cursor surface changes from this point, so they reflect the current action. @@ -1984,7 +2001,7 @@ </request> </interface> - <interface name="wl_seat" version="10"> + <interface name="wl_seat" version="11"> <description summary="group of input devices"> A seat is a group of keyboards, pointer and touch devices. This object is published as a global during start up, or when such a @@ -2098,7 +2115,7 @@ shared across processes to refer to a specific wl_seat global. The name event is sent after binding to the seat global, and should be sent - before announcing capabilities. This event only sent once per seat object, + before announcing capabilities. This event is only sent once per seat object, and the name does not change over the lifetime of the wl_seat global. Compositors may re-use the same seat name if the wl_seat global is @@ -2118,7 +2135,7 @@ </interface> - <interface name="wl_pointer" version="10"> + <interface name="wl_pointer" version="11"> <description summary="pointer input device"> The wl_pointer interface represents one or more input devices, such as mice, which control the pointer location and pointer_focus @@ -2228,7 +2245,7 @@ <description summary="pointer button event"> Mouse button click and release notifications. - The location of the click is given by the last motion or + The location of the click is given by the last motion, warp or enter event. The time argument is a timestamp with millisecond granularity, with an undefined base. @@ -2529,9 +2546,31 @@ <arg name="direction" type="uint" enum="axis_relative_direction" summary="physical direction relative to axis motion"/> </event> + + <!-- Version 11 additions --> + + <event name="warp" since="11"> + <description summary="pointer warp event"> + Notification of pointer location change within a surface. + + This location change is not due to events on the input device, + but because either the surface under the pointer was moved and + thus the relative position of the pointer changed, or because + the compositor changed the pointer position in response to an + event like pointer confinement being exited. + + The arguments surface_x and surface_y are the location relative to + the focused surface. + + This event must not occur in the same wl_pointer.frame as a + wl_pointer.enter or wl_pointer.motion event. + </description> + <arg name="surface_x" type="fixed" summary="surface-local x coordinate"/> + <arg name="surface_y" type="fixed" summary="surface-local y coordinate"/> + </event> </interface> - <interface name="wl_keyboard" version="10"> + <interface name="wl_keyboard" version="11"> <description summary="keyboard input device"> The wl_keyboard interface represents one or more keyboards associated with a seat. @@ -2712,7 +2751,7 @@ </event> </interface> - <interface name="wl_touch" version="10"> + <interface name="wl_touch" version="11"> <description summary="touchscreen input device"> The wl_touch interface represents a touchscreen associated with a seat. @@ -2816,7 +2855,7 @@ of the ellipse, while the minor axis length describes the shorter diameter. Major and minor are orthogonal and both are specified in surface-local coordinates. The center of the ellipse is always at the - touchpoint location as reported by wl_touch.down or wl_touch.move. + touchpoint location as reported by wl_touch.down or wl_touch.motion. This event is only sent by the compositor if the touch device supports shape reports. The client has to make reasonable assumptions about the @@ -3335,12 +3374,20 @@ </request> </interface> - <interface name="wl_fixes" version="1"> + <interface name="wl_fixes" version="2"> <description summary="wayland protocol fixes"> This global fixes problems with other core-protocol interfaces that cannot be fixed in these interfaces themselves. </description> + <enum name="error"> + <description summary="wl_fixes error values"> + These errors can be emitted in response to wl_fixes requests. + </description> + <entry name="invalid_ack_remove" value="0" + summary="unknown global or the global is not removed"/> + </enum> + <request name="destroy" type="destructor"> <description summary="destroys this object"/> </request> @@ -3360,6 +3407,36 @@ <arg name="registry" type="object" interface="wl_registry" summary="the registry to destroy"/> </request> + + <request name="ack_global_remove" since="2"> + <description summary="acknowledge global removal"> + Acknowledge the removal of the specified global. + + If no global with the specified name exists or the global is not removed, + the wl_fixes.invalid_ack_remove protocol error will be posted. + + Due to the Wayland protocol being asynchronous, the wl_global objects + cannot be destroyed immediately. For example, if a wl_global is removed + and a client attempts to bind that global around same time, it can + result in a protocol error due to an unknown global name in the bind + request. + + In order to avoid crashing clients, the compositor should remove the + wl_global once it is guaranteed that no more bind requests will come. + + The wl_fixes.ack_global_remove() request is used to signal to the + compositor that the client will not bind the given global anymore. After + all clients acknowledge the removal of the global, the compositor can + safely destroy it. + + The client must call the wl_fixes.ack_global_remove() request in + response to a wl_registry.global_remove() event even if it did not bind + the corresponding global. + </description> + <arg name="registry" type="object" interface="wl_registry" + summary="the registry object"/> + <arg name="name" type="uint" summary="unique name of the global"/> + </request> </interface> </protocol> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/src/connection.c new/wayland-1.26.0/src/connection.c --- old/wayland-1.25.0/src/connection.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/src/connection.c 2026-07-16 17:24:51.000000000 +0200 @@ -1547,7 +1547,6 @@ struct argument_details arg; const char *signature = closure->message->signature; struct timespec tp; - unsigned int time; uint32_t nval; FILE *f; char *buffer; @@ -1558,10 +1557,12 @@ return; clock_gettime(CLOCK_REALTIME, &tp); - time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - fprintf(f, "%s[%7u.%03u] ", + fprintf(f, "%s[%02u:%02u:%02u.%06u] ", color ? WL_DEBUG_COLOR_GREEN : "", - time / 1000, time % 1000); + (unsigned) ((tp.tv_sec / 3600) % 24), + (unsigned) ((tp.tv_sec / 60) % 60), + (unsigned) (tp.tv_sec % 60), + (unsigned) (tp.tv_nsec / 1000)); #if defined(HAVE_GETTID) if (include_tid < 0) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/src/scanner.c new/wayland-1.26.0/src/scanner.c --- old/wayland-1.25.0/src/scanner.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/src/scanner.c 2026-07-16 17:24:51.000000000 +0200 @@ -189,7 +189,6 @@ char *uppercase_name; struct wl_list arg_list; struct wl_list link; - int arg_count; int new_id_count; int type_index; int all_null; @@ -890,7 +889,6 @@ arg->summary = xstrdup(summary); wl_list_insert(ctx->message->arg_list.prev, &arg->link); - ctx->message->arg_count++; } else if (strcmp(element_name, "enum") == 0) { if (name == NULL) fail(&ctx->loc, "no enum name given"); @@ -1224,7 +1222,6 @@ "interface '%s' has method named destroy " "but no destructor", interface->name); - exit(EXIT_FAILURE); } if (!has_destroy && strcmp(interface->name, "wl_display") != 0) { @@ -1630,6 +1627,10 @@ m->all_null = 1; wl_list_for_each(a, &m->arg_list, link) { length++; + if (a->type == NEW_ID && !a->interface_name) + // adds implicit string and uint arguments + length += 2; + switch (a->type) { case NEW_ID: case OBJECT: @@ -1867,9 +1868,15 @@ m->type_index = protocol->null_run_length + protocol->type_index; - protocol->type_index += m->arg_count; wl_list_for_each(a, &m->arg_list, link) { + protocol->type_index++; + if (a->type == NEW_ID && !a->interface_name) { + // adds implicit string and uint arguments + printf("\tNULL,\n\tNULL,\n"); + protocol->type_index += 2; + } + switch (a->type) { case NEW_ID: case OBJECT: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/src/wayland-client.c new/wayland-1.26.0/src/wayland-client.c --- old/wayland-1.25.0/src/wayland-client.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/src/wayland-client.c 2026-07-16 17:24:51.000000000 +0200 @@ -1577,7 +1577,6 @@ const struct wl_message *message; struct wl_event_queue *queue; struct timespec tp; - unsigned int time; int num_zombie_fds; wl_connection_copy(display->connection, p, sizeof p); @@ -1619,11 +1618,13 @@ if (debug_client) { clock_gettime(CLOCK_REALTIME, &tp); - time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - fprintf(stderr, "%s[%7u.%03u] %sdiscarded %s[%s]%s#%u%s.[event %d]%s" + fprintf(stderr, "%s[%02u:%02u:%02u.%06u] %sdiscarded %s[%s]%s#%u%s.[event %d]%s" "(%d fd, %d byte)\n", debug_color ? WL_DEBUG_COLOR_GREEN : "", - time / 1000, time % 1000, + (unsigned) ((tp.tv_sec / 3600) % 24), + (unsigned) ((tp.tv_sec / 60) % 60), + (unsigned) (tp.tv_sec % 60), + (unsigned) (tp.tv_nsec / 1000), debug_color ? WL_DEBUG_COLOR_RED : "", debug_color ? WL_DEBUG_COLOR_BLUE : "", zombie ? "zombie" : "unknown", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/src/wayland-server-core.h new/wayland-1.26.0/src/wayland-server-core.h --- old/wayland-1.25.0/src/wayland-server-core.h 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/src/wayland-server-core.h 2026-07-16 17:24:51.000000000 +0200 @@ -205,6 +205,9 @@ int wl_display_add_socket_fd(struct wl_display *display, int sock_fd); +int +wl_display_remove_socket_fd(struct wl_display *display, int sock_fd); + void wl_display_terminate(struct wl_display *display); @@ -222,10 +225,13 @@ size_t max_buffer_size); struct wl_client; +struct wl_global; typedef void (*wl_global_bind_func_t)(struct wl_client *client, void *data, uint32_t version, uint32_t id); +typedef void (*wl_global_withdrawn_func_t)(struct wl_global *global); + uint32_t wl_display_get_serial(const struct wl_display *display); @@ -254,6 +260,10 @@ wl_global_remove(struct wl_global *global); void +wl_global_set_withdrawn_listener(struct wl_global *global, + wl_global_withdrawn_func_t func); + +void wl_global_destroy(struct wl_global *global); /** A filter function for wl_global objects @@ -725,6 +735,11 @@ void wl_protocol_logger_destroy(struct wl_protocol_logger *logger); +void +wl_fixes_handle_ack_global_remove(struct wl_resource *fixes_resource, + struct wl_resource *registry_resource, + uint32_t global_name); + #ifdef __cplusplus } #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/src/wayland-server.c new/wayland-1.26.0/src/wayland-server.c --- old/wayland-1.25.0/src/wayland-server.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/src/wayland-server.c 2026-07-16 17:24:51.000000000 +0200 @@ -115,15 +115,29 @@ size_t max_buffer_size; }; +struct wl_registry { + struct wl_display *display; + struct wl_list offer_list; +}; + struct wl_global { struct wl_display *display; const struct wl_interface *interface; + struct wl_list offer_list; uint32_t name; uint32_t version; void *data; wl_global_bind_func_t bind; + wl_global_withdrawn_func_t withdrawn; struct wl_list link; - bool removed; + bool unpublished; +}; + +struct wl_global_offer { + struct wl_list global_link; /**< in struct wl_global::offer_list */ + struct wl_list registry_link; /**< in struct wl_registry::offer_list */ + struct wl_resource *registry_resource; + struct wl_global *global; }; struct wl_resource { @@ -1048,7 +1062,8 @@ const char *interface, uint32_t version, uint32_t id) { struct wl_global *global; - struct wl_display *display = resource->data; + struct wl_registry *registry = resource->data; + struct wl_display *display = registry->display; wl_list_for_each(global, &display->global_list, link) if (global->name == name) @@ -1100,9 +1115,76 @@ } static void -unbind_resource(struct wl_resource *resource) +wl_global_publish(struct wl_global *global, + struct wl_resource *registry_resource) +{ + struct wl_global_offer *offer; + struct wl_registry *registry = registry_resource->data; + + offer = zalloc(sizeof *offer); + if (offer == NULL) { + wl_resource_post_no_memory(registry_resource); + return; + } + + offer->registry_resource = registry_resource; + offer->global = global; + wl_list_insert(&global->offer_list, &offer->global_link); + wl_list_insert(®istry->offer_list, &offer->registry_link); + + wl_resource_post_event(registry_resource, + WL_REGISTRY_GLOBAL, + global->name, + global->interface->name, + global->version); +} + +static void +wl_global_offer_destroy(struct wl_global_offer *offer) +{ + wl_list_remove(&offer->global_link); + wl_list_remove(&offer->registry_link); + free(offer); +} + +static void +wl_global_offer_done(struct wl_global_offer *offer) +{ + struct wl_global *global = offer->global; + + wl_global_offer_destroy(offer); + + if (global->unpublished && global->withdrawn && wl_list_empty(&global->offer_list)) + global->withdrawn(global); +} + +static struct wl_global_offer * +wl_registry_find_offer(struct wl_registry *registry, + uint32_t global_name) +{ + struct wl_global_offer *offer; + + wl_list_for_each(offer, ®istry->offer_list, registry_link) { + if (offer->global->name == global_name) + return offer; + } + + return NULL; +} + +static void +unbind_registry_resource(struct wl_resource *resource) { + struct wl_registry *registry = resource->data; + struct wl_global_offer *offer; + + while (!wl_list_empty(®istry->offer_list)) { + offer = wl_container_of(registry->offer_list.next, offer, registry_link); + wl_global_offer_done(offer); + } + wl_list_remove(&resource->link); + free(registry); } static void @@ -1112,6 +1194,7 @@ struct wl_display *display = resource->data; struct wl_resource *registry_resource; struct wl_global *global; + struct wl_registry *registry; registry_resource = wl_resource_create(client, &wl_registry_interface, 1, id); @@ -1120,20 +1203,25 @@ return; } + registry = zalloc(sizeof *registry); + if (registry == NULL) { + wl_client_post_no_memory(client); + return; + } + + registry->display = display; + wl_list_init(®istry->offer_list); + wl_resource_set_implementation(registry_resource, ®istry_interface, - display, unbind_resource); + registry, unbind_registry_resource); wl_list_insert(&display->registry_resource_list, ®istry_resource->link); wl_list_for_each(global, &display->global_list, link) - if (wl_global_is_visible(client, global) && !global->removed) - wl_resource_post_event(registry_resource, - WL_REGISTRY_GLOBAL, - global->name, - global->interface->name, - global->version); + if (wl_global_is_visible(client, global) && !global->unpublished) + wl_global_publish(global, registry_resource); } static const struct wl_display_interface display_interface = { @@ -1270,6 +1358,7 @@ if (s->fd_lock >= 0) close(s->fd_lock); + wl_list_remove(&s->link); free(s); } @@ -1284,6 +1373,7 @@ s->fd = -1; s->fd_lock = -1; + wl_list_init(&s->link); return s; } @@ -1399,20 +1489,30 @@ global->version = version; global->data = data; global->bind = bind; - global->removed = false; + global->withdrawn = NULL; + global->unpublished = false; wl_list_insert(display->global_list.prev, &global->link); + wl_list_init(&global->offer_list); wl_list_for_each(resource, &display->registry_resource_list, link) if (wl_global_is_visible(resource->client, global)) - wl_resource_post_event(resource, - WL_REGISTRY_GLOBAL, - global->name, - global->interface->name, - global->version); + wl_global_publish(global, resource); return global; } +static void +wl_global_unpublish(struct wl_global *global) +{ + struct wl_global_offer *offer; + + wl_list_for_each(offer, &global->offer_list, global_link) + wl_resource_post_event(offer->registry_resource, + WL_REGISTRY_GLOBAL_REMOVE, global->name); + + global->unpublished = true; +} + /** Remove the global * * \param global The Wayland global. @@ -1420,45 +1520,93 @@ * Broadcast a global remove event to all clients without destroying the * global. This function can only be called once per global. * - * wl_global_destroy() removes the global and immediately destroys it. On - * the other end, this function only removes the global, allowing clients - * that have not yet received the global remove event to continue to bind to - * it. - * * This can be used by compositors to mitigate clients being disconnected - * because a global has been added and removed too quickly. Compositors can call - * wl_global_remove(), then wait an implementation-defined amount of time, then - * call wl_global_destroy(). Note that the destruction of a global is still - * racy, since clients have no way to acknowledge that they received the remove - * event. + * because a global has been added and removed too quickly. + * + * Due to the Wayland protocol being asynchronous, the wl_global objects + * cannot be destroyed immediately. For example, if a wl_global is destroyed + * and a client attempts to bind that global around same time, it can + * result in a protocol error due to an unknown global name in the bind request. + * + * In order to avoid crashing clients, the compositor should destroy the + * wl_global once it is guaranteed that no more bind requests will come. + * + * The recommended way to destroy globals is as follows: + * + * - the compositor registers a callback using the wl_global_set_withdrawn_listener() + * function, which will be called when it is safe to call wl_global_destroy(); + * - the compositor calls wl_global_remove(). This will broadcast + * wl_registry.global_remove events to all clients; + * - upon receiving a wl_registry.global_remove event, a client must + * reply to it by calling the wl_fixes.ack_global_remove() request. The + * client must call the wl_fixes.ack_global_remove() request even if it + * did not bind the global; + * - the compositor will call the wl_fixes_handle_ack_global_remove() function when + * it receives the wl_fixes.ack_global_remove() request from the client; + * - after all clients have acknowledged the global removal, the "withdrawn" + * callback will be called to notify the compositor that the + * wl_global_destroy() function can be called now. * + * \sa wl_global_set_withdrawn_listener + * \sa wl_fixes_handle_ack_global_remove * \since 1.17.90 */ WL_EXPORT void wl_global_remove(struct wl_global *global) { - struct wl_display *display = global->display; - struct wl_resource *resource; - - if (global->removed) + if (global->unpublished) wl_abort("wl_global_remove: called twice on the same " "global '%s#%"PRIu32"'", global->interface->name, global->name); - wl_list_for_each(resource, &display->registry_resource_list, link) - if (wl_global_is_visible(resource->client, global)) - wl_resource_post_event(resource, WL_REGISTRY_GLOBAL_REMOVE, - global->name); + wl_global_unpublish(global); - global->removed = true; + if (global->withdrawn && wl_list_empty(&global->offer_list)) + global->withdrawn(global); +} + +/** Set the withdrawn callback. + * + * \param global The Wayland global. + * \param func The withdrawn callback. + * + * The withdrawn callback notifies the compositor that the global can be + * safely destroyed by calling wl_global_destroy(). The callback will be called + * in the following cases: + * + * - immediately by wl_global_remove(), if there are no registeries where the + * global was announced; + * - or some time later after wl_global_remove(), due to either receiving the + * last wl_fixes_handle_ack_global_remove() or getting the last wl_registry + * where the global was announced destroyed. + * + * The withdrawn callback will never be called without prior wl_global_remove(). + * + * \sa wl_fixes_handle_ack_global_remove + * \since 1.26 + */ +WL_EXPORT void +wl_global_set_withdrawn_listener(struct wl_global *global, + wl_global_withdrawn_func_t func) +{ + global->withdrawn = func; } WL_EXPORT void wl_global_destroy(struct wl_global *global) { - if (!global->removed) - wl_global_remove(global); + struct wl_global_offer *offer; + + if (!global->unpublished) + wl_global_unpublish(global); + wl_list_remove(&global->link); + + while (!wl_list_empty(&global->offer_list)) { + offer = wl_container_of(global->offer_list.next, offer, global_link); + wl_global_offer_destroy(offer); + } + free(global); } @@ -1894,7 +2042,8 @@ * with both bind() and listen() already called. * * On success, the socket fd ownership is transferred to libwayland: - * libwayland will close the socket when the display is destroyed. + * libwayland will close the socket when the display is destroyed or when + * wl_display_remove_socket_fd() is called. * * \memberof wl_display */ @@ -1930,6 +2079,37 @@ return 0; } +/** Remove a socket fd from the Wayland display. + * + * \param display Wayland display from which the socket should be removed. + * \param sock_fd The socket file descriptor to be removed. + * \return 0 if success. -1 if failed. + * + * Remove a socket fd previously added via wl_display_add_socket_fd(). + * + * \memberof wl_display + * \since 1.25.90 + */ +WL_EXPORT int +wl_display_remove_socket_fd(struct wl_display *display, int sock_fd) +{ + struct wl_socket *s; + bool found; + + found = false; + wl_list_for_each(s, &display->socket_list, link) { + if (s->fd == sock_fd) { + found = true; + break; + } + } + if (!found) + return -1; + + wl_socket_destroy(s); + return 0; +} + /** Add a socket to Wayland display for the clients to connect. * * \param display Wayland display to which the socket should be added. @@ -2709,6 +2889,47 @@ wl_global_destroy(global); } +/** Acknowledge global removal by a client + * + * \param fixes_resource The wl_fixes resource. + * \param registry_resource The wl_registry resource. + * \param global_name The unique id of the global. + * + * The compositor should call this function from the wl_fixes.ack_global_remove + * request implementation. + * + * libwayland-server automatically takes care of the implied ack-remove due + * to client disconnection or explicit wl_resource_destroy() of the wl_registry + * resource. + * + * \sa wl_global_remove + * \sa wl_global_set_withdrawn_listener + * \since 1.26 + */ +WL_EXPORT void +wl_fixes_handle_ack_global_remove(struct wl_resource *fixes_resource, + struct wl_resource *registry_resource, + uint32_t global_name) +{ + struct wl_global_offer *offer; + struct wl_registry *registry = registry_resource->data; + + offer = wl_registry_find_offer(registry, global_name); + if (!offer) { + wl_resource_post_error(fixes_resource, WL_FIXES_ERROR_INVALID_ACK_REMOVE, + "the given registry did not announce global %u", global_name); + return; + } + + if (!offer->global->unpublished) { + wl_resource_post_error(fixes_resource, WL_FIXES_ERROR_INVALID_ACK_REMOVE, + "global %u is not removed", global_name); + return; + } + + wl_global_offer_done(offer); +} + /** \endcond */ /* Functions at the end of this file are deprecated. Instead of adding new diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/src/wayland-util.c new/wayland-1.26.0/src/wayland-util.c --- old/wayland-1.25.0/src/wayland-util.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/src/wayland-util.c 2026-07-16 17:24:51.000000000 +0200 @@ -424,10 +424,12 @@ union map_entry entry, *start; size_t count; - start = (union map_entry *) entries->data; - count = entries->size / sizeof(union map_entry); + for (size_t idx = 0; ; idx++) { + count = entries->size / sizeof(union map_entry); + if (idx >= count) + break; - for (size_t idx = 0; idx < count; idx++) { + start = (union map_entry *) entries->data; entry = start[idx]; if (entry.data && !map_entry_is_free(entry)) { ret = func(map_entry_get_data(entry), data, map_entry_get_flags(entry)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/tests/data/small-code-core.c new/wayland-1.26.0/tests/data/small-code-core.c --- old/wayland-1.25.0/tests/data/small-code-core.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/tests/data/small-code-core.c 2026-07-16 17:24:51.000000000 +0200 @@ -35,6 +35,8 @@ static const struct wl_interface *small_test_types[] = { NULL, + NULL, + NULL, &intf_not_here_interface, NULL, NULL, @@ -46,7 +48,7 @@ static const struct wl_message intf_A_requests[] = { { "rq1", "sun", small_test_types + 0 }, - { "rq2", "nsiufho", small_test_types + 1 }, + { "rq2", "nsiufho", small_test_types + 3 }, { "destroy", "", small_test_types + 0 }, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/tests/data/small-code.c new/wayland-1.26.0/tests/data/small-code.c --- old/wayland-1.25.0/tests/data/small-code.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/tests/data/small-code.c 2026-07-16 17:24:51.000000000 +0200 @@ -35,6 +35,8 @@ static const struct wl_interface *small_test_types[] = { NULL, + NULL, + NULL, &intf_not_here_interface, NULL, NULL, @@ -46,7 +48,7 @@ static const struct wl_message intf_A_requests[] = { { "rq1", "sun", small_test_types + 0 }, - { "rq2", "nsiufho", small_test_types + 1 }, + { "rq2", "nsiufho", small_test_types + 3 }, { "destroy", "", small_test_types + 0 }, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/tests/data/small-private-code.c new/wayland-1.26.0/tests/data/small-private-code.c --- old/wayland-1.25.0/tests/data/small-private-code.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/tests/data/small-private-code.c 2026-07-16 17:24:51.000000000 +0200 @@ -45,6 +45,8 @@ static const struct wl_interface *small_test_types[] = { NULL, + NULL, + NULL, &intf_not_here_interface, NULL, NULL, @@ -56,7 +58,7 @@ static const struct wl_message intf_A_requests[] = { { "rq1", "sun", small_test_types + 0 }, - { "rq2", "nsiufho", small_test_types + 1 }, + { "rq2", "nsiufho", small_test_types + 3 }, { "destroy", "", small_test_types + 0 }, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/tests/display-test.c new/wayland-1.26.0/tests/display-test.c --- old/wayland-1.25.0/tests/display-test.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/tests/display-test.c 2026-07-16 17:24:51.000000000 +0200 @@ -51,6 +51,63 @@ #include "tests-server-protocol.h" #include "tests-client-protocol.h" +static void +registry_handle_global_noop(void *data, + struct wl_registry *registry, + uint32_t id, const char *interface, + uint32_t ver) +{ +} + +static void +registry_handle_global_remove_noop(void *data, + struct wl_registry *registry, + uint32_t id) +{ +} + +static void +fixes_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +fixes_destroy_registry(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *registry) +{ + wl_resource_destroy(registry); +} + +static void +fixes_ack_global_remove(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *registry, + uint32_t global_id) +{ + wl_fixes_handle_ack_global_remove(resource, registry, global_id); +} + +static const struct wl_fixes_interface fixes_implementation = { + .destroy = fixes_destroy, + .destroy_registry = fixes_destroy_registry, + .ack_global_remove = fixes_ack_global_remove, +}; + +static void +bind_fixes(struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(client, &wl_fixes_interface, version, id); + wl_resource_set_implementation(resource, &fixes_implementation, NULL, NULL); +} + struct display_destroy_listener { struct wl_listener listener; int done; @@ -1692,6 +1749,172 @@ wl_global_destroy(global); + display_destroy(d); +} + +static void +registry_ack_global_remove_handle_global_remove(void *data, + struct wl_registry *registry, + uint32_t id) +{ + struct client *client = data; + + wl_fixes_ack_global_remove(client->wl_fixes, registry, id); +} + +static struct wl_registry_listener registry_ack_global_remove_listener = { + .global = registry_handle_global_noop, + .global_remove = registry_ack_global_remove_handle_global_remove, +}; + +static void +ack_global_remove_client(void *data) +{ + struct client *c = client_connect(); + struct wl_registry *registry; + struct wl_registry_listener *registry_listener = data; + int ret; + + registry = wl_display_get_registry(c->wl_display); + wl_registry_add_listener(registry, + registry_listener, + c); + + ret = wl_display_roundtrip(c->wl_display); + assert(ret >= 0); + + /* yield the control back to the compositor so it can remove + * the global */ + assert(stop_display(c, 1) >= 0); + + /* check if there are any global_remove events */ + ret = wl_display_roundtrip(c->wl_display); + assert(ret >= 0); + + /* yield the control back to the compositor so it can check + * whether the global is withdrawn */ + assert(stop_display(c, 1) >= 0); + + wl_registry_destroy(registry); + + client_disconnect(c); +} + +static void +mark_global_withdrawn(struct wl_global *global) +{ + bool *withdrawn = wl_global_get_user_data(global); + + *withdrawn = true; +} + +TEST(ack_global_remove) +{ + struct display *d; + struct wl_global *seat; + struct wl_global *fixes; + bool withdrawn = false; + + d = display_create(); + + fixes = wl_global_create(d->wl_display, &wl_fixes_interface, + 2, NULL, bind_fixes); + + seat = wl_global_create(d->wl_display, &wl_seat_interface, + 1, &withdrawn, NULL); + + wl_global_set_withdrawn_listener(seat, mark_global_withdrawn); + + client_create(d, ack_global_remove_client, + ®istry_ack_global_remove_listener); + + display_run(d); + assert(!withdrawn); + + wl_global_remove(seat); + + /* the global will be marked as withdrawn after the client + * acknowledges the wl_registry.global_remove event */ + display_resume(d); + assert(withdrawn); + + /* the global will be still marked as withdrawn even after + * the client disconnects */ + display_resume(d); + assert(withdrawn); + + wl_global_destroy(seat); + wl_global_destroy(fixes); + + display_destroy(d); +} + +static struct wl_registry_listener registry_no_ack_global_remove_listener = { + .global = registry_handle_global_noop, + .global_remove = registry_handle_global_remove_noop, +}; + +TEST(no_ack_global_remove) +{ + struct display *d; + struct wl_global *seat; + struct wl_global *fixes; + bool withdrawn = false; + + d = display_create(); + + fixes = wl_global_create(d->wl_display, &wl_fixes_interface, + 2, NULL, bind_fixes); + + seat = wl_global_create(d->wl_display, &wl_seat_interface, + 1, &withdrawn, NULL); + + wl_global_set_withdrawn_listener(seat, mark_global_withdrawn); + + client_create(d, ack_global_remove_client, + ®istry_no_ack_global_remove_listener); + + display_run(d); + assert(!withdrawn); + + wl_global_remove(seat); + + /* if the client does not ack the wl_registry.global_remove event, + * the global will not be marked as withdrawn */ + display_resume(d); + assert(!withdrawn); + + /* when the client disconnects, the wl_registry.global_remove will + * be implicitly acknowledged and the global can be destroyed then */ + display_resume(d); + assert(withdrawn); + + wl_global_destroy(seat); + wl_global_destroy(fixes); + + display_destroy(d); +} + +TEST(global_remove_without_clients) +{ + struct display *d; + struct wl_global *global; + bool withdrawn = false; + + d = display_create(); + + global = wl_global_create(d->wl_display, &wl_seat_interface, + 1, &withdrawn, NULL); + + wl_global_set_withdrawn_listener(global, mark_global_withdrawn); + + /* if there are no clients at all, the global can be destroyed + * immediately */ + wl_global_remove(global); + assert(withdrawn); + + wl_global_destroy(global); + display_destroy(d); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/tests/test-compositor.c new/wayland-1.26.0/tests/test-compositor.c --- old/wayland-1.25.0/tests/test-compositor.c 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/tests/test-compositor.c 2026-07-16 17:24:51.000000000 +0200 @@ -491,14 +491,15 @@ { struct client *c = data; - if (strcmp(intf, "test") != 0) - return; - - c->tc = wl_registry_bind(registry, id, &test_compositor_interface, ver); - assert(c->tc && "Failed binding to registry"); - - wl_proxy_add_listener((struct wl_proxy *) c->tc, - (void *) &tc_listener, c); + if (strcmp(intf, "test") == 0) { + c->tc = wl_registry_bind(registry, id, &test_compositor_interface, ver); + assert(c->tc && "Failed binding to registry"); + + wl_proxy_add_listener((struct wl_proxy *) c->tc, + (void *) &tc_listener, c); + } else if (strcmp(intf, wl_fixes_interface.name) == 0) { + c->wl_fixes = wl_registry_bind(registry, id, &wl_fixes_interface, 2); + } } static const struct wl_registry_listener registry_listener = @@ -524,6 +525,9 @@ wl_display_roundtrip(c->wl_display); assert(c->tc); + if (c->wl_fixes) { + wl_fixes_destroy_registry(c->wl_fixes, reg); + } wl_registry_destroy(reg); return c; @@ -556,6 +560,10 @@ /* check for errors */ check_error(c->wl_display); + if (c->wl_fixes) { + wl_fixes_destroy(c->wl_fixes); + } + wl_proxy_destroy((struct wl_proxy *) c->tc); wl_display_disconnect(c->wl_display); free(c); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wayland-1.25.0/tests/test-compositor.h new/wayland-1.26.0/tests/test-compositor.h --- old/wayland-1.25.0/tests/test-compositor.h 2026-03-19 17:03:08.000000000 +0100 +++ new/wayland-1.26.0/tests/test-compositor.h 2026-07-16 17:24:51.000000000 +0200 @@ -66,6 +66,7 @@ * filled. */ struct client { struct wl_display *wl_display; + struct wl_fixes *wl_fixes; struct test_compositor *tc; atomic_bool display_stopped;
