Compare commits

...

78 commits

Author SHA1 Message Date
Sebastian Wick
7fc400ffff protocol: Define Content Update behavior
The protocol currently is in a state where we define that commits create
content updates and they are queued up until they are applied, and the
old view that commit applies the state or caches it in the parent state.

This commit moves the protocol completely to the new model which retains
the old behavior when no constraints are being used but allows for
constraints to be used to hold back a group of synchronized content
updates.

To convince yourself that this indeed retains the original behavior I
suggest to play around with a few examples and look at the resulting
graphs, as is done here:

https://gitlab.freedesktop.org/wayland/wayland/-/issues/457#note_2403135

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-10-27 22:15:26 +01:00
Isaac Freund
d81525a235 client: add wl_display_dispatch_pending_single
As well as wl_display_dispatch_queue_pending_single.

The motivation is writing libwayland bindings for a dynamic language
with exceptions/non-local returns. Since it is invalid for a
wl_dispatcher_func_t callback provided to libwayland to not return,
there is no way to prevent dispatching of further events in the case of
an exception in the dynamic language event handler.

Furthermore, since creating/destroying Wayland objects in an event
handler affects the dispatching of subsequent events by libwayland,
it is not possible to collect Wayland events in a queue outside
libwayland and dispatch them one-by-one after
wl_display_dispatch_pending() returns.

Adding libwayland API to dispatch at most one pending event solves this
problem cleanly. The bindings can have libwayland dispatch a single
event, wait for wl_display_dispatch_pending_single() to return, run the
dynamic language event handler (which may longjmp away), and continue
the loop for as long as there are more events to dispatch.

References: https://codeberg.org/ifreund/janet-wayland
Signed-off-by: Isaac Freund <mail@isaacfreund.com>
2025-09-16 11:48:33 +03:00
Kyle Brenneman
4673ef7e9c connection: Add a thread ID to WAYLAND_DEBUG output.
If WAYLAND_DEBUG contains the token "thread_id", and gettid() is
available, then include the current thread ID in the output from
wl_closure_print.

If multiple threads are sending requests, then those requests can get
interleaved. That's usually fine, but for wl_surface requests and
commits, that can cause problems ranging from incorrect behavior to
protocol errors.

Being able to see which requests are sent by different threads would
make such problems much easier to diagnose.

Signed-off-by: Kyle Brenneman <kbrenneman@nvidia.com>
2025-09-15 14:45:53 +01:00
Kyle Brenneman
77730f10a0 connection: Add a function to parse WAYLAND_DEBUG tokens
Add a new function, wl_check_env_token, to scan for a token in a
comma-separated string.

Change wl_display_create in wayland-server.c and
wl_display_connect_to_fd in wayland-client.c to use that instead of a
simple substring search.

This means that WAYLAND_DEBUG will accept a value like "client,server"
but not "clientserver". But, this will make it easier to add other
tokens without worrying about overlap between them.

Signed-off-by: Kyle Brenneman <kbrenneman@nvidia.com>
2025-09-15 14:45:53 +01:00
YaoBing Xiao
264da6a92b cursor: Free theme when size check fails to avoid memory leak
Signed-off-by: YaoBing Xiao <xiaoyaobing@uniontech.com>
2025-08-03 11:36:34 +00:00
ykla
cd0d1543c0 ci: upgrade FreeBSD to 14.3
Signed-off-by: ykla yklaxds@gmail.com
2025-07-20 02:09:35 +00:00
ykla
90187031e6
ci: upgrade FreeBSD to 14.2
Signed-off-by: ykla <yklaxds@gmail.com>
2025-06-25 07:18:28 +08:00
Simon Ser
eecf3f7635 build: re-open main branch for regular development
Signed-off-by: Simon Ser <contact@emersion.fr>
2025-06-21 13:38:28 +02:00
Demi Marie Obenour
adf84614ca connection: Do not busy-loop if a message exceeds the buffer size
If the length of a message exceeds the maximum length of the buffer, the
buffer size will reach its maximum value and stay there forever, with no
message ever being successfully processed.  Since libwayland uses
level-triggered epoll, this will cause the compositor to loop forever
and consume CPU time.  In libwayland 1.22 and below, there was an
explicit check that caused messages exceeding 4096 bytes to result in an
EOVERFLOW error, preventing the loop.  However, this check was removed
between d074d52902 ("connection: Dynamically resize connection buffers").

To prevent this problem, always limit the size of messages to 4096 bytes.
Since the default and minimum buffer size is 4096 bytes, this ensures
that a single message will always fit in the buffer.  It would be
possible to allow larger messages if the buffer size was larger, but the
maximum size of a message should not depend on the buffer size chosen by
the compositor.

Rejecting messages that exceed 4092 bytes seems to have the advantage of
reserving 4 bits, not 3, in the size field for future use.  However,
message sizes in the range [0x0, 0x7] are invalid, so one can obtain a
fourth bit by negating the meaning of bit 12 if bits 0 through 11
(inclusive) are 0.  Allowing 4096-byte messages provides the far more
important advantage that regressions compared to 1.22 are impossible
and regressions compared to 1.23 are extremely unlikely.  The only case
where a regression is possible is:

- The receiving side is using libwayland 1.23.
- The sending side is either using libwayland 1.23 or is not using
  libwayland.
- The sender sends a message exceeding 4096 bytes.
- If the sender of the large message is the client, the server has
  increased the buffer size from the default value.

This combination is considered extremely unlikely, as libwayland 1.22
and below would disconnect upon receiving such a large message.
4096-byte messages, however, have always worked, so there was no reason
to avoid sending them.

Fixes: d074d52902 ("connection: Dynamically resize connection buffers").
Fixes: #494
Signed-off-by: Demi Marie Obenour <demi@invisiblethingslab.com>
2025-06-21 11:29:04 +00:00
Pekka Paalanen
ba9f9a446f doc: add a section on color management
I think the docbook deserves an introduction to how color management is
designed in Wayland, aimed at people who are familiar with pixels but
new to the topic.

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
2025-06-12 14:37:32 +03:00
Matt Turner
53fbc2b0c1 egl: Make wayland-egl symbols check depend on wayland_egl
Closes: https://gitlab.freedesktop.org/wayland/wayland/-/issues/515
Signed-off-by: Matt Turner <mattst88@gmail.com>
2025-06-08 17:30:55 +00:00
Matt Turner
fdac631d17 tests: Depend on exec-fd-leak-checker
Closes: https://gitlab.freedesktop.org/wayland/wayland/-/issues/514
Signed-off-by: Matt Turner <mattst88@gmail.com>
2025-06-08 17:30:55 +00:00
Matt Turner
6c1da92018 tests: Add support for specifying runtime dependencies
Signed-off-by: Matt Turner <mattst88@gmail.com>
2025-06-08 17:30:55 +00:00
Matt Turner
ca83185e8a tests: Make tests dict elements dicts themselves
Previously each value was a list of extra sources. The next commit will add an
additional field to each test, so they need to be dicts themselves.

Signed-off-by: Matt Turner <mattst88@gmail.com>
2025-06-08 17:30:55 +00:00
Manuel Stoeckl
4a0c4e2119 doc: Further explain typical display socket lookup
This change mentions the case where WAYLAND_SOCKET is used, which helps
people avoid just testing 'getenv(WAYLAND_DISPLAY)' to see if a
Wayland compositor is available;

Signed-off-by: Manuel Stoeckl <code@mstoeckl.com>
2025-06-08 16:20:35 +00:00
Manuel Stoeckl
387adc6a79 server: Document wl_display_add_socket_auto
The exact sequence of names tried has de facto become part of the API.

Signed-off-by: Manuel Stoeckl <code@mstoeckl.com>
2025-06-08 16:20:35 +00:00
Tobias Stoeckmann
0de833da29 cursor: Properly check realloc for errors
Do not override realloc's input pointer before checking for errors,
otherwise it's not possible to keep old value, as intended.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-06-08 16:16:09 +00:00
Tobias Stoeckmann
2978fd701a cursor: Ignore invalid cursor files
The header offset must not be smaller than file header length.
Ignore such invalid files.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-06-08 16:16:09 +00:00
Tobias Stoeckmann
5c2f31d8d6 cursor: Gracefully handle huge cursor files
If cursor files require more than INT_MAX bytes, it is possible to
trigger out of boundary writes.

Since these sizes are most likely not desired anyway, gracefully
handle these situations like out of memory errors.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-06-08 16:16:09 +00:00
Tobias Stoeckmann
ce0ac4f29e cursor: Gracefully handle out of memory condition
If the full path could not be constructed, avoid calling opendir(NULL)
which, depending on library, might trigger undefined behavior.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-06-08 16:16:09 +00:00
Tobias Stoeckmann
1bee7aa4a7 cursor: Fix undefined behavior with huge names
If an index.theme contains a theme name which gets close to INT_MAX,
then creation of full path can lead to a signed integer overflow,
which is undefined behavior.

Fix this by turning one of the values to size_t. Easy solution for a
probably never occurring issue.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-06-08 16:16:09 +00:00
Kirill Primak
6281ccbd3d client: fix conversion specifier in the discarded event log message
Signed-off-by: Kirill Primak <vyivel@eclair.cafe>
2025-06-01 14:56:34 +03:00
Caitlyn
ecff0ee10c debug: Colorize output for easier reading
Signed-off-by: Caitlyn <caitlynrosestewart@gmail.com>
2025-06-01 11:21:36 +00:00
Caitlyn Stewart
827d0c30ad connection: fix segfault in wl_closure_invoke()
Signed-off-by: Caitlyn Stewart <caitlynrosestewart@gmail.com>
2025-05-27 14:45:32 +01:00
Simon Ser
62cd0990e8 build: bump version to 1.23.90 for the RC1 release
Signed-off-by: Simon Ser <contact@emersion.fr>
2025-05-22 21:00:30 +02:00
Simon Ser
9b169ff945 protocol: drop reference to linux-explicit-synchronization
This protocol has been superseded. Replace this outdated reference
with a generic hint that protocol extensions may provide this
functionality.

Signed-off-by: Simon Ser <contact@emersion.fr>
2025-05-20 20:57:52 +00:00
Tobias Stoeckmann
cc06c3825f Fix typos
Typos found with codespell and during code audit.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-05-20 20:49:32 +00:00
Isaac Freund
8cad6f7b82 server: add wl_resource_get_interface()
This is useful for the wayland bindings/scanner I'm working on for a
dynamically typed language.

Signed-off-by: Isaac Freund <mail@isaacfreund.com>
2025-05-20 20:31:16 +00:00
Isaac Freund
4497232102 client: add wl_proxy_get_interface()
This is useful for the wayland bindings/scanner I'm working on for a
dynamically typed language.

Signed-off-by: Isaac Freund <mail@isaacfreund.com>
2025-05-20 20:31:16 +00:00
David Edmundson
3214f858e2 protocol: Clarify sending of wl_seat.capabilities
It wasn't explicitly stated that wl_seat.capabilities should also
be sent on bind. Everyone did because it was obviously sensible.

This also clarifies that static seat name should be sent before
announcing capabilities so clients can associate these devices with the
right seat name.

Signed-off-by: David Edmundson <davidedmundson@kde.org>
2025-05-20 20:20:13 +00:00
Simon Ser
66fc3f007d shm: linkify function references in docs
Parentheses make it so the generated HTML documentation contains
links, which makes navigation easier.

Signed-off-by: Simon Ser <contact@emersion.fr>
2025-05-20 20:14:52 +00:00
Simon Ser
9dd1b2d7e3 shm: fix comment about wl_shm_buffer_begin_access() safety
The paragraph later says that accessing different buffers is
allowed. The function checks whether the same pool is accessed.

Signed-off-by: Simon Ser <contact@emersion.fr>
2025-05-20 20:14:52 +00:00
Sebastian Wick
d2a3d33063 shm: Generate an error when shm access failed even without a resource
Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-05-20 21:50:22 +02:00
Sebastian Wick
9367c4da76 shm: Add wl_shm_buffer ref and unref functions
Shared memory buffers are currently tied to the lifetime of their
underlying wl_buffer resource. This becomes problematic when the client
destroys the resource after committing new state which references the
wl_buffer because a compositor might have to defer applying the commit.

This commit adds methods to keep the wl_shm_buffer alive longer than the
underlying resource. This implicitly also keeps the buffer pool alive
and because the wl_shm_buffer uses offsets into the pool, it even works
when the underlying storage gets remapped somewhere else, which can
happen when the client resizes the pool.

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-05-20 21:50:22 +02:00
Sebastian Wick
af453f876e shm: Remove refcount check which cannot be triggered
If the pool refcount reaches zero, it is freed, so accessing its members
is UB which ASan would catch.

Also simplify check for negative refcounts.

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-05-20 21:50:09 +02:00
Sebastian Wick
9ec01ab2dc shm: Linkify wl_shm_pool_unref in the ref_pool documentation
Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-05-20 21:30:56 +02:00
Michel Dänzer
6137c8c213 protocol: Clarify wl_buffer.release description
Sebastian pointed out that the existing text could be read as
wl_buffer.destroy not being allowed before the wl_buffer.release event
arrives, contrary to what the wl_surface.attach description says.
Clarify to be consistent with the latter.

This is a follow-up for
https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/141 .

Signed-off-by: Michel Dänzer <mdaenzer@redhat.com>

v2:
* Simplify clarification, don't talk about callbacks. (Julian Orth)
* Add reference to details in the description of wl_surface.attach.
  (Daniel Stone)
v3:
* Tweak clarification again. (Sebastian Wick)
v4:
* Make clarification even less ambiguous. (Simon Ser, Julian Orth)
v5:
* Just refer to the description of wl_surface.attach instead of trying
  to clarify anything here. (Sebastian Wick)
2025-03-15 23:12:07 +00:00
Julian Orth
7033e74857 client: document get_listener behavior for dispatchers
This seems to have been the case since 2013.

This is useful for wrappers that need two pointers to identify proxies.
One pointer (stored in the user data) pointing to a singleton object to
identify that the proxy has a known structure. And one pointer (stored
in the dispatcher data) pointing to per-proxy data.

Signed-off-by: Julian Orth <ju.orth@gmail.com>
2025-03-15 23:07:39 +00:00
Simon Ser
dbfa8d784e scanner: use separate guards for validator functions
Generated XXX_is_valid() functions for enums are guarded behind the
same #define as the enum itself. This worked fine until recently,
but since fbd7460737 ("scanner: add new enum-header mode") we're
also generating enum-only headers.

When including the enum-only header first, and then the server
header, the validator functions are missing.

Define a separate guard to fix this.

Signed-off-by: Simon Ser <contact@emersion.fr>
2025-02-23 23:38:15 +01:00
Vlad Zahorodnii
1ab6b693b1 Forward declarate timespec struct
The `timespec` struct is defined in `time.h` header but only if
`_POSIX_C_SOURCE` is set or when using the C11 standard.

Signed-off-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
2025-02-06 10:18:17 +00:00
David Redondo
afd498b6f5 Also use [[deprecated]] when compiling with at least C++14
Signed-off-by: David Redondo <kde@david-redondo.de>
2025-02-05 09:43:21 +01:00
David Redondo
7c2ffb0d71 Make wayland-util.h -Wundef safe when compiled by a C++ compiler
Fixes #522
Signed-off-by: David Redondo <kde@david-redondo.de>
2025-02-05 09:21:43 +01:00
Daniel Stone
02ad102e2d build: Add -lm to pkg-config dependencies
Now that wl_fixed_from_double() calls round() from a function declared
in a header, our users need to explicitly pick that dependency up in
order to avoid build errors.

Signed-off-by: Daniel Stone <daniels@collabora.com>
Closes: wayland/weston#991
2025-02-05 06:52:53 +00:00
Sebastian Wick
74f322c35a tests: Add dispatch timeout tests
Add tests which verify that...

* wl_display_dispatch_timeout with a big enough timeout behaves the same
  as wl_display_dispatch
* wl_display_dispatch_timeout will time out when there are no messages
  to dispatch

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-02-04 14:09:51 +00:00
Sebastian Wick
00dcf6b323 client: Add wl_display_dispatch_timeout
A variant of wl_display_dispatch_queue_timeout for the default queue.

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-02-04 14:09:51 +00:00
Sebastian Wick
ddd348da7e client: Add wl_display_dispatch_queue_timeout
For dispatching messages on a queue with a timeout.

This slightly changes the samantics of wl_display_dispatch. Previously
it was possible for it to return even though there wasn't a single
dispatched event. The function correctly returned 0 in this case but it
is now used to indicate a timeout.

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-02-04 14:09:51 +00:00
Sebastian Wick
ff8b885523 event-loop: Use timespec utils instead of hand-rolling our own
Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-02-04 14:09:51 +00:00
Sebastian Wick
893e4fc46d timespec: Implement saturating timespec substraction
Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-02-04 14:09:51 +00:00
Sebastian Wick
9d5de6062b timespec: Pull in timespec_after and timespec_add from mesa
Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-02-04 14:09:51 +00:00
Sebastian Wick
37469d5ced timespec: Pull in timespec.h from weston
Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-02-04 14:09:51 +00:00
Sebastian Wick
bdba21ec92 server: add const qualifier to function arguments where possible
Makes it possible to e.g. `call wl_client_get_credentials` with a `const
struct wl_client *` from a global filter callback.

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2025-01-22 14:28:50 +00:00
Daniel Stone
597a6b94f5 ci: Update ci-templates
This includes an explicit way to specify the container architecture,
which fixes our rebuilds on ARMv7.

Signed-off-by: Daniel Stone <daniels@collabora.com>
2025-01-21 16:43:45 +00:00
Haihua Hu
f246e619d1 util: reduce error of wl_fixed_from_double()
when cast double to fixed pointer, there will be big
error, eg 1919.9998 to 1919. Call round before cast
to get nearest value 1920 of 1919.9998

Signed-off-by: Haihua Hu <jared.hu@nxp.com>
2025-01-09 09:58:32 +09:00
Demi Marie Obenour
9cb3d7aa9d connection: Fix wrong format string
Prevents undefined behavior if there is not enough space in the buffer
for a queued message.

Signed-off-by: Demi Marie Obenour <demi@invisiblethingslab.com>
2024-11-30 16:02:55 -05:00
Demi Marie Obenour
290c36bc50 tests: Avoid calling function with wrong type
Calling a function with the wrong type is immediate undefined behavior,
even if the ABI says it should be harmless.  UBSAN picks it up
immediately, and any decent control-flow integrity mechanism will as
well.

Signed-off-by: Demi Marie Obenour <demi@invisiblethingslab.com>
2024-11-30 11:31:36 -05:00
Demi Marie Obenour
4273a5edc8 connection: Avoid undefined pointer arithmetic
Creating a pointer that is more than one element past the end of an
array is undefined behavior, even if the pointer is not dereferenced.
Avoid this undefined behavior by using `p >= end` instead of
`p + 1 > end` and `SOMETHING > end - p` instead of
`p + SOMETHING > end`.

Signed-off-by: Demi Marie Obenour <demi@invisiblethingslab.com>
2024-11-29 19:19:45 -05:00
Julian Orth
10df74c240 protocol: add wl_fixes interface
This commit describes a new wl_fixes interface that can be used to
destroy wl_registry objects.

Users of libwayland-client should use it as follows:

- call wl_fixes_destroy_registry(registry)
- call wl_registry_destroy(registry)

Users of libwayland-server should, in their implementation of the
request, call wl_resource_destroy(registry).

It should be similar in other protocol implementations.

Signed-off-by: Julian Orth <ju.orth@gmail.com>
2024-11-18 09:25:20 +00:00
YaoBing Xiao
f67db75ec1 cursor: add check to ensure wl_shm_create_pool succeeded
Signed-off-by: YaoBing Xiao <xiaoyaobing@uniontech.com>
2024-10-18 16:49:45 +08:00
Simon Ser
38f91fe6ad protocol: document that wl_surface.offset is role-specific
This request doesn't make sense for all surface roles. For instance,
for maximized/tiled/fullscreen xdg_toplevel, for xdg_popup, for
layer-shell surfaces, etc.

Signed-off-by: Simon Ser <contact@emersion.fr>
2024-10-05 14:06:51 +00:00
Andri Yngvason
1b0d45e9c6 Add wl_keyboard key repeat events
This allows the compositor to take over the responsibility of repeating
keys.

Signed-off-by: Andri Yngvason <andri@yngvason.is>
2024-09-23 15:23:45 +00:00
Julian Orth
7c6259e9ad protocol: clients should not emulate key-press events on enter
The previous change introducing the logical state caused some confusion.
Clarify that most application should not use the list of pressed keys.

Signed-off-by: Julian Orth <ju.orth@gmail.com>
2024-09-10 07:36:48 +00:00
Demi Marie Obenour
6c4a695045 connection: Reject strings containing NUL bytes
libwayland cannot construct these messages as it uses strlen() to
determine string lengths.  libwayland is also guaranteed to misinterpret
these messages, since message handlers only get a pointer and no length.
Therefore, reject strings containing NUL bytes.

Also remove a redundant check from the unmarshalling code.  The
zero-length case has already been checked for.

Signed-off-by: Demi Marie Obenour <demi@invisiblethingslab.com>
2024-08-18 17:08:56 +00:00
Joaquim Monteiro
0239b082b9
meson: Fix use of install_data() without specifying install_dir
This was broken (when in a subproject) before Meson 1.3.0, and so
Meson warns against this unless the project targets 1.3.0 or newer.

Signed-off-by: Joaquim Monteiro <joaquim.monteiro@protonmail.com>
2024-08-15 14:13:57 +01:00
Fangzhou Ge
5b692b50b9 client: Log the object and methods when marshalling or sending fails
The log that appears before a display_error can be captured as crash
signature. Useful to know what it is.

This is cherry-picked from chromium https://crrev.com/c/4697877

Signed-off-by: Fangzhou Ge <fangzhoug@chromium.org>
2024-08-12 15:49:14 -04:00
Simon Ser
efa648056a ci: use detached MR pipelines
See the freedesktop wiki [1]. This allows external contributors to
have CI run properly.

[1]: https://gitlab.freedesktop.org/freedesktop/freedesktop/-/wikis/GitLab-CI#for-project-developers

Signed-off-by: Simon Ser <contact@emersion.fr>
2024-08-09 20:46:58 +00:00
Derek Foreman
58bb6c7211 src: Finish assert() clean-up
From cleanup commit 0cecde304:
assert()s can be compiled away by #defining NDEBUG. Some build systems
do this. Using wl_abort gives a human readable error message and it
isn't compiled away.

That commit missed one final assert, presumably due to missing it with
grep because of a coding style issue. Fix that up, and remove inclusion
of <assert.h> as appropriate.

Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
2024-08-09 20:38:52 +00:00
Sebastian Wick
2bbd80c8df doc: Require strings to be UTF-8
Nothing checks this yet but this gives us the opportunity to do so when
we want.

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2024-08-09 20:33:05 +00:00
Kirill Primak
65454cf7db server: expose wl_resource_post_error_vargs()
Signed-off-by: Kirill Primak <vyivel@eclair.cafe>
2024-08-09 20:29:24 +00:00
Kirill Primak
64248963d3 server: add wl_resource_post_error() docs
Signed-off-by: Kirill Primak <vyivel@eclair.cafe>
2024-08-09 20:29:24 +00:00
Kirill Primak
a6a4e081da Put WL_DEPRECATED in front of the function declarations
This fixes the following clang error when using C23:

../src/wayland-server-core.h:680:41: error: 'deprecated' attribute cannot be applied to types
  680 |                      int32_t stride, uint32_t format) WL_DEPRECATED;
      |                                                       ^
../src/wayland-util.h:52:25: note: expanded from macro 'WL_DEPRECATED'
   52 | #define WL_DEPRECATED [[deprecated]]
      |                         ^

Signed-off-by: Kirill Primak <vyivel@eclair.cafe>
2024-08-09 20:25:11 +00:00
Sebastian Wick
f6f0a3cdec client: Handle proxies with no queue
wl_proxy_get_queue can return NULL if the queue of the proxy was already
destroyed with wl_event_queue_destroy. In this case, the queue also has
no name anymore.

Fixes: b42218f ("client: Allow setting names for queues")
Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
2024-07-26 18:33:28 +02:00
meltq
0cecde304f src: switch asserts to wl_abort
assert()s can be compiled away by #defining NDEBUG. Some build systems
do this. Using wl_abort gives a human readable error message and it
isn't compiled away. This commit closes issue #230.

Signed-off-by: meltq <tejasvipin76@gmail.com>
2024-07-11 17:44:04 +00:00
Simon Ser
fa1811ce3e tests: add enum bitfield test
Signed-off-by: Simon Ser <contact@emersion.fr>
2024-07-09 18:22:10 +02:00
Simon Ser
c669d99259 scanner: fix validator for bitfields
Bitfields are valid if the value only contains bits inside of
the supported entries for the given version.

Signed-off-by: Simon Ser <contact@emersion.fr>
2024-07-09 18:22:10 +02:00
Simon Ser
caaa308c0d scanner: extract validator function emission to helper function
This function will grow in the next commit.

Signed-off-by: Simon Ser <contact@emersion.fr>
2024-07-09 18:22:10 +02:00
Simon Ser
1d5772b7b9 build: re-open main branch for regular development
Signed-off-by: Simon Ser <contact@emersion.fr>
2024-05-30 21:07:24 +02:00
Simon Ser
a156431ea6 build: bump to version 1.23.0 for the official release
Signed-off-by: Simon Ser <contact@emersion.fr>
2024-05-30 20:59:51 +02:00
Hugo Osvaldo Barrera
26c419e046 protocol: clarify divergence in compositor behaviour
This is intended to only document the current situation. Whether further
behaviour will be defined is out of scope and left for protocol v7.

See: https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/363
Signed-off-by: Hugo Osvaldo Barrera <hugo@whynothugo.nl>
2024-05-29 14:23:38 +02:00
38 changed files with 2042 additions and 475 deletions

View file

@ -43,7 +43,7 @@ include:
# API changes. If you need new features from ci-templates you must bump
# this to the current SHA you require from the ci-templates repo, however
# be aware that you may need to account for API changes when doing so.
ref: b791bd48996e3ced9ca13f1c5ee82be8540b8adb
ref: 48c2c583a865bd59be21e8938df247faf460099c
file:
- '/templates/debian.yml'
- '/templates/freebsd.yml'
@ -62,6 +62,13 @@ stages:
- "Build and test"
- "Other build configurations"
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
.ci-rules:
rules:
- when: on_success
@ -75,7 +82,7 @@ stages:
FDO_DISTRIBUTION_EXEC: 'pip3 install --break-system-packages meson~=0.57.2'
# bump this tag every time you change something which requires rebuilding the
# base image
FDO_DISTRIBUTION_TAG: "2024-03-28.2"
FDO_DISTRIBUTION_TAG: "2025-01-21.1"
.debian-x86_64:
extends:
@ -94,6 +101,7 @@ stages:
- .os-debian
variables:
BUILD_ARCH: "armv7"
FDO_DISTRIBUTION_PLATFORM: "linux/arm/v7"
# Does not inherit .ci-rules as we only want it to run in MR context.
@ -147,7 +155,6 @@ armv7-debian-container_prep:
stage: "Base container"
variables:
GIT_STRATEGY: none
FDO_BASE_IMAGE: "arm32v7/debian:$FDO_DISTRIBUTION_VERSION"
# Core build environment.
@ -299,11 +306,11 @@ armv7-release-debian-build:
.os-freebsd:
variables:
BUILD_OS: freebsd
FDO_DISTRIBUTION_VERSION: "13.2"
FDO_DISTRIBUTION_VERSION: "14.3"
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: "2023-08-02.0"
FDO_DISTRIBUTION_TAG: "2025-07-20.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"

View file

@ -29,7 +29,6 @@
* http://fontforge.org/pcf-format.html
*/
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>

View file

@ -27,6 +27,7 @@
#include "xcursor.h"
#include "wayland-cursor.h"
#include "wayland-client.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
@ -68,11 +69,16 @@ shm_pool_create(struct wl_shm *shm, int size)
goto err_close;
pool->pool = wl_shm_create_pool(shm, pool->fd, size);
if (!pool->pool)
goto err_unmap;
pool->size = size;
pool->used = 0;
return pool;
err_unmap:
munmap(pool->data, size);
err_close:
close(pool->fd);
err_free:
@ -279,7 +285,8 @@ wl_cursor_create_from_xcursor_images(struct xcursor_images *images,
{
struct cursor *cursor;
struct cursor_image *image;
int i, size;
size_t size;
int i;
cursor = malloc(sizeof *cursor);
if (!cursor)
@ -309,7 +316,12 @@ wl_cursor_create_from_xcursor_images(struct xcursor_images *images,
image->image.hotspot_y = images->images[i]->yhot;
image->image.delay = images->images[i]->delay;
size = image->image.width * image->image.height * 4;
size = (size_t) image->image.width * image->image.height * 4;
if (size > INT_MAX) {
free(image);
break;
}
image->offset = shm_pool_allocate(theme->pool, size);
if (image->offset < 0) {
free(image);
@ -339,6 +351,8 @@ load_callback(struct xcursor_images *images, void *data)
{
struct wl_cursor_theme *theme = data;
struct wl_cursor *cursor;
struct wl_cursor **p;
size_t s;
if (wl_cursor_theme_get_cursor(theme, images->name)) {
xcursor_images_destroy(images);
@ -348,15 +362,14 @@ load_callback(struct xcursor_images *images, void *data)
cursor = wl_cursor_create_from_xcursor_images(images, theme);
if (cursor) {
theme->cursor_count++;
theme->cursors =
realloc(theme->cursors,
theme->cursor_count * sizeof theme->cursors[0]);
s = theme->cursor_count + 1;
p = realloc(theme->cursors, s * sizeof theme->cursors[0]);
if (theme->cursors == NULL) {
theme->cursor_count--;
if (p == NULL) {
free(cursor);
} else {
theme->cursor_count = s;
theme->cursors = p;
theme->cursors[theme->cursor_count - 1] = cursor;
}
}
@ -384,6 +397,9 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
if (!theme)
return NULL;
if (size < 0 || (size > 0 && INT_MAX / size / 4 < size))
goto err;
if (!name)
name = "default";
@ -393,7 +409,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
theme->pool = shm_pool_create(shm, size * size * 4);
if (!theme->pool)
goto out_error_pool;
goto err;
xcursor_load_theme(name, size, load_callback, theme);
@ -405,7 +421,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
return theme;
out_error_pool:
err:
free(theme);
return NULL;
}

View file

@ -259,6 +259,8 @@ xcursor_read_file_header(FILE *file)
return NULL;
if (!xcursor_read_uint(file, &head.ntoc))
return NULL;
if (head.header < XCURSOR_FILE_HEADER_LEN)
return NULL;
skip = head.header - XCURSOR_FILE_HEADER_LEN;
if (skip)
if (fseek(file, skip, SEEK_CUR) == EOF)
@ -571,7 +573,7 @@ xcursor_build_theme_dir(const char *dir, const char *theme)
* add space for any needed directory separators, one per component,
* and one for the trailing null
*/
full_size = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
full_size = (size_t) 1 + homelen + 1 + dirlen + 1 + themelen + 1;
full = malloc(full_size);
if (!full)
return NULL;
@ -686,11 +688,15 @@ load_all_cursors_from_dir(const char *path, int size,
void *user_data)
{
FILE *f;
DIR *dir = opendir(path);
DIR *dir;
struct dirent *ent;
char *full;
struct xcursor_images *images;
if (!path)
return;
dir = opendir(path);
if (!dir)
return;
@ -798,14 +804,14 @@ xcursor_load_theme_protected(const char *theme, int size,
free(xcursor_path);
}
/** Load all the cursor of a theme
/** Load all the cursors of a theme
*
* This function loads all the cursor images of a given theme and its
* inherited themes. Each cursor is loaded into an struct xcursor_images object
* inherited themes. Each cursor is loaded into a struct xcursor_images object
* which is passed to the caller's load callback. If a cursor appears
* more than once across all the inherited themes, the load callback
* will be called multiple times, with possibly different struct xcursor_images
* object which have the same name. The user is expected to destroy the
* objects which have the same name. The user is expected to destroy the
* struct xcursor_images objects passed to the callback with
* xcursor_images_destroy().
*

View file

@ -0,0 +1,139 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "Wayland.ent">
%BOOK_ENTITIES;
]>
<chapter id="chap-Color-Management">
<title>Color management</title>
<section id="sect-Color-Management-preface">
<title>Overview</title>
<para>
Color management in Wayland considers only displays. All pictures in
Wayland are always display-referred, meaning that the pixel values are
intended as-is for some specific display where they would produce the
light emissions (<ulink
url="https://cie.co.at/eilvterm/17-23-002">stimuli</ulink>) the picture's
author desired. Wayland does not support displaying "raw" camera or
scanner images as they are not display-referred, nor are they even
pictures without complex and subjective processing.
</para>
<para>
Stimuli — the picture itself — are only half of the picture reproduction.
The other half is the environment where a display is viewed. A striking
example is comparing a brightly lit office to a dark movie theater, the
stimuli required to produce a good reading of the picture is greatly
different. Therefore display-referred does not include only the display
but the viewing environment as well.
</para>
<para>
Window systems have been very well capable of operating without any
explicit consideration to color management. This is because there used to
be the implicit assumption of the standard display, the sRGB display,
which all computer monitors implemented, more or less. The viewing
environment was and still is accounted by adjusting the display and/or the
room to produce a workable experience. Pictures are authored on a computer
system by drawing, painting and adjusting the picture until it looks right
on the author's monitor. This implicitly builds the standard display and
environment assumption into the picture data. Deviations from the sRGB
specification were minor enough that they often did not matter if not in a
professional context like the printing industry. Displaying video material
required some more attention to the details, because video and television
standards differ enough from the sRGB display. What really made explicit
color management a hard requirement for entertainment is the coming of
wide color gamut (WCG) and high dynamic range (HDR) materials and
displays.
</para>
<para>
The color management design in Wayland follows the general Wayland design
principles: compositors tell clients what would be the optimal thing to
do, clients tell the compositors what kind of pictures they are actually
producing, and then compositors display those pictures the best they can.
</para>
</section>
<section id="sect-Color-Management-Protocol">
<title>Protocol Interfaces</title>
<para>
Color management interfaces in Wayland and divided into two protocols:
<ulink url="https://gitlab.freedesktop.org/wayland/wayland-protocols/-/tree/main/staging/color-management?ref_type=heads">color-management</ulink>
and
<ulink url="https://gitlab.freedesktop.org/wayland/wayland-protocols/-/tree/main/staging/color-representation?ref_type=heads">color-representation</ulink>.
They are designed to work together, but they can also be used
independently when the other one is not needed.
</para>
<section id="sect-Color-Management-Protocol-color-management">
<title>Color-management</title>
<para>
Color management protocol has two main purposes. First, it puts the
responsibility of color management on the compositor. This means that
clients do not necessarily need to care about color management at all,
and can display just fine by using the traditional standard display
assumption even when the actual display is wildly different. Clients
can also choose to target some other assumed display and let the
compositor handle it, or they can explicitly render for the actual
display at hand. Second, when the window system has multiple different
monitors, and a wl_surface happens to span more than one monitor, the
compositor can display the surface content correctly on all spanned
monitors simultaneously, as much as physically possible.
</para>
<para>
Color-management protocol concentrates on colorimetry: when you have a
pixel with RGB values, what stimulus do those values represent. The
stimulus definition follows the CIE 1931 two-degree observer model. Some
core concepts here are color primaries, white point, transfer function,
and dynamic range. The viewing environment is represented in an
extremely simplified way as the reference white luminance. The
connection between pixel RGB values and stimulus plus viewing
environment is recorded in an <emphasis>image description</emphasis>
object. Clients can create image description objects and tag
<code>wl_surface</code>s with them, to indicate what kind of surface
content there will be. Clients can also ask what image description the
compositor would prefer to have on the <code>wl_surface</code>, and that
preference can change over time, e.g. when the <code>wl_surface</code>
is moved from one
<code>wl_output</code> to another. Following the compositor's preference
may provide advantages in image quality and power consumption.
</para>
<para>
Image description objects can come in two flavors: parametric and
ICC-based. The above was written with parametric image descriptions in
mind, and they have first-class support for HDR. ICC-based image
descriptions are wrapping an ICC profile and have no other data. ICC
profiles are the standard tool for standard dynamic range (SDR) display
color management. This means the capabilities between the two flavors
differ, and one cannot always be replaced by the other. Compositor
support for each flavor is optional.
</para>
</section>
<section id="sect-Color-Management-Protocol-color-representation">
<title>Color-representation</title>
<para>
Color-representation protocol deals with (potentially sub-sampled)
YCbCr-RGB conversion, quantization range, and the inclusion of alpha in
the RGB color channels, a.k.a. pre-multiplication. There are several
different specifications on how an YCbCr-like (including ICtCp) signal,
with chroma sub-sampling or not, is created from a full-resolution RGB
image. Again, a client can tag a <code>wl_surface</code> with
color-representation metadata to tell the compositor what kind of pixel
data will be displayed through the wl_surface.
</para>
<para>
The main purpose of color-representation is to correctly off-load the
YCbCr-RGB conversion to the compositor, which can then opportunistically
off-load it further to very power-efficient fixed-function circuitry in
a display controller. This can significantly reduce power consumption
when watching videos compared to using a GPU for the same, and on some
embedded hardware platforms it is a hard requirement for processing high
resolution video.
</para>
</section>
</section>
</chapter>

View file

@ -87,7 +87,7 @@
</para>
<para>
Overall, the philosophy of Wayland is to provide clients with a way to
manage windows and how their contents is displayed. Rendering is left
manage windows and how their contents are displayed. Rendering is left
to clients, and system wide memory management interfaces are used to
pass buffer handles between clients and the compositing manager.
</para>

View file

@ -97,7 +97,9 @@
in the environment). Beginning in Wayland 1.15, implementations can
optionally support server socket endpoints located at arbitrary
locations in the filesystem by setting <emphasis>WAYLAND_DISPLAY</emphasis>
to the absolute path at which the server endpoint listens.
to the absolute path at which the server endpoint listens. The socket may
also be provided through file descriptor inheritance, in which case
<emphasis>WAYLAND_SOCKET</emphasis> is set.
</para>
<para>
Every message is structured as 32-bit words; values are represented in the
@ -150,9 +152,10 @@
<listitem>
<para>
Starts with an unsigned 32-bit length (including null terminator),
followed by the string contents, including terminating null byte,
then padding to a 32-bit boundary. A null value is represented
with a length of 0.
followed by the UTF-8 encoded string contents, including
terminating null byte, then padding to a 32-bit boundary. A null
value is represented with a length of 0. Interior null bytes are
not permitted.
</para>
</listitem>
</varlistentry>

View file

@ -12,6 +12,7 @@
<xi:include href="Architecture.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Protocol.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Xwayland.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Color.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="ProtocolSpec.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Client.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
<xi:include href="Server.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>

View file

@ -54,6 +54,7 @@ publican_sources = [
'Protocol.xml',
'Xwayland.xml',
'Compositors.xml',
'Color.xml',
'Client.xml',
'Server.xml'
]

View file

@ -22,6 +22,7 @@ if get_option('tests')
test(
'wayland-egl symbols check',
find_program('wayland-egl-symbols-check'),
depends: wayland_egl,
env: [
'WAYLAND_EGL_LIB=@0@'.format(wayland_egl_shared.full_path()),
'NM=@0@'.format(nm_path)

View file

@ -1,6 +1,6 @@
project(
'wayland', 'c',
version: '1.22.93',
version: '1.24.90',
license: 'MIT',
meson_version: '>= 0.57.0',
default_options: [
@ -46,6 +46,7 @@ have_funcs = [
'memfd_create',
'mremap',
'strndup',
'gettid',
]
foreach f: have_funcs
config_h.set('HAVE_' + f.underscorify().to_upper(), cc.has_function(f))
@ -131,7 +132,9 @@ if get_option('scanner')
'wayland-scanner.mk',
'protocol/wayland.xml',
'protocol/wayland.dtd',
])
],
install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'wayland'),
)
install_data(
[ 'wayland-scanner.m4' ],

View file

@ -501,8 +501,10 @@
<event name="release">
<description summary="compositor releases buffer">
Sent when this wl_buffer is no longer used by the compositor.
The client is now free to reuse or destroy this buffer and its
backing storage.
For more information on when release events may or may not be sent,
and what consequences it has, please see the description of
wl_surface.attach.
If a client receives a release event before the frame callback
requested in the same wl_surface.commit that attaches this
@ -1504,7 +1506,8 @@
the delivery of wl_buffer.release events becomes undefined. A well
behaved client should not rely on wl_buffer.release events in this
case. Alternatively, a client could create multiple wl_buffer objects
from the same backing storage or use wp_linux_buffer_release.
from the same backing storage or use a protocol extension providing
per-commit release notifications.
Destroying the wl_buffer after wl_buffer.release does not change
the surface contents. Destroying the wl_buffer before wl_buffer.release
@ -1514,9 +1517,15 @@
mutates the underlying buffer storage, the surface contents become
undefined immediately.
If wl_surface.attach is sent with a NULL wl_buffer, or the pending
wl_buffer has been destroyed, the following wl_surface.commit will
remove the surface content.
If wl_surface.attach is sent with a NULL wl_buffer, the
following wl_surface.commit will remove the surface content.
If a pending wl_buffer has been destroyed, the result is not specified.
Many compositors are known to remove the surface content on the following
wl_surface.commit, but this behaviour is not universal. Clients seeking to
maximise compatibility should not destroy pending buffers and should
ensure that they explicitly remove content from surfaces, even after
destroying buffers.
</description>
<arg name="buffer" type="object" interface="wl_buffer" allow-null="true"
summary="buffer of surface contents"/>
@ -1658,21 +1667,48 @@
etc.) is double-buffered. Protocol requests modify the pending state,
as opposed to the active state in use by the compositor.
A commit request atomically creates a content update from the pending
state, even if the pending state has not been touched. The content
update is placed in a queue until it becomes active. After commit, the
new pending state is as documented for each related request.
When the content update is applied, the wl_buffer is applied before all
other state. This means that all coordinates in double-buffered state
are relative to the newly attached wl_buffers, except for
wl_surface.attach itself. If there is no newly attached wl_buffer, the
coordinates are relative to the previous content update.
All requests that need a commit to become effective are documented
to affect double-buffered state.
Other interfaces may add further double-buffered surface state.
A commit request atomically creates a Content Update (CU) from the
pending state, even if the pending state has not been touched. The
content update is placed at the end of a per-surface queue until it
becomes active. After commit, the new pending state is as documented for
each related request.
A CU is either a Desync Content Update (DCU) or a Sync Content Update
(SCU). If the surface is effectively synchronized at the commit request,
it is a SCU, otherwise a DCU.
When a surface transitions from effectively synchronized to effectively
desynchronized, all SCUs in its queue which are not reachable by any
DCU become DCUs and dependency edges from outside the queue to these CUs
are removed.
See wl_subsurface for the definition of 'effectively synchronized' and
'effectively desynchronized'.
When a CU is placed in the queue, the CU has a dependency on the CU in
front of it and to the SCU at end of the queue of every direct child
surface if that SCU exists and does not have another dependent. This can
form a directed acyclic graph of CUs with dependencies as edges.
In addition to surface state, the CU can have constraints that must be
satisfied before it can be applied. Other interfaces may add CU
constraints.
All DCUs which do not have a SCU in front of themselves in their queue,
are candidates. If the graph that's reachable by a candidate does not
have any unsatisfied constraints, the entire graph must be applied
atomically.
When a CU is applied, the wl_buffer is applied before all other state.
This means that all coordinates in double-buffered state are relative to
the newly attached wl_buffers, except for wl_surface.attach itself. If
there is no newly attached wl_buffer, the coordinates are relative to
the previous content update.
</description>
</request>
@ -1826,6 +1862,9 @@
x and y, combined with the new surface size define in which
directions the surface's size changes.
The exact semantics of wl_surface.offset are role-specific. Refer to
the documentation of specific roles for more information.
Surface location offset is double-buffered state, see
wl_surface.commit.
@ -1874,7 +1913,7 @@
</event>
</interface>
<interface name="wl_seat" version="9">
<interface name="wl_seat" version="10">
<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
@ -1902,9 +1941,10 @@
<event name="capabilities">
<description summary="seat capabilities changed">
This is emitted whenever a seat gains or loses the pointer,
keyboard or touch capabilities. The argument is a capability
enum containing the complete set of capabilities this seat has.
This is sent on binding to the seat global or whenever a seat gains
or loses the pointer, keyboard or touch capabilities.
The argument is a capability enum containing the complete set of
capabilities this seat has.
When the pointer capability is added, a client may create a
wl_pointer object using the wl_seat.get_pointer request. This object
@ -1986,9 +2026,9 @@
The same seat names are used for all clients. Thus, the name can be
shared across processes to refer to a specific wl_seat global.
The name event is sent after binding to the seat global. This event is
only sent once per seat object, and the name does not change over the
lifetime of the 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,
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
destroyed and re-created later.
@ -2007,7 +2047,7 @@
</interface>
<interface name="wl_pointer" version="9">
<interface name="wl_pointer" version="10">
<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
@ -2420,7 +2460,7 @@
</event>
</interface>
<interface name="wl_keyboard" version="9">
<interface name="wl_keyboard" version="10">
<description summary="keyboard input device">
The wl_keyboard interface represents one or more keyboards
associated with a seat.
@ -2473,6 +2513,9 @@
the surface argument and the keys currently logically down to the keys
in the keys argument. The compositor must not send this event if the
wl_keyboard already had an active surface immediately before this event.
Clients should not use the list of pressed keys to emulate key-press
events. The order of keys in the list is unspecified.
</description>
<arg name="serial" type="uint" summary="serial number of the enter event"/>
<arg name="surface" type="object" interface="wl_surface" summary="surface gaining keyboard focus"/>
@ -2499,9 +2542,18 @@
<enum name="key_state">
<description summary="physical key state">
Describes the physical state of a key that produced the key event.
Since version 10, the key can be in a "repeated" pseudo-state which
means the same as "pressed", but is used to signal repetition in the
key event.
The key may only enter the repeated state after entering the pressed
state and before entering the released state. This event may be
generated multiple times while the key is down.
</description>
<entry name="released" value="0" summary="key is not pressed"/>
<entry name="pressed" value="1" summary="key is pressed"/>
<entry name="repeated" value="2" summary="key was repeated" since="10"/>
</enum>
<event name="key">
@ -2524,6 +2576,11 @@
compositor must not send this event if state is pressed (resp. released)
and the key was already logically down (resp. was not logically down)
immediately before this event.
Since version 10, compositors may send key events with the "repeated"
key state when a wl_keyboard.repeat_info event with a rate argument of
0 has been received. This allows the compositor to take over the
responsibility of key repetition.
</description>
<arg name="serial" type="uint" summary="serial number of the key event"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
@ -2584,7 +2641,7 @@
</event>
</interface>
<interface name="wl_touch" version="9">
<interface name="wl_touch" version="10">
<description summary="touchscreen input device">
The wl_touch interface represents a touchscreen
associated with a seat.
@ -3090,23 +3147,9 @@
hidden, or if a NULL wl_buffer is applied. These rules apply
recursively through the tree of surfaces.
The behaviour of a wl_surface.commit request on a sub-surface
depends on the sub-surface's mode. The possible modes are
synchronized and desynchronized, see methods
wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized
mode caches the wl_surface state to be applied when the parent's
state gets applied, and desynchronized mode applies the pending
wl_surface state directly. A sub-surface is initially in the
synchronized mode.
Sub-surfaces also have another kind of state, which is managed by
wl_subsurface requests, as opposed to wl_surface requests. This
state includes the sub-surface position relative to the parent
surface (wl_subsurface.set_position), and the stacking order of
the parent and its sub-surfaces (wl_subsurface.place_above and
.place_below). This state is applied when the parent surface's
wl_surface state is applied, regardless of the sub-surface's mode.
As the exception, set_sync and set_desync are effective immediately.
A sub-surface can be in one of two modes. The possible modes are
synchronized and desynchronized, see methods wl_subsurface.set_sync and
wl_subsurface.set_desync.
The main surface can be thought to be always in desynchronized mode,
since it does not have a parent in the sub-surfaces sense.
@ -3118,6 +3161,22 @@
synchronized mode, and then assume that all its child and grand-child
sub-surfaces are synchronized, too, without explicitly setting them.
If a surface behaves as in synchronized mode, it is effectively
synchronized, otherwise it is effectively desynchronized.
A sub-surface is initially in the synchronized mode.
Sub-surfaces also have another kind of state, which is managed by
wl_subsurface requests, as opposed to wl_surface requests. This
state includes the sub-surface position relative to the parent
surface (wl_subsurface.set_position), and the stacking order of
the parent and its sub-surfaces (wl_subsurface.place_above and
.place_below).
This state is double-buffered on the parent's surface regardless of the
sub-surface's mode. As the exception, set_sync and set_desync are
effective immediately.
Destroying a sub-surface takes effect immediately. If you need to
synchronize the removal of a sub-surface to the parent surface update,
unmap the sub-surface first by attaching a NULL wl_buffer, update parent,
@ -3199,44 +3258,47 @@
<request name="set_sync">
<description summary="set sub-surface to synchronized mode">
Change the commit behaviour of the sub-surface to synchronized
mode, also described as the parent dependent mode.
mode.
In synchronized mode, wl_surface.commit on a sub-surface will
accumulate the committed state in a cache, but the state will
not be applied and hence will not change the compositor output.
The cached state is applied to the sub-surface immediately after
the parent surface's state is applied. This ensures atomic
updates of the parent and all its synchronized sub-surfaces.
Applying the cached state will invalidate the cache, so further
parent surface commits do not (re-)apply old state.
See wl_subsurface for the recursive effect of this mode.
See wl_subsurface and wl_surface.commit for more information.
</description>
</request>
<request name="set_desync">
<description summary="set sub-surface to desynchronized mode">
Change the commit behaviour of the sub-surface to desynchronized
mode, also described as independent or freely running mode.
mode.
In desynchronized mode, wl_surface.commit on a sub-surface will
apply the pending state directly, without caching, as happens
normally with a wl_surface. Calling wl_surface.commit on the
parent surface has no effect on the sub-surface's wl_surface
state. This mode allows a sub-surface to be updated on its own.
If cached state exists when wl_surface.commit is called in
desynchronized mode, the pending state is added to the cached
state, and applied as a whole. This invalidates the cache.
Note: even if a sub-surface is set to desynchronized, a parent
sub-surface may override it to behave as synchronized. For details,
see wl_subsurface.
If a surface's parent surface behaves as desynchronized, then
the cached state is applied on set_desync.
See wl_subsurface and wl_surface.commit for more information.
</description>
</request>
</interface>
<interface name="wl_fixes" version="1">
<description summary="wayland protocol fixes">
This global fixes problems with other core-protocol interfaces that
cannot be fixed in these interfaces themselves.
</description>
<request name="destroy" type="destructor">
<description summary="destroys this object"/>
</request>
<request name="destroy_registry">
<description summary="destroy a wl_registry">
This request destroys a wl_registry object.
The client should no longer use the wl_registry after making this
request.
The compositor will emit a wl_display.delete_id event with the object ID
of the registry and will no longer emit any events on the registry. The
client should re-use the object ID once it receives the
wl_display.delete_id event.
</description>
<arg name="registry" type="object" interface="wl_registry"
summary="the registry to destroy"/>
</request>
</interface>
</protocol>

View file

@ -26,7 +26,8 @@
#define _GNU_SOURCE
#include <assert.h>
#include "../config.h"
#include <math.h>
#include <stdlib.h>
#include <stdint.h>
@ -75,7 +76,8 @@ struct wl_connection {
static inline size_t
size_pot(uint32_t size_bits)
{
assert(size_bits < 8 * sizeof(size_t));
if (!(size_bits < 8 * sizeof(size_t)))
wl_abort("Too many bits for size_t\n");
return ((size_t)1) << size_bits;
}
@ -260,7 +262,7 @@ ring_buffer_ensure_space(struct wl_ring_buffer *b, size_t count)
* allowed).
*/
if (net_size > size_pot(size_bits)) {
wl_log("Data too big for buffer (%d + %zd > %zd).\n",
wl_log("Data too big for buffer (%zu + %zu > %zu).\n",
ring_buffer_size(b), count, size_pot(size_bits));
errno = E2BIG;
return -1;
@ -928,7 +930,7 @@ wl_connection_demarshal(struct wl_connection *connection,
for (i = 0; i < count; i++) {
signature = get_next_argument(signature, &arg);
if (arg.type != WL_ARG_FD && p + 1 > end) {
if (arg.type != WL_ARG_FD && p >= end) {
wl_log("message too short, "
"object (%d), message %s(%s)\n",
closure->sender_id, message->name,
@ -975,7 +977,7 @@ wl_connection_demarshal(struct wl_connection *connection,
s = (char *) p;
if (length > 0 && s[length - 1] != '\0') {
if (s[length - 1] != '\0') {
wl_log("string not nul-terminated, "
"message %s(%s)\n",
message->name, message->signature);
@ -983,6 +985,14 @@ wl_connection_demarshal(struct wl_connection *connection,
goto err;
}
if (strlen(s) != length - 1) {
wl_log("string has embedded nul at offset %zu, "
"message %s(%s)\n", strlen(s),
message->name, message->signature);
errno = EINVAL;
goto err;
}
closure->args[i].s = s;
p = next;
break;
@ -1221,6 +1231,11 @@ wl_closure_invoke(struct wl_closure *closure, uint32_t flags,
count + 2, &ffi_type_void, ffi_types);
implementation = target->implementation;
if (!implementation) {
wl_abort("Implementation of resource %d of %s is NULL\n",
target->id, target->interface->name);
}
if (!implementation[opcode]) {
wl_abort("listener function for opcode %u of %s is NULL\n",
opcode, target->interface->name);
@ -1343,7 +1358,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer,
if (arg.type == WL_ARG_FD)
continue;
if (p + 1 > end)
if (p >= end)
goto overflow;
switch (arg.type) {
@ -1371,7 +1386,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer,
size = strlen(closure->args[i].s) + 1;
*p++ = size;
if (p + div_roundup(size, sizeof *p) > end)
if (div_roundup(size, sizeof *p) > (uint32_t)(end - p))
goto overflow;
memcpy(p, closure->args[i].s, size);
@ -1386,7 +1401,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer,
size = closure->args[i].a->size;
*p++ = size;
if (p + div_roundup(size, sizeof *p) > end)
if (div_roundup(size, sizeof *p) > (uint32_t)(end - p))
goto overflow;
if (size != 0)
@ -1478,11 +1493,56 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection)
return result;
}
bool
wl_check_env_token(const char *env, const char *token)
{
const char *ptr = env;
size_t token_len;
if (env == NULL)
return false;
token_len = strlen(token);
// Scan the string for comma-separated tokens and look for a match.
while (true) {
const char *end;
size_t len;
// Skip over any leading separators.
while (*ptr == ',')
ptr++;
if (*ptr == '\x00')
return false;
end = strchr(ptr + 1, ',');
// If there isn't another separarator, then the rest of the string
// is one token.
if (end == NULL)
return (strcmp(ptr, token) == 0);
len = end - ptr;
if (len == token_len && memcmp(ptr, token, len) == 0) {
return true;
}
// Skip to the next token.
ptr += len;
}
return false;
}
void
wl_closure_print(struct wl_closure *closure, struct wl_object *target,
int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg),
const char *queue_name)
const char *queue_name, int color)
{
#if defined(HAVE_GETTID)
static int include_tid = -1;
#endif // defined(HAVE_GETTID)
int i;
struct argument_details arg;
const char *signature = closure->message->signature;
@ -1499,17 +1559,40 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target,
clock_gettime(CLOCK_REALTIME, &tp);
time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
fprintf(f, "%s[%7u.%03u] ",
color ? WL_DEBUG_COLOR_GREEN : "",
time / 1000, time % 1000);
fprintf(f, "[%7u.%03u] ", time / 1000, time % 1000);
#if defined(HAVE_GETTID)
if (include_tid < 0) {
include_tid = wl_check_env_token(getenv("WAYLAND_DEBUG"), "thread_id");
}
if (queue_name)
fprintf(f, "{%s} ", queue_name);
if (include_tid) {
fprintf(f, "%sTID#%d ",
color ? WL_DEBUG_COLOR_CYAN : "",
(int) gettid());
}
#endif
fprintf(f, "%s%s%s#%u.%s(",
if (queue_name) {
fprintf(f, "%s{%s} ",
color ? WL_DEBUG_COLOR_YELLOW : "",
queue_name);
}
fprintf(f, "%s%s%s%s%s%s%s#%u%s.%s%s(",
color ? WL_DEBUG_COLOR_RED : "",
discarded ? "discarded " : "",
color ? WL_DEBUG_COLOR_RESET : "",
send ? " -> " : "",
target->interface->name, target->id,
closure->message->name);
color ? WL_DEBUG_COLOR_BLUE : "",
target->interface->name,
color ? WL_DEBUG_COLOR_MAGENTA : "",
target->id,
color ? WL_DEBUG_COLOR_CYAN : "",
closure->message->name,
color ? WL_DEBUG_COLOR_RESET : "");
for (i = 0; i < closure->count; i++) {
signature = get_next_argument(signature, &arg);
@ -1574,7 +1657,7 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target,
}
}
fprintf(f, ")\n");
fprintf(f, ")%s\n", color ? WL_DEBUG_COLOR_RESET : "");
if (fclose(f) == 0) {
fprintf(stderr, "%s", buffer);

View file

@ -23,7 +23,6 @@
* SOFTWARE.
*/
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <errno.h>
@ -39,6 +38,7 @@
#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include "timespec-util.h"
#include "wayland-util.h"
#include "wayland-private.h"
#include "wayland-server-core.h"
@ -447,7 +447,8 @@ wl_timer_heap_disarm(struct wl_timer_heap *timers,
struct wl_event_source_timer *last_end_evt;
int old_source_idx;
assert(source->heap_idx >= 0);
if (!(source->heap_idx >= 0))
wl_abort("Timer has already been disarmed\n");
old_source_idx = source->heap_idx;
source->heap_idx = -1;
@ -476,7 +477,8 @@ wl_timer_heap_arm(struct wl_timer_heap *timers,
struct wl_event_source_timer *source,
struct timespec deadline)
{
assert(source->heap_idx == -1);
if (!(source->heap_idx == -1))
wl_abort("Timer is already armed\n");
source->deadline = deadline;
timers->data[timers->active] = source;
@ -972,57 +974,6 @@ wl_event_loop_dispatch_idle(struct wl_event_loop *loop)
}
}
static int
timespec_to_ms(struct timespec value)
{
return (value.tv_sec * 1000) + (value.tv_nsec / 1000000);
}
static struct timespec
ms_to_timespec(int ms)
{
struct timespec val;
val.tv_sec = ms / 1000;
val.tv_nsec = (ms % 1000) * 1000000;
return val;
}
static struct timespec
timespec_normalize(struct timespec value)
{
struct timespec result = value;
while (result.tv_nsec >= 1000000000) {
result.tv_nsec -= 1000000000;
result.tv_sec++;
}
while (result.tv_nsec < 0) {
result.tv_nsec += 1000000000;
result.tv_sec--;
}
return result;
}
static struct timespec
timespec_add(struct timespec a, struct timespec b)
{
struct timespec result;
result.tv_sec = a.tv_sec + b.tv_sec;
result.tv_nsec = a.tv_nsec + b.tv_nsec;
return timespec_normalize(result);
}
static struct timespec
timespec_sub(struct timespec a, struct timespec b)
{
struct timespec result;
result.tv_sec = a.tv_sec - b.tv_sec;
result.tv_nsec = a.tv_nsec - b.tv_nsec;
return timespec_normalize(result);
}
/** Wait for events and dispatch them
*
* \param loop The event loop whose sources to wait for.
@ -1051,13 +1002,15 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout)
int i, count;
bool has_timers = false;
bool use_timeout = timeout > 0;
struct timespec now, end;
struct timespec now;
struct timespec deadline = {0};
struct timespec result;
wl_event_loop_dispatch_idle(loop);
if (use_timeout) {
clock_gettime(CLOCK_MONOTONIC, &now);
end = timespec_add(now, ms_to_timespec(timeout));
timespec_add_msec(&deadline, &now, timeout);
}
while (true) {
@ -1069,7 +1022,8 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout)
if (use_timeout) {
clock_gettime(CLOCK_MONOTONIC, &now);
timeout = timespec_to_ms(timespec_sub(end, now));
timespec_sub(&result, &deadline, &now);
timeout = timespec_to_msec(&result);
if (timeout <= 0) {
/* too late */
count = 0;

View file

@ -212,6 +212,7 @@ if get_option('libraries')
description: 'Server side implementation of the Wayland protocol',
version: meson.project_version(),
filebase: 'wayland-server',
libraries: mathlib_dep,
variables: [
'datarootdir=' + join_paths('${prefix}', get_option('datadir')),
'pkgdatadir=' + join_paths('${pc_sysrootdir}${datarootdir}', meson.project_name())
@ -251,6 +252,7 @@ if get_option('libraries')
description: 'Wayland client side library',
version: meson.project_version(),
filebase: 'wayland-client',
libraries: mathlib_dep,
variables: [
'datarootdir=' + join_paths('${prefix}', get_option('datadir')),
'pkgdatadir=' + join_paths('${pc_sysrootdir}${datarootdir}', meson.project_name())

View file

@ -1378,6 +1378,58 @@ emit_event_wrappers(struct wl_list *message_list, struct interface *interface)
}
}
static void
emit_validator(struct interface *interface, struct enumeration *e)
{
struct entry *entry;
printf("#ifndef %s_%s_ENUM_IS_VALID\n",
interface->uppercase_name, e->uppercase_name);
printf("#define %s_%s_ENUM_IS_VALID\n",
interface->uppercase_name, e->uppercase_name);
printf("/**\n"
" * @ingroup iface_%s\n"
" * Validate a %s %s value.\n"
" *\n"
" * @return true on success, false on error.\n"
" * @ref %s_%s\n"
" */\n"
"static inline bool\n"
"%s_%s_is_valid(uint32_t value, uint32_t version) {\n",
interface->name, interface->name, e->name,
interface->name, e->name,
interface->name, e->name);
if (e->bitfield) {
printf(" uint32_t valid = 0;\n");
wl_list_for_each(entry, &e->entry_list, link) {
printf(" if (version >= %d)\n"
" valid |= %s_%s_%s;\n",
entry->since,
interface->uppercase_name, e->uppercase_name,
entry->uppercase_name);
}
printf(" return (value & ~valid) == 0;\n");
} else {
printf(" switch (value) {\n");
wl_list_for_each(entry, &e->entry_list, link) {
printf(" case %s%s_%s_%s:\n"
" return version >= %d;\n",
entry->value[0] == '-' ? "(uint32_t)" : "",
interface->uppercase_name, e->uppercase_name,
entry->uppercase_name, entry->since);
}
printf(" default:\n"
" return false;\n"
" }\n");
}
printf("}\n");
printf("#endif /* %s_%s_ENUM_IS_VALID */\n\n",
interface->uppercase_name, e->uppercase_name);
}
static void
emit_enumerations(struct interface *interface, bool with_validators)
{
@ -1439,35 +1491,11 @@ emit_enumerations(struct interface *interface, bool with_validators)
}
if (with_validators) {
printf("/**\n"
" * @ingroup iface_%s\n"
" * Validate a %s %s value.\n"
" *\n"
" * @return true on success, false on error.\n"
" * @ref %s_%s\n"
" */\n"
"static inline bool\n"
"%s_%s_is_valid(uint32_t value, uint32_t version) {\n"
" switch (value) {\n",
interface->name, interface->name, e->name,
interface->name, e->name,
interface->name, e->name);
wl_list_for_each(entry, &e->entry_list, link) {
printf(" case %s%s_%s_%s:\n"
" return version >= %d;\n",
entry->value[0] == '-' ? "(uint32_t)" : "",
interface->uppercase_name, e->uppercase_name,
entry->uppercase_name, entry->since);
}
printf(" default:\n"
" return false;\n"
" }\n"
"}\n");
}
printf("#endif /* %s_%s_ENUM */\n\n",
interface->uppercase_name, e->uppercase_name);
if (with_validators)
emit_validator(interface, e);
}
}

311
src/timespec-util.h Normal file
View file

@ -0,0 +1,311 @@
/*
* Copyright © 2014 - 2015 Collabora, Ltd.
*
* 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.
*/
#ifndef TIMESPEC_UTIL_H
#define TIMESPEC_UTIL_H
#include <stdint.h>
#include <assert.h>
#include <time.h>
#include <stdbool.h>
#define NSEC_PER_SEC 1000000000
/* Subtract timespecs
*
* \param r[out] result: a - b
* \param a[in] operand
* \param b[in] operand
*/
static inline void
timespec_sub(struct timespec *r,
const struct timespec *a, const struct timespec *b)
{
r->tv_sec = a->tv_sec - b->tv_sec;
r->tv_nsec = a->tv_nsec - b->tv_nsec;
if (r->tv_nsec < 0) {
r->tv_sec--;
r->tv_nsec += NSEC_PER_SEC;
}
}
/* Add a nanosecond value to a timespec
*
* \param r[out] result: a + b
* \param a[in] base operand as timespec
* \param b[in] operand in nanoseconds
*/
static inline void
timespec_add_nsec(struct timespec *r, const struct timespec *a, int64_t b)
{
r->tv_sec = a->tv_sec + (b / NSEC_PER_SEC);
r->tv_nsec = a->tv_nsec + (b % NSEC_PER_SEC);
if (r->tv_nsec >= NSEC_PER_SEC) {
r->tv_sec++;
r->tv_nsec -= NSEC_PER_SEC;
} else if (r->tv_nsec < 0) {
r->tv_sec--;
r->tv_nsec += NSEC_PER_SEC;
}
}
/* Add a millisecond value to a timespec
*
* \param r[out] result: a + b
* \param a[in] base operand as timespec
* \param b[in] operand in milliseconds
*/
static inline void
timespec_add_msec(struct timespec *r, const struct timespec *a, int64_t b)
{
timespec_add_nsec(r, a, b * 1000000);
}
/* Convert timespec to nanoseconds
*
* \param a timespec
* \return nanoseconds
*/
static inline int64_t
timespec_to_nsec(const struct timespec *a)
{
return (int64_t)a->tv_sec * NSEC_PER_SEC + a->tv_nsec;
}
/* Subtract timespecs and return result in nanoseconds
*
* \param a[in] operand
* \param b[in] operand
* \return to_nanoseconds(a - b)
*/
static inline int64_t
timespec_sub_to_nsec(const struct timespec *a, const struct timespec *b)
{
struct timespec r;
timespec_sub(&r, a, b);
return timespec_to_nsec(&r);
}
/* Convert timespec to milliseconds
*
* \param a timespec
* \return milliseconds
*
* Rounding to integer milliseconds happens always down (floor()).
*/
static inline int64_t
timespec_to_msec(const struct timespec *a)
{
return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
}
/* Subtract timespecs and return result in milliseconds
*
* \param a[in] operand
* \param b[in] operand
* \return to_milliseconds(a - b)
*/
static inline int64_t
timespec_sub_to_msec(const struct timespec *a, const struct timespec *b)
{
return timespec_sub_to_nsec(a, b) / 1000000;
}
/* Convert timespec to microseconds
*
* \param a timespec
* \return microseconds
*
* Rounding to integer microseconds happens always down (floor()).
*/
static inline int64_t
timespec_to_usec(const struct timespec *a)
{
return (int64_t)a->tv_sec * 1000000 + a->tv_nsec / 1000;
}
/* Convert timespec to protocol data
*
* \param a timespec
* \param tv_sec_hi[out] the high bytes of the seconds part
* \param tv_sec_lo[out] the low bytes of the seconds part
* \param tv_nsec[out] the nanoseconds part
*
* The input timespec must be normalized (the nanoseconds part should
* be less than 1 second) and non-negative.
*/
static inline void
timespec_to_proto(const struct timespec *a, uint32_t *tv_sec_hi,
uint32_t *tv_sec_lo, uint32_t *tv_nsec)
{
assert(a->tv_sec >= 0);
assert(a->tv_nsec >= 0 && a->tv_nsec < NSEC_PER_SEC);
uint64_t sec64 = a->tv_sec;
*tv_sec_hi = sec64 >> 32;
*tv_sec_lo = sec64 & 0xffffffff;
*tv_nsec = a->tv_nsec;
}
/* Convert nanoseconds to timespec
*
* \param a timespec
* \param b nanoseconds
*/
static inline void
timespec_from_nsec(struct timespec *a, int64_t b)
{
a->tv_sec = b / NSEC_PER_SEC;
a->tv_nsec = b % NSEC_PER_SEC;
}
/* Convert microseconds to timespec
*
* \param a timespec
* \param b microseconds
*/
static inline void
timespec_from_usec(struct timespec *a, int64_t b)
{
timespec_from_nsec(a, b * 1000);
}
/* Convert milliseconds to timespec
*
* \param a timespec
* \param b milliseconds
*/
static inline void
timespec_from_msec(struct timespec *a, int64_t b)
{
timespec_from_nsec(a, b * 1000000);
}
/* Convert protocol data to timespec
*
* \param a[out] timespec
* \param tv_sec_hi the high bytes of seconds part
* \param tv_sec_lo the low bytes of seconds part
* \param tv_nsec the nanoseconds part
*/
static inline void
timespec_from_proto(struct timespec *a, uint32_t tv_sec_hi,
uint32_t tv_sec_lo, uint32_t tv_nsec)
{
a->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
a->tv_nsec = tv_nsec;
}
/* Check if a timespec is zero
*
* \param a timespec
* \return whether the timespec is zero
*/
static inline bool
timespec_is_zero(const struct timespec *a)
{
return a->tv_sec == 0 && a->tv_nsec == 0;
}
/* Check if two timespecs are equal
*
* \param a[in] timespec to check
* \param b[in] timespec to check
* \return whether timespecs a and b are equal
*/
static inline bool
timespec_eq(const struct timespec *a, const struct timespec *b)
{
return a->tv_sec == b->tv_sec &&
a->tv_nsec == b->tv_nsec;
}
/* Convert milli-Hertz to nanoseconds
*
* \param mhz frequency in mHz, not zero
* \return period in nanoseconds
*/
static inline int64_t
millihz_to_nsec(uint32_t mhz)
{
assert(mhz > 0);
return 1000000000000LL / mhz;
}
/**
* Checks whether a timespec value is after another
*
* \param a[in] timespec to compare
* \param b[in] timespec to compare
* \return whether a is after b
*/
static inline bool
timespec_after(const struct timespec *a, const struct timespec *b)
{
return (a->tv_sec == b->tv_sec) ?
(a->tv_nsec > b->tv_nsec) :
(a->tv_sec > b->tv_sec);
}
/**
* Add timespecs
*
* \param r[out] result: a + b
* \param a[in] operand
* \param b[in] operand
*/
static inline void
timespec_add(struct timespec *r,
const struct timespec *a, const struct timespec *b)
{
r->tv_sec = a->tv_sec + b->tv_sec;
r->tv_nsec = a->tv_nsec + b->tv_nsec;
if (r->tv_nsec > NSEC_PER_SEC) {
r->tv_sec++;
r->tv_nsec -= NSEC_PER_SEC;
}
}
/**
* Saturating timespec subtraction
*
* \param r[out] result: max(a - b, 0)
* \param a[in] operand
* \param b[in] operand
*/
static inline void
timespec_sub_saturate(struct timespec *r,
const struct timespec *a, const struct timespec *b)
{
timespec_sub(r, a, b);
if (r->tv_sec < 0) {
r->tv_sec = 0;
r->tv_nsec = 0;
}
}
#endif /* TIMESPEC_UTIL_H */

View file

@ -34,6 +34,8 @@
extern "C" {
#endif
struct timespec;
/** \class wl_proxy
*
* \brief Represents a protocol object on the client side.
@ -219,6 +221,9 @@ wl_proxy_get_tag(struct wl_proxy *proxy);
const char *
wl_proxy_get_class(struct wl_proxy *proxy);
const struct wl_interface *
wl_proxy_get_interface(struct wl_proxy *proxy);
struct wl_display *
wl_proxy_get_display(struct wl_proxy *proxy);
@ -250,13 +255,29 @@ int
wl_display_dispatch_queue(struct wl_display *display,
struct wl_event_queue *queue);
int
wl_display_dispatch_timeout(struct wl_display *display,
const struct timespec *timeout);
int
wl_display_dispatch_queue_timeout(struct wl_display *display,
struct wl_event_queue *queue,
const struct timespec *timeout);
int
wl_display_dispatch_queue_pending(struct wl_display *display,
struct wl_event_queue *queue);
int
wl_display_dispatch_queue_pending_single(struct wl_display *display,
struct wl_event_queue *queue);
int
wl_display_dispatch_pending(struct wl_display *display);
int
wl_display_dispatch_pending_single(struct wl_display *display);
int
wl_display_get_error(struct wl_display *display);

View file

@ -37,7 +37,6 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <ctype.h>
#include <assert.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
@ -46,6 +45,7 @@
#include "wayland-os.h"
#include "wayland-client.h"
#include "wayland-private.h"
#include "timespec-util.h"
/** \cond */
@ -115,6 +115,7 @@ struct wl_display {
/** \endcond */
static int debug_client = 0;
static int debug_color = 0;
/**
* This helper function wakes up all threads that are
@ -235,13 +236,16 @@ wl_event_queue_init(struct wl_event_queue *queue,
static void
wl_proxy_unref(struct wl_proxy *proxy)
{
assert(proxy->refcount > 0);
if (!(proxy->refcount > 0))
wl_abort("Proxy requested for unref has no references\n");
if (--proxy->refcount > 0)
return;
/* If we get here, the client must have explicitly requested
* deletion. */
assert(proxy->flags & WL_PROXY_FLAG_DESTROYED);
if (!(proxy->flags & WL_PROXY_FLAG_DESTROYED))
wl_abort("Proxy with no references not yet explicitly"
"destroyed\n");
free(proxy);
}
@ -671,6 +675,9 @@ wl_proxy_add_listener(struct wl_proxy *proxy,
* This function is useful in clients with multiple listeners on the same
* interface to allow the identification of which code to execute.
*
* If \ref wl_proxy_add_dispatcher was used, this function returns the
* dispatcher_data pointer instead.
*
* \memberof wl_proxy
*/
WL_EXPORT const void *
@ -914,21 +921,29 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode,
closure = wl_closure_marshal(&proxy->object, opcode, args, message);
if (closure == NULL) {
wl_log("Error marshalling request: %s\n", strerror(errno));
wl_log("Error marshalling request for %s.%s: %s\n",
proxy->object.interface->name, message->name,
strerror(errno));
display_fatal_error(proxy->display, errno);
goto err_unlock;
}
if (debug_client) {
struct wl_event_queue *queue;
const char *queue_name = NULL;
queue = wl_proxy_get_queue(proxy);
if (queue)
queue_name = wl_event_queue_get_name(queue);
wl_closure_print(closure, &proxy->object, true, false, NULL,
wl_event_queue_get_name(queue));
queue_name, debug_color);
}
if (wl_closure_send(closure, proxy->display->connection)) {
wl_log("Error sending request: %s\n", strerror(errno));
wl_log("Error sending request for %s.%s: %s\n",
proxy->object.interface->name, message->name,
strerror(errno));
display_fatal_error(proxy->display, errno);
}
@ -1172,7 +1187,8 @@ connect_to_socket(const char *name)
"%s", name) + 1;
}
assert(name_size > 0);
if (!(name_size > 0))
wl_abort("Error assigning path name for socket connection\n");
if (name_size > (int)sizeof addr.sun_path) {
if (!path_is_absolute) {
wl_log("error: socket path \"%s/%s\" plus null terminator"
@ -1214,10 +1230,23 @@ wl_display_connect_to_fd(int fd)
{
struct wl_display *display;
const char *debug;
const char *no_color;
const char *force_color;
no_color = getenv("NO_COLOR");
force_color = getenv("FORCE_COLOR");
debug = getenv("WAYLAND_DEBUG");
if (debug && (strstr(debug, "client") || strstr(debug, "1")))
if (debug && (wl_check_env_token(debug, "client") || wl_check_env_token(debug, "1"))) {
debug_client = 1;
if (isatty(fileno(stderr)))
debug_color = 1;
}
if (force_color && force_color[0] != '\0')
debug_color = 1;
if (no_color && no_color[0] != '\0')
debug_color = 0;
display = zalloc(sizeof *display);
if (display == NULL) {
@ -1549,6 +1578,28 @@ queue_event(struct wl_display *display, int len)
id = p[0];
opcode = p[1] & 0xffff;
size = p[1] >> 16;
/*
* If the message is larger than the maximum size of the
* connection buffer, the connection buffer will fill to
* its max size and stay there, with no message ever
* successfully being processed. If the user of
* libwayland-client uses a level-triggered event loop,
* this will cause the client to enter a loop that
* consumes CPU. To avoid this, immediately drop the
* connection. Since the maximum size of a message should
* not depend on the max buffer size chosen by the client,
* always compare the message size against the
* limit enforced by libwayland 1.22 and below (4096),
* rather than the actual value the client chose.
*/
if (size > WL_MAX_MESSAGE_SIZE) {
wl_log("Message length %u exceeds limit %d\n",
size, WL_MAX_MESSAGE_SIZE);
errno = E2BIG;
return -1;
}
if (len < size)
return 0;
@ -1563,12 +1614,16 @@ queue_event(struct wl_display *display, int len)
if (debug_client) {
clock_gettime(CLOCK_REALTIME, &tp);
time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
fprintf(stderr, "[%7u.%03u] discarded [%s]#%d.[event %d]"
fprintf(stderr, "%s[%7u.%03u] %sdiscarded %s[%s]%s#%u%s.[event %d]%s"
"(%d fd, %d byte)\n",
debug_color ? WL_DEBUG_COLOR_GREEN : "",
time / 1000, time % 1000,
debug_color ? WL_DEBUG_COLOR_RED : "",
debug_color ? WL_DEBUG_COLOR_BLUE : "",
zombie ? "zombie" : "unknown",
id, opcode,
debug_color ? WL_DEBUG_COLOR_MAGENTA : "", id,
debug_color ? WL_DEBUG_COLOR_BLUE : "", opcode,
debug_color ? WL_DEBUG_COLOR_RESET : "",
num_zombie_fds, size);
}
if (num_zombie_fds > 0)
@ -1653,7 +1708,7 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue)
!(proxy->dispatcher || proxy->object.implementation);
wl_closure_print(closure, &proxy->object, false, discarded,
id_from_object, queue->name);
id_from_object, queue->name, debug_color);
}
if (proxy_destroyed) {
@ -1827,6 +1882,34 @@ err:
return -1;
}
static int
dispatch_queue_single(struct wl_display *display, struct wl_event_queue *queue)
{
if (display->last_error)
goto err;
while (!wl_list_empty(&display->display_queue.event_list)) {
dispatch_event(display, &display->display_queue);
if (display->last_error)
goto err;
}
if (!wl_list_empty(&queue->event_list)) {
dispatch_event(display, queue);
if (display->last_error)
goto err;
return 1;
} else {
return 0;
}
err:
errno = display->last_error;
return -1;
}
/** Prepare to read events from the display's file descriptor to a queue
*
* \param display The display context object
@ -1942,20 +2025,142 @@ wl_display_cancel_read(struct wl_display *display)
}
static int
wl_display_poll(struct wl_display *display, short int events)
wl_display_poll(struct wl_display *display,
short int events,
const struct timespec *timeout)
{
int ret;
struct pollfd pfd[1];
struct timespec now;
struct timespec deadline = {0};
struct timespec result;
struct timespec *remaining_timeout = NULL;
if (timeout) {
clock_gettime(CLOCK_MONOTONIC, &now);
timespec_add(&deadline, &now, timeout);
}
pfd[0].fd = display->fd;
pfd[0].events = events;
do {
ret = poll(pfd, 1, -1);
if (timeout) {
clock_gettime(CLOCK_MONOTONIC, &now);
timespec_sub_saturate(&result, &deadline, &now);
remaining_timeout = &result;
}
ret = ppoll(pfd, 1, remaining_timeout, NULL);
} while (ret == -1 && errno == EINTR);
return ret;
}
/** Dispatch events in an event queue with a timeout
*
* \param display The display context object
* \param queue The event queue to dispatch
* \param timeout A timeout describing how long the call should block trying to
* dispatch events
* \return The number of dispatched events on success, -1 on failure
*
* This function behaves identical to wl_display_dispatch_queue() except
* that it also takes a timeout and returns 0 if the timeout elapsed.
*
* Passing NULL as a timeout means an infinite timeout. An empty timespec
* causes wl_display_dispatch_queue_timeout() to return immediately even if no
* events have been dispatched.
*
* If a timeout is passed to wl_display_dispatch_queue_timeout() it is updated
* to the remaining time.
*
* \sa wl_display_dispatch_queue()
*
* \memberof wl_display
*/
WL_EXPORT int
wl_display_dispatch_queue_timeout(struct wl_display *display,
struct wl_event_queue *queue,
const struct timespec *timeout)
{
int ret;
struct timespec now;
struct timespec deadline = {0};
struct timespec result;
struct timespec *remaining_timeout = NULL;
if (timeout) {
clock_gettime(CLOCK_MONOTONIC, &now);
timespec_add(&deadline, &now, timeout);
}
if (wl_display_prepare_read_queue(display, queue) == -1)
return wl_display_dispatch_queue_pending(display, queue);
while (true) {
ret = wl_display_flush(display);
if (ret != -1 || errno != EAGAIN)
break;
if (timeout) {
clock_gettime(CLOCK_MONOTONIC, &now);
timespec_sub_saturate(&result, &deadline, &now);
remaining_timeout = &result;
}
ret = wl_display_poll(display, POLLOUT, remaining_timeout);
if (ret <= 0) {
wl_display_cancel_read(display);
return ret;
}
}
/* Don't stop if flushing hits an EPIPE; continue so we can read any
* protocol error that may have triggered it. */
if (ret < 0 && errno != EPIPE) {
wl_display_cancel_read(display);
return -1;
}
while (true) {
if (timeout) {
clock_gettime(CLOCK_MONOTONIC, &now);
timespec_sub_saturate(&result, &deadline, &now);
remaining_timeout = &result;
}
ret = wl_display_poll(display, POLLIN, remaining_timeout);
if (ret <= 0) {
wl_display_cancel_read(display);
break;
}
ret = wl_display_read_events(display);
if (ret == -1)
break;
ret = wl_display_dispatch_queue_pending(display, queue);
if (ret != 0)
break;
/* We managed to read data from the display but there is no
* complete event to dispatch yet. Try reading again. */
if (wl_display_prepare_read_queue(display, queue) == -1)
return wl_display_dispatch_queue_pending(display, queue);
}
return ret;
}
WL_EXPORT int
wl_display_dispatch_timeout(struct wl_display *display,
const struct timespec *timeout)
{
return wl_display_dispatch_queue_timeout(display,
&display->default_queue,
timeout);
}
/** Dispatch events in an event queue
*
* \param display The display context object
@ -1987,8 +2192,8 @@ wl_display_poll(struct wl_display *display, short int events)
* \note Since Wayland 1.5 the display has an extra queue
* for its own events (i. e. delete_id). This queue is dispatched always,
* no matter what queue we passed as an argument to this function.
* That means that this function can return non-0 value even when it
* haven't dispatched any event for the given queue.
* That means that this function can return even when it has not dispatched any
* event for the given queue.
*
* \sa wl_display_dispatch(), wl_display_dispatch_pending(),
* wl_display_dispatch_queue_pending(), wl_display_prepare_read_queue()
@ -2001,37 +2206,10 @@ wl_display_dispatch_queue(struct wl_display *display,
{
int ret;
if (wl_display_prepare_read_queue(display, queue) == -1)
return wl_display_dispatch_queue_pending(display, queue);
ret = wl_display_dispatch_queue_timeout(display, queue, NULL);
assert(ret == -1 || ret > 0);
while (true) {
ret = wl_display_flush(display);
if (ret != -1 || errno != EAGAIN)
break;
if (wl_display_poll(display, POLLOUT) == -1) {
wl_display_cancel_read(display);
return -1;
}
}
/* Don't stop if flushing hits an EPIPE; continue so we can read any
* protocol error that may have triggered it. */
if (ret < 0 && errno != EPIPE) {
wl_display_cancel_read(display);
return -1;
}
if (wl_display_poll(display, POLLIN) == -1) {
wl_display_cancel_read(display);
return -1;
}
if (wl_display_read_events(display) == -1)
return -1;
return wl_display_dispatch_queue_pending(display, queue);
return ret;
}
/** Dispatch pending events in an event queue
@ -2062,6 +2240,34 @@ wl_display_dispatch_queue_pending(struct wl_display *display,
return ret;
}
/** Dispatch at most one pending event in an event queue
*
* \param display The display context object
* \param queue The event queue to dispatch
* \return The number of dispatched events (0 or 1) on success or -1 on failure
*
* Dispatch at most one pending event for objects assigned to the given
* event queue. On failure -1 is returned and errno set appropriately.
* If there are no events queued, this function returns immediately.
*
* \memberof wl_display
* \since 1.25.0
*/
WL_EXPORT int
wl_display_dispatch_queue_pending_single(struct wl_display *display,
struct wl_event_queue *queue)
{
int ret;
pthread_mutex_lock(&display->mutex);
ret = dispatch_queue_single(display, queue);
pthread_mutex_unlock(&display->mutex);
return ret;
}
/** Process incoming events
*
* \param display The display context object
@ -2122,6 +2328,25 @@ wl_display_dispatch_pending(struct wl_display *display)
&display->default_queue);
}
/** Dispatch at most one pending event in the default event queue.
*
* \param display The display context object
* \return The number of dispatched events (0 or 1) on success or -1 on failure
*
* Dispatch at most one pending event for objects assigned to the default
* event queue. On failure -1 is returned and errno set appropriately.
* If there are no events queued, this function returns immediately.
*
* \memberof wl_display
* \since 1.25.0
*/
WL_EXPORT int
wl_display_dispatch_pending_single(struct wl_display *display)
{
return wl_display_dispatch_queue_pending_single(display,
&display->default_queue);
}
/** Retrieve the last error that occurred on a display
*
* \param display The display context object
@ -2403,6 +2628,20 @@ wl_proxy_get_class(struct wl_proxy *proxy)
return proxy->object.interface->name;
}
/** Get the interface of a proxy object
*
* \param proxy The proxy object
* \return The interface of the object associated with the proxy
*
* \memberof wl_proxy
* \since 1.24
*/
WL_EXPORT const struct wl_interface *
wl_proxy_get_interface(struct wl_proxy *proxy)
{
return proxy->object.interface;
}
/** Get the display of a proxy object
*
* \param proxy The proxy object
@ -2453,7 +2692,9 @@ wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue)
wl_list_remove(&proxy->queue_link);
if (queue) {
assert(proxy->display == queue->display);
if (!(proxy->display == queue->display))
wl_abort("Proxy and queue point to different "
"wl_displays");
proxy->queue = queue;
} else {
proxy->queue = &proxy->display->default_queue;
@ -2581,7 +2822,8 @@ wl_proxy_wrapper_destroy(void *proxy_wrapper)
wl_abort("Tried to destroy non-wrapper proxy with "
"wl_proxy_wrapper_destroy\n");
assert(wrapper->refcount == 1);
if (!(wrapper->refcount == 1))
wl_abort("Expected proxy wrapper's refcount to be 1\n");
pthread_mutex_lock(&wrapper->display->mutex);

View file

@ -49,6 +49,17 @@
#define WL_CLOSURE_MAX_ARGS 20
#define WL_BUFFER_DEFAULT_SIZE_POT 12
#define WL_BUFFER_DEFAULT_MAX_SIZE (1 << WL_BUFFER_DEFAULT_SIZE_POT)
#if WL_BUFFER_DEFAULT_MAX_SIZE < WL_MAX_MESSAGE_SIZE
# error default buffer cannot hold maximum-sized message
#endif
#define WL_DEBUG_COLOR_RESET "\e[0m"
#define WL_DEBUG_COLOR_RED "\e[31m"
#define WL_DEBUG_COLOR_GREEN "\e[32m"
#define WL_DEBUG_COLOR_YELLOW "\e[33m"
#define WL_DEBUG_COLOR_BLUE "\e[34m"
#define WL_DEBUG_COLOR_MAGENTA "\e[35m"
#define WL_DEBUG_COLOR_CYAN "\e[36m"
/**
* Argument types used in signatures.
@ -226,11 +237,14 @@ wl_closure_send(struct wl_closure *closure, struct wl_connection *connection);
int
wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection);
bool
wl_check_env_token(const char *env, const char *token);
void
wl_closure_print(struct wl_closure *closure,
struct wl_object *target, int send, int discarded,
uint32_t (*n_parse)(union wl_argument *arg),
const char *queue_name);
const char *queue_name, int color);
void
wl_closure_destroy(struct wl_closure *closure);

View file

@ -227,7 +227,7 @@ typedef void (*wl_global_bind_func_t)(struct wl_client *client, void *data,
uint32_t version, uint32_t id);
uint32_t
wl_display_get_serial(struct wl_display *display);
wl_display_get_serial(const struct wl_display *display);
uint32_t
wl_display_next_serial(struct wl_display *display);
@ -324,7 +324,7 @@ void
wl_client_flush(struct wl_client *client);
void
wl_client_get_credentials(struct wl_client *client,
wl_client_get_credentials(const struct wl_client *client,
pid_t *pid, uid_t *uid, gid_t *gid);
int
@ -550,7 +550,10 @@ void
wl_resource_queue_event_array(struct wl_resource *resource,
uint32_t opcode, union wl_argument *args);
/* msg is a printf format string, variable args are its args. */
void
wl_resource_post_error_vargs(struct wl_resource *resource,
uint32_t code, const char *msg, va_list argp);
void
wl_resource_post_error(struct wl_resource *resource,
uint32_t code, const char *msg, ...) WL_PRINTF(3, 4);
@ -583,7 +586,7 @@ void
wl_resource_destroy(struct wl_resource *resource);
uint32_t
wl_resource_get_id(struct wl_resource *resource);
wl_resource_get_id(const struct wl_resource *resource);
struct wl_list *
wl_resource_get_link(struct wl_resource *resource);
@ -604,7 +607,7 @@ void *
wl_resource_get_user_data(struct wl_resource *resource);
int
wl_resource_get_version(struct wl_resource *resource);
wl_resource_get_version(const struct wl_resource *resource);
void
wl_resource_set_destructor(struct wl_resource *resource,
@ -614,8 +617,12 @@ int
wl_resource_instance_of(struct wl_resource *resource,
const struct wl_interface *interface,
const void *implementation);
const char *
wl_resource_get_class(struct wl_resource *resource);
wl_resource_get_class(const struct wl_resource *resource);
const struct wl_interface *
wl_resource_get_interface(struct wl_resource *resource);
void
wl_resource_add_destroy_listener(struct wl_resource *resource,
@ -651,16 +658,22 @@ void *
wl_shm_buffer_get_data(struct wl_shm_buffer *buffer);
int32_t
wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer);
wl_shm_buffer_get_stride(const struct wl_shm_buffer *buffer);
uint32_t
wl_shm_buffer_get_format(struct wl_shm_buffer *buffer);
wl_shm_buffer_get_format(const struct wl_shm_buffer *buffer);
int32_t
wl_shm_buffer_get_width(struct wl_shm_buffer *buffer);
wl_shm_buffer_get_width(const struct wl_shm_buffer *buffer);
int32_t
wl_shm_buffer_get_height(struct wl_shm_buffer *buffer);
wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer);
struct wl_shm_buffer *
wl_shm_buffer_ref(struct wl_shm_buffer *buffer);
void
wl_shm_buffer_unref(struct wl_shm_buffer *buffer);
struct wl_shm_pool *
wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer);
@ -674,10 +687,11 @@ wl_display_init_shm(struct wl_display *display);
uint32_t *
wl_display_add_shm_format(struct wl_display *display, uint32_t format);
WL_DEPRECATED
struct wl_shm_buffer *
wl_shm_buffer_create(struct wl_client *client,
uint32_t id, int32_t width, int32_t height,
int32_t stride, uint32_t format) WL_DEPRECATED;
int32_t stride, uint32_t format);
void
wl_log_set_handler_server(wl_log_func_t handler);

View file

@ -37,7 +37,6 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <dlfcn.h>
#include <assert.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/eventfd.h>
@ -150,6 +149,7 @@ struct wl_protocol_logger {
};
static int debug_server = 0;
static int debug_color = 0;
static void
log_closure(struct wl_resource *resource,
@ -161,7 +161,7 @@ log_closure(struct wl_resource *resource,
struct wl_protocol_logger_message message;
if (debug_server)
wl_closure_print(closure, object, send, false, NULL, NULL);
wl_closure_print(closure, object, send, false, NULL, NULL, debug_color);
if (!wl_list_empty(&display->protocol_loggers)) {
message.resource = resource;
@ -288,7 +288,16 @@ wl_resource_queue_event(struct wl_resource *resource, uint32_t opcode, ...)
wl_resource_queue_event_array(resource, opcode, args);
}
static void
/** Post a protocol error
*
* \param resource The resource object
* \param code The error code
* \param msg The error message format string
* \param argp The format string argument list
*
* \memberof wl_resource
*/
WL_EXPORT void
wl_resource_post_error_vargs(struct wl_resource *resource,
uint32_t code, const char *msg, va_list argp)
{
@ -310,9 +319,17 @@ wl_resource_post_error_vargs(struct wl_resource *resource,
wl_resource_post_event(client->display_resource,
WL_DISPLAY_ERROR, resource, code, buffer);
client->error = true;
}
/** Post a protocol error
*
* \param resource The resource object
* \param code The error code
* \param msg The error message format string
* \param ... The format string arguments
*
* \memberof wl_resource
*/
WL_EXPORT void
wl_resource_post_error(struct wl_resource *resource,
uint32_t code, const char *msg, ...)
@ -381,6 +398,29 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
wl_connection_copy(connection, p, sizeof p);
opcode = p[1] & 0xffff;
size = p[1] >> 16;
/*
* If the message is larger than the maximum size of the
* connection buffer, the connection buffer will fill to
* its max size and stay there, with no message ever
* successfully being processed. Since libwayland-server
* uses level-triggered epoll, it will cause the server to
* enter a loop that consumes CPU. To avoid this,
* immediately disconnect the client with a protocol
* error. Since the maximum size of a message should not
* depend on the buffer size chosen by the compositor,
* always compare the message size against the
* limit enforced by libwayland 1.22 and below (4096),
* rather than the actual value the compositor chose.
*/
if (size > WL_MAX_MESSAGE_SIZE) {
wl_resource_post_error(client->display_resource,
WL_DISPLAY_ERROR_INVALID_METHOD,
"message length %u exceeds %d",
size, WL_MAX_MESSAGE_SIZE);
break;
}
if (len < size)
break;
@ -603,7 +643,7 @@ err_client:
* \memberof wl_client
*/
WL_EXPORT void
wl_client_get_credentials(struct wl_client *client,
wl_client_get_credentials(const struct wl_client *client,
pid_t *pid, uid_t *uid, gid_t *gid)
{
if (pid)
@ -783,7 +823,7 @@ wl_resource_destroy(struct wl_resource *resource)
}
WL_EXPORT uint32_t
wl_resource_get_id(struct wl_resource *resource)
wl_resource_get_id(const struct wl_resource *resource)
{
return resource->object.id;
}
@ -837,7 +877,7 @@ wl_resource_get_user_data(struct wl_resource *resource)
}
WL_EXPORT int
wl_resource_get_version(struct wl_resource *resource)
wl_resource_get_version(const struct wl_resource *resource)
{
return resource->version;
}
@ -884,11 +924,25 @@ wl_resource_get_destroy_listener(struct wl_resource *resource,
* \memberof wl_resource
*/
WL_EXPORT const char *
wl_resource_get_class(struct wl_resource *resource)
wl_resource_get_class(const struct wl_resource *resource)
{
return resource->object.interface->name;
}
/** Get the interface of a resource object
*
* \param resource The resource object
* \return The interface of the object associated with the resource
*
* \memberof wl_resource
* \since 1.24
*/
WL_EXPORT const struct wl_interface *
wl_resource_get_interface(struct wl_resource *resource)
{
return resource->object.interface;
}
/**
* Add a listener to be called at the beginning of wl_client destruction
*
@ -1138,10 +1192,23 @@ wl_display_create(void)
{
struct wl_display *display;
const char *debug;
const char *no_color;
const char *force_color;
no_color = getenv("NO_COLOR");
force_color = getenv("FORCE_COLOR");
debug = getenv("WAYLAND_DEBUG");
if (debug && (strstr(debug, "server") || strstr(debug, "1")))
if (debug && (wl_check_env_token(debug, "server") || wl_check_env_token(debug, "1"))) {
debug_server = 1;
if (isatty(fileno(stderr)))
debug_color = 1;
}
if (force_color && force_color[0] != '\0')
debug_color = 1;
if (no_color && no_color[0] != '\0')
debug_color = 0;
display = zalloc(sizeof *display);
if (display == NULL)
@ -1480,7 +1547,7 @@ wl_global_set_user_data(struct wl_global *global, void *data)
* \memberof wl_display
*/
WL_EXPORT uint32_t
wl_display_get_serial(struct wl_display *display)
wl_display_get_serial(const struct wl_display *display)
{
return display->serial;
}
@ -1517,7 +1584,8 @@ wl_display_terminate(struct wl_display *display)
display->run = false;
ret = write(display->terminate_efd, &terminate, sizeof(terminate));
assert (ret >= 0 || errno == EAGAIN);
if (ret < 0 && errno != EAGAIN)
wl_abort("Write failed at shutdown\n");
}
WL_EXPORT void
@ -1714,7 +1782,8 @@ wl_socket_init_for_display_name(struct wl_socket *s, const char *name)
name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path,
"%s%s%s", runtime_dir, separator, name) + 1;
assert(name_size > 0);
if (!(name_size > 0))
wl_abort("Error assigning path name for socket address\n");
if (name_size > (int)sizeof s->addr.sun_path) {
wl_log("error: socket path \"%s%s%s\" plus null terminator"
" exceeds 108 bytes\n", runtime_dir, separator, name);
@ -1762,6 +1831,24 @@ _wl_display_add_socket(struct wl_display *display, struct wl_socket *s)
return 0;
}
/** Automatically pick a Wayland display socket for the clients to connect to.
*
* \param display Wayland display to which the socket should be added.
* \return The socket name if success. NULL if failed.
*
* This adds a Unix socket to Wayland display which can be used by clients to
* connect to Wayland display. The name of the socket is chosen automatically
* as the first available name in the sequence "wayland-0", "wayland-1",
* "wayland-2", ..., "wayland-32".
*
* The string returned by this function is owned by the library and should
* not be freed.
*
* \sa wl_display_add_socket
*
* \memberof wl_display
*/
WL_EXPORT const char *
wl_display_add_socket_auto(struct wl_display *display)
{
@ -2042,7 +2129,7 @@ wl_log_set_handler_server(wl_log_func_t handler)
* \param func The function to call to log a new protocol message
* \param user_data The user data pointer to pass to \a func
*
* \return The protol logger object on success, NULL on failure.
* \return The protocol logger object on success, NULL on failure.
*
* \sa wl_protocol_logger_destroy
*
@ -2483,9 +2570,10 @@ wl_priv_signal_final_emit(struct wl_priv_signal *signal, void *data)
/** \cond */ /* Deprecated functions below. */
WL_DEPRECATED
uint32_t
wl_client_add_resource(struct wl_client *client,
struct wl_resource *resource) WL_DEPRECATED;
struct wl_resource *resource);
WL_EXPORT uint32_t
wl_client_add_resource(struct wl_client *client,
@ -2514,11 +2602,12 @@ wl_client_add_resource(struct wl_client *client,
return resource->object.id;
}
WL_DEPRECATED
struct wl_resource *
wl_client_add_object(struct wl_client *client,
const struct wl_interface *interface,
const void *implementation,
uint32_t id, void *data) WL_DEPRECATED;
uint32_t id, void *data);
WL_EXPORT struct wl_resource *
wl_client_add_object(struct wl_client *client,
@ -2537,10 +2626,11 @@ wl_client_add_object(struct wl_client *client,
return resource;
}
WL_DEPRECATED
struct wl_resource *
wl_client_new_object(struct wl_client *client,
const struct wl_interface *interface,
const void *implementation, void *data) WL_DEPRECATED;
const void *implementation, void *data);
WL_EXPORT struct wl_resource *
wl_client_new_object(struct wl_client *client,
@ -2599,10 +2689,11 @@ wl_client_get_user_data(struct wl_client *client)
return client->data;
}
WL_DEPRECATED
struct wl_global *
wl_display_add_global(struct wl_display *display,
const struct wl_interface *interface,
void *data, wl_global_bind_func_t bind) WL_DEPRECATED;
void *data, wl_global_bind_func_t bind);
WL_EXPORT struct wl_global *
wl_display_add_global(struct wl_display *display,
@ -2612,9 +2703,10 @@ wl_display_add_global(struct wl_display *display,
return wl_global_create(display, interface, interface->version, data, bind);
}
WL_DEPRECATED
void
wl_display_remove_global(struct wl_display *display,
struct wl_global *global) WL_DEPRECATED;
struct wl_global *global);
WL_EXPORT void
wl_display_remove_global(struct wl_display *display, struct wl_global *global)

View file

@ -70,30 +70,35 @@ struct wl_resource {
void *data;
};
WL_DEPRECATED
uint32_t
wl_client_add_resource(struct wl_client *client,
struct wl_resource *resource) WL_DEPRECATED;
struct wl_resource *resource);
WL_DEPRECATED
struct wl_resource *
wl_client_add_object(struct wl_client *client,
const struct wl_interface *interface,
const void *implementation,
uint32_t id, void *data) WL_DEPRECATED;
uint32_t id, void *data);
WL_DEPRECATED
struct wl_resource *
wl_client_new_object(struct wl_client *client,
const struct wl_interface *interface,
const void *implementation, void *data) WL_DEPRECATED;
const void *implementation, void *data);
WL_DEPRECATED
struct wl_global *
wl_display_add_global(struct wl_display *display,
const struct wl_interface *interface,
void *data,
wl_global_bind_func_t bind) WL_DEPRECATED;
wl_global_bind_func_t bind);
WL_DEPRECATED
void
wl_display_remove_global(struct wl_display *display,
struct wl_global *global) WL_DEPRECATED;
struct wl_global *global);
#endif

View file

@ -40,7 +40,6 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
@ -85,6 +84,10 @@ struct wl_shm_pool {
*/
struct wl_shm_buffer {
struct wl_resource *resource;
int internal_refcount;
int external_refcount;
struct wl_client *client;
struct wl_listener client_destroy_listener;
int32_t width, height;
int32_t stride;
uint32_t format;
@ -144,12 +147,16 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external)
{
if (external) {
pool->external_refcount--;
assert(pool->external_refcount >= 0);
if (pool->external_refcount < 0)
wl_abort("Requested to unref an external reference to "
"pool but none found\n");
if (pool->external_refcount == 0)
shm_pool_finish_resize(pool);
} else {
pool->internal_refcount--;
assert(pool->internal_refcount >= 0);
if (pool->internal_refcount < 0)
wl_abort("Requested to unref an internal reference to "
"pool but none found\n");
}
if (pool->internal_refcount + pool->external_refcount > 0)
@ -162,13 +169,38 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external)
free(pool);
}
static void
shm_buffer_unref(struct wl_shm_buffer *buffer, bool external)
{
if (external) {
buffer->external_refcount--;
if (buffer->external_refcount < 0) {
wl_abort("Requested to unref an external reference to "
"buffer but none found\n");
}
} else {
buffer->internal_refcount--;
if (buffer->internal_refcount < 0) {
wl_abort("Requested to unref an internal reference to "
"buffer but none found\n");
}
}
if (buffer->internal_refcount + buffer->external_refcount > 0)
return;
if (buffer->client)
wl_list_remove(&buffer->client_destroy_listener.link);
shm_pool_unref(buffer->pool, false);
free(buffer);
}
static void
destroy_buffer(struct wl_resource *resource)
{
struct wl_shm_buffer *buffer = wl_resource_get_user_data(resource);
shm_pool_unref(buffer->pool, false);
free(buffer);
shm_buffer_unref(buffer, false);
}
static void
@ -202,6 +234,17 @@ format_is_supported(struct wl_client *client, uint32_t format)
return false;
}
static void
shm_buffer_client_destroy_notify(struct wl_listener *listener, void *data)
{
struct wl_shm_buffer *buffer =
wl_container_of(listener, buffer, client_destroy_listener);
buffer->client = NULL;
wl_list_remove(&buffer->client_destroy_listener.link);
}
static void
shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource,
uint32_t id, int32_t offset,
@ -234,6 +277,14 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource,
return;
}
buffer->client = client;
buffer->client_destroy_listener.notify =
shm_buffer_client_destroy_notify;
wl_client_add_destroy_listener(buffer->client,
&buffer->client_destroy_listener);
buffer->internal_refcount = 1;
buffer->external_refcount = 0;
buffer->width = width;
buffer->height = height;
buffer->format = format;
@ -441,7 +492,7 @@ wl_shm_buffer_get(struct wl_resource *resource)
}
WL_EXPORT int32_t
wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer)
wl_shm_buffer_get_stride(const struct wl_shm_buffer *buffer)
{
return buffer->stride;
}
@ -458,8 +509,8 @@ wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer)
* SIGBUS signals. This can happen if the client claims that the
* buffer is larger than it is or if something truncates the
* underlying file. To prevent this signal from causing the compositor
* to crash you should call wl_shm_buffer_begin_access and
* wl_shm_buffer_end_access around code that reads from the memory.
* to crash you should call wl_shm_buffer_begin_access() and
* wl_shm_buffer_end_access() around code that reads from the memory.
*
* \memberof wl_shm_buffer
*/
@ -475,23 +526,62 @@ wl_shm_buffer_get_data(struct wl_shm_buffer *buffer)
}
WL_EXPORT uint32_t
wl_shm_buffer_get_format(struct wl_shm_buffer *buffer)
wl_shm_buffer_get_format(const struct wl_shm_buffer *buffer)
{
return buffer->format;
}
WL_EXPORT int32_t
wl_shm_buffer_get_width(struct wl_shm_buffer *buffer)
wl_shm_buffer_get_width(const struct wl_shm_buffer *buffer)
{
return buffer->width;
}
WL_EXPORT int32_t
wl_shm_buffer_get_height(struct wl_shm_buffer *buffer)
wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer)
{
return buffer->height;
}
/** Reference a shm_buffer
*
* \param buffer The buffer object
*
* Returns a pointer to the buffer and increases the refcount.
*
* The compositor must remember to call wl_shm_buffer_unref() when
* it no longer needs the reference to ensure proper destruction
* of the buffer.
*
* \memberof wl_shm_buffer
* \sa wl_shm_buffer_unref
*/
WL_EXPORT struct wl_shm_buffer *
wl_shm_buffer_ref(struct wl_shm_buffer *buffer)
{
buffer->external_refcount++;
return buffer;
}
/** Unreference a shm_buffer
*
* \param buffer The buffer object
*
* Drops a reference to a buffer object.
*
* This is only necessary if the compositor has explicitly
* taken a reference with wl_shm_buffer_ref(), otherwise
* the buffer will be automatically destroyed when appropriate.
*
* \memberof wl_shm_buffer
* \sa wl_shm_buffer_ref
*/
WL_EXPORT void
wl_shm_buffer_unref(struct wl_shm_buffer *buffer)
{
shm_buffer_unref(buffer, true);
}
/** Get a reference to a shm_buffer's shm_pool
*
* \param buffer The buffer object
@ -499,7 +589,7 @@ wl_shm_buffer_get_height(struct wl_shm_buffer *buffer)
* Returns a pointer to a buffer's shm_pool and increases the
* shm_pool refcount.
*
* The compositor must remember to call wl_shm_pool_unref when
* The compositor must remember to call wl_shm_pool_unref() when
* it no longer needs the reference to ensure proper destruction
* of the pool.
*
@ -509,9 +599,6 @@ wl_shm_buffer_get_height(struct wl_shm_buffer *buffer)
WL_EXPORT struct wl_shm_pool *
wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer)
{
assert(buffer->pool->internal_refcount +
buffer->pool->external_refcount);
buffer->pool->external_refcount++;
return buffer->pool;
}
@ -613,7 +700,7 @@ init_sigbus_data_key(void)
* In order to make the compositor robust against clients that change
* the size of the underlying file or lie about its size, you should
* protect access to the buffer by calling this function before
* reading from the memory and call wl_shm_buffer_end_access
* reading from the memory and call wl_shm_buffer_end_access()
* afterwards. This will install a signal handler for SIGBUS which
* will prevent the compositor from crashing.
*
@ -624,15 +711,15 @@ init_sigbus_data_key(void)
*
* If a SIGBUS signal is received for an address within the range of
* the SHM pool of the given buffer then the client will be sent an
* error event when wl_shm_buffer_end_access is called. If the signal
* error event when wl_shm_buffer_end_access() is called. If the signal
* is for an address outside that range then the signal handler will
* reraise the signal which would will likely cause the compositor to
* terminate.
*
* It is safe to nest calls to these functions as long as the nested
* calls are all accessing the same buffer. The number of calls to
* wl_shm_buffer_end_access must match the number of calls to
* wl_shm_buffer_begin_access. These functions are thread-safe and it
* calls are all accessing the same pool. The number of calls to
* wl_shm_buffer_end_access() must match the number of calls to
* wl_shm_buffer_begin_access(). These functions are thread-safe and it
* is allowed to simultaneously access different buffers or the same
* buffer from multiple threads.
*
@ -658,18 +745,19 @@ wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer)
pthread_setspecific(wl_shm_sigbus_data_key, sigbus_data);
}
assert(sigbus_data->current_pool == NULL ||
sigbus_data->current_pool == pool);
if (!(sigbus_data->current_pool == NULL ||
sigbus_data->current_pool == pool))
wl_abort("Incorrect pool passed for current thread\n");
sigbus_data->current_pool = pool;
sigbus_data->access_count++;
}
/** Ends the access to a buffer started by wl_shm_buffer_begin_access
/** Ends the access to a buffer started by wl_shm_buffer_begin_access()
*
* \param buffer The SHM buffer
*
* This should be called after wl_shm_buffer_begin_access once the
* This should be called after wl_shm_buffer_begin_access() once the
* buffer is no longer being accessed. If a SIGBUS signal was
* generated in-between these two calls then the resource for the
* given buffer will be sent an error.
@ -686,13 +774,22 @@ wl_shm_buffer_end_access(struct wl_shm_buffer *buffer)
return;
sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
assert(sigbus_data && sigbus_data->access_count >= 1);
if (!(sigbus_data && sigbus_data->access_count >= 1))
wl_abort("sigbus_data is NULL or wl_shm_buffer_begin_access "
"wasn't called before\n");
if (--sigbus_data->access_count == 0) {
if (sigbus_data->fallback_mapping_used) {
wl_resource_post_error(buffer->resource,
WL_SHM_ERROR_INVALID_FD,
"error accessing SHM buffer");
if (buffer->resource) {
wl_resource_post_error(buffer->resource,
WL_SHM_ERROR_INVALID_FD,
"error accessing SHM buffer");
} else if (buffer->client) {
wl_client_post_implementation_error(buffer->client,
"Error accessing SHM buffer of a "
"wl_buffer resource which has "
"already been destroyed");
}
sigbus_data->fallback_mapping_used = 0;
}

View file

@ -48,7 +48,7 @@ extern "C" {
#endif
/** Deprecated attribute */
#if __STDC_VERSION__ >= 202311L
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || (defined(__cplusplus) && __cplusplus >= 201402L)
#define WL_DEPRECATED [[deprecated]]
#elif defined(__GNUC__) && __GNUC__ >= 4
#define WL_DEPRECATED __attribute__ ((deprecated))
@ -70,7 +70,7 @@ extern "C" {
#define WL_PRINTF(x, y)
#endif
#if __STDC_VERSION__ >= 202311L
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
#define WL_TYPEOF(expr) typeof(expr)
#else
#define WL_TYPEOF(expr) __typeof__(expr)
@ -90,6 +90,14 @@ extern "C" {
*/
struct wl_object;
/**
* The maximum size of a protocol message.
*
* If a message size exceeds this value, the connection will be dropped.
* Servers will send an invalid_method error before disconnecting.
*/
#define WL_MAX_MESSAGE_SIZE 4096
/**
* Protocol message signature
*
@ -635,7 +643,7 @@ wl_fixed_to_double(wl_fixed_t f)
static inline wl_fixed_t
wl_fixed_from_double(double d)
{
return (wl_fixed_t) (d * 256.0);
return (wl_fixed_t) (round(d * 256.0));
}
/**

View file

@ -686,7 +686,7 @@ TEST(connection_marshal_big_enough)
TEST(connection_marshal_unbounded_boundary_size)
{
/* A string of lenth 8178 requires a buffer size of exactly 2^13. */
/* A string of length 8178 requires a buffer size of exactly 2^13. */
struct marshal_data data;
char *big_string = malloc(8178);
assert(big_string);

View file

@ -901,6 +901,10 @@ enum wl_display_error {
*/
WL_DISPLAY_ERROR_NO_MEMORY = 2,
};
#endif /* WL_DISPLAY_ERROR_ENUM */
#ifndef WL_DISPLAY_ERROR_ENUM_IS_VALID
#define WL_DISPLAY_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_display
* Validate a wl_display error value.
@ -921,7 +925,7 @@ wl_display_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_DISPLAY_ERROR_ENUM */
#endif /* WL_DISPLAY_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_wl_display
@ -1194,6 +1198,10 @@ enum wl_shm_error {
*/
WL_SHM_ERROR_INVALID_FD = 2,
};
#endif /* WL_SHM_ERROR_ENUM */
#ifndef WL_SHM_ERROR_ENUM_IS_VALID
#define WL_SHM_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_shm
* Validate a wl_shm error value.
@ -1214,7 +1222,7 @@ wl_shm_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_SHM_ERROR_ENUM */
#endif /* WL_SHM_ERROR_ENUM_IS_VALID */
#ifndef WL_SHM_FORMAT_ENUM
#define WL_SHM_FORMAT_ENUM
@ -1466,6 +1474,10 @@ enum wl_shm_format {
*/
WL_SHM_FORMAT_YVU444 = 0x34325659,
};
#endif /* WL_SHM_FORMAT_ENUM */
#ifndef WL_SHM_FORMAT_ENUM_IS_VALID
#define WL_SHM_FORMAT_ENUM_IS_VALID
/**
* @ingroup iface_wl_shm
* Validate a wl_shm format value.
@ -1596,7 +1608,7 @@ wl_shm_format_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_SHM_FORMAT_ENUM */
#endif /* WL_SHM_FORMAT_ENUM_IS_VALID */
/**
* @ingroup iface_wl_shm
@ -1706,6 +1718,10 @@ enum wl_data_offer_error {
*/
WL_DATA_OFFER_ERROR_INVALID_OFFER = 3,
};
#endif /* WL_DATA_OFFER_ERROR_ENUM */
#ifndef WL_DATA_OFFER_ERROR_ENUM_IS_VALID
#define WL_DATA_OFFER_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_data_offer
* Validate a wl_data_offer error value.
@ -1728,7 +1744,7 @@ wl_data_offer_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_DATA_OFFER_ERROR_ENUM */
#endif /* WL_DATA_OFFER_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_wl_data_offer
@ -1940,6 +1956,10 @@ enum wl_data_source_error {
*/
WL_DATA_SOURCE_ERROR_INVALID_SOURCE = 1,
};
#endif /* WL_DATA_SOURCE_ERROR_ENUM */
#ifndef WL_DATA_SOURCE_ERROR_ENUM_IS_VALID
#define WL_DATA_SOURCE_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_data_source
* Validate a wl_data_source error value.
@ -1958,7 +1978,7 @@ wl_data_source_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_DATA_SOURCE_ERROR_ENUM */
#endif /* WL_DATA_SOURCE_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_wl_data_source
@ -2130,6 +2150,10 @@ enum wl_data_device_error {
*/
WL_DATA_DEVICE_ERROR_ROLE = 0,
};
#endif /* WL_DATA_DEVICE_ERROR_ENUM */
#ifndef WL_DATA_DEVICE_ERROR_ENUM_IS_VALID
#define WL_DATA_DEVICE_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_data_device
* Validate a wl_data_device error value.
@ -2146,7 +2170,7 @@ wl_data_device_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_DATA_DEVICE_ERROR_ENUM */
#endif /* WL_DATA_DEVICE_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_wl_data_device
@ -2387,6 +2411,10 @@ enum wl_data_device_manager_dnd_action {
*/
WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK = 4,
};
#endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM */
#ifndef WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM_IS_VALID
#define WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM_IS_VALID
/**
* @ingroup iface_wl_data_device_manager
* Validate a wl_data_device_manager dnd_action value.
@ -2396,20 +2424,18 @@ enum wl_data_device_manager_dnd_action {
*/
static inline bool
wl_data_device_manager_dnd_action_is_valid(uint32_t value, uint32_t version) {
switch (value) {
case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE:
return version >= 1;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY:
return version >= 1;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE:
return version >= 1;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK:
return version >= 1;
default:
return false;
}
uint32_t valid = 0;
if (version >= 1)
valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
if (version >= 1)
valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
if (version >= 1)
valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
if (version >= 1)
valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
return (value & ~valid) == 0;
}
#endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM */
#endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM_IS_VALID */
/**
* @ingroup iface_wl_data_device_manager
@ -2456,6 +2482,10 @@ enum wl_shell_error {
*/
WL_SHELL_ERROR_ROLE = 0,
};
#endif /* WL_SHELL_ERROR_ENUM */
#ifndef WL_SHELL_ERROR_ENUM_IS_VALID
#define WL_SHELL_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_shell
* Validate a wl_shell error value.
@ -2472,7 +2502,7 @@ wl_shell_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_SHELL_ERROR_ENUM */
#endif /* WL_SHELL_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_wl_shell
@ -2551,6 +2581,10 @@ enum wl_shell_surface_resize {
*/
WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT = 10,
};
#endif /* WL_SHELL_SURFACE_RESIZE_ENUM */
#ifndef WL_SHELL_SURFACE_RESIZE_ENUM_IS_VALID
#define WL_SHELL_SURFACE_RESIZE_ENUM_IS_VALID
/**
* @ingroup iface_wl_shell_surface
* Validate a wl_shell_surface resize value.
@ -2560,30 +2594,28 @@ enum wl_shell_surface_resize {
*/
static inline bool
wl_shell_surface_resize_is_valid(uint32_t value, uint32_t version) {
switch (value) {
case WL_SHELL_SURFACE_RESIZE_NONE:
return version >= 1;
case WL_SHELL_SURFACE_RESIZE_TOP:
return version >= 1;
case WL_SHELL_SURFACE_RESIZE_BOTTOM:
return version >= 1;
case WL_SHELL_SURFACE_RESIZE_LEFT:
return version >= 1;
case WL_SHELL_SURFACE_RESIZE_TOP_LEFT:
return version >= 1;
case WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT:
return version >= 1;
case WL_SHELL_SURFACE_RESIZE_RIGHT:
return version >= 1;
case WL_SHELL_SURFACE_RESIZE_TOP_RIGHT:
return version >= 1;
case WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT:
return version >= 1;
default:
return false;
}
uint32_t valid = 0;
if (version >= 1)
valid |= WL_SHELL_SURFACE_RESIZE_NONE;
if (version >= 1)
valid |= WL_SHELL_SURFACE_RESIZE_TOP;
if (version >= 1)
valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM;
if (version >= 1)
valid |= WL_SHELL_SURFACE_RESIZE_LEFT;
if (version >= 1)
valid |= WL_SHELL_SURFACE_RESIZE_TOP_LEFT;
if (version >= 1)
valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT;
if (version >= 1)
valid |= WL_SHELL_SURFACE_RESIZE_RIGHT;
if (version >= 1)
valid |= WL_SHELL_SURFACE_RESIZE_TOP_RIGHT;
if (version >= 1)
valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT;
return (value & ~valid) == 0;
}
#endif /* WL_SHELL_SURFACE_RESIZE_ENUM */
#endif /* WL_SHELL_SURFACE_RESIZE_ENUM_IS_VALID */
#ifndef WL_SHELL_SURFACE_TRANSIENT_ENUM
#define WL_SHELL_SURFACE_TRANSIENT_ENUM
@ -2600,6 +2632,10 @@ enum wl_shell_surface_transient {
*/
WL_SHELL_SURFACE_TRANSIENT_INACTIVE = 0x1,
};
#endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM */
#ifndef WL_SHELL_SURFACE_TRANSIENT_ENUM_IS_VALID
#define WL_SHELL_SURFACE_TRANSIENT_ENUM_IS_VALID
/**
* @ingroup iface_wl_shell_surface
* Validate a wl_shell_surface transient value.
@ -2609,14 +2645,12 @@ enum wl_shell_surface_transient {
*/
static inline bool
wl_shell_surface_transient_is_valid(uint32_t value, uint32_t version) {
switch (value) {
case WL_SHELL_SURFACE_TRANSIENT_INACTIVE:
return version >= 1;
default:
return false;
}
uint32_t valid = 0;
if (version >= 1)
valid |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE;
return (value & ~valid) == 0;
}
#endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM */
#endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM_IS_VALID */
#ifndef WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM
#define WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM
@ -2646,6 +2680,10 @@ enum wl_shell_surface_fullscreen_method {
*/
WL_SHELL_SURFACE_FULLSCREEN_METHOD_FILL = 3,
};
#endif /* WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM */
#ifndef WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM_IS_VALID
#define WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM_IS_VALID
/**
* @ingroup iface_wl_shell_surface
* Validate a wl_shell_surface fullscreen_method value.
@ -2668,7 +2706,7 @@ wl_shell_surface_fullscreen_method_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM */
#endif /* WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM_IS_VALID */
/**
* @ingroup iface_wl_shell_surface
@ -3000,6 +3038,10 @@ enum wl_surface_error {
*/
WL_SURFACE_ERROR_INVALID_TRANSFORM = 1,
};
#endif /* WL_SURFACE_ERROR_ENUM */
#ifndef WL_SURFACE_ERROR_ENUM_IS_VALID
#define WL_SURFACE_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_surface
* Validate a wl_surface error value.
@ -3018,7 +3060,7 @@ wl_surface_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_SURFACE_ERROR_ENUM */
#endif /* WL_SURFACE_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_wl_surface
@ -3477,6 +3519,10 @@ enum wl_seat_capability {
*/
WL_SEAT_CAPABILITY_TOUCH = 4,
};
#endif /* WL_SEAT_CAPABILITY_ENUM */
#ifndef WL_SEAT_CAPABILITY_ENUM_IS_VALID
#define WL_SEAT_CAPABILITY_ENUM_IS_VALID
/**
* @ingroup iface_wl_seat
* Validate a wl_seat capability value.
@ -3486,18 +3532,16 @@ enum wl_seat_capability {
*/
static inline bool
wl_seat_capability_is_valid(uint32_t value, uint32_t version) {
switch (value) {
case WL_SEAT_CAPABILITY_POINTER:
return version >= 1;
case WL_SEAT_CAPABILITY_KEYBOARD:
return version >= 1;
case WL_SEAT_CAPABILITY_TOUCH:
return version >= 1;
default:
return false;
}
uint32_t valid = 0;
if (version >= 1)
valid |= WL_SEAT_CAPABILITY_POINTER;
if (version >= 1)
valid |= WL_SEAT_CAPABILITY_KEYBOARD;
if (version >= 1)
valid |= WL_SEAT_CAPABILITY_TOUCH;
return (value & ~valid) == 0;
}
#endif /* WL_SEAT_CAPABILITY_ENUM */
#endif /* WL_SEAT_CAPABILITY_ENUM_IS_VALID */
/**
* @ingroup iface_wl_seat
@ -3621,6 +3665,10 @@ enum wl_pointer_error {
*/
WL_POINTER_ERROR_ROLE = 0,
};
#endif /* WL_POINTER_ERROR_ENUM */
#ifndef WL_POINTER_ERROR_ENUM_IS_VALID
#define WL_POINTER_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_pointer
* Validate a wl_pointer error value.
@ -3637,7 +3685,7 @@ wl_pointer_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_POINTER_ERROR_ENUM */
#endif /* WL_POINTER_ERROR_ENUM_IS_VALID */
#ifndef WL_POINTER_BUTTON_STATE_ENUM
#define WL_POINTER_BUTTON_STATE_ENUM
@ -3658,6 +3706,10 @@ enum wl_pointer_button_state {
*/
WL_POINTER_BUTTON_STATE_PRESSED = 1,
};
#endif /* WL_POINTER_BUTTON_STATE_ENUM */
#ifndef WL_POINTER_BUTTON_STATE_ENUM_IS_VALID
#define WL_POINTER_BUTTON_STATE_ENUM_IS_VALID
/**
* @ingroup iface_wl_pointer
* Validate a wl_pointer button_state value.
@ -3676,7 +3728,7 @@ wl_pointer_button_state_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_POINTER_BUTTON_STATE_ENUM */
#endif /* WL_POINTER_BUTTON_STATE_ENUM_IS_VALID */
#ifndef WL_POINTER_AXIS_ENUM
#define WL_POINTER_AXIS_ENUM
@ -3696,6 +3748,10 @@ enum wl_pointer_axis {
*/
WL_POINTER_AXIS_HORIZONTAL_SCROLL = 1,
};
#endif /* WL_POINTER_AXIS_ENUM */
#ifndef WL_POINTER_AXIS_ENUM_IS_VALID
#define WL_POINTER_AXIS_ENUM_IS_VALID
/**
* @ingroup iface_wl_pointer
* Validate a wl_pointer axis value.
@ -3714,7 +3770,7 @@ wl_pointer_axis_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_POINTER_AXIS_ENUM */
#endif /* WL_POINTER_AXIS_ENUM_IS_VALID */
#ifndef WL_POINTER_AXIS_SOURCE_ENUM
#define WL_POINTER_AXIS_SOURCE_ENUM
@ -3762,6 +3818,10 @@ enum wl_pointer_axis_source {
* @ingroup iface_wl_pointer
*/
#define WL_POINTER_AXIS_SOURCE_WHEEL_TILT_SINCE_VERSION 6
#endif /* WL_POINTER_AXIS_SOURCE_ENUM */
#ifndef WL_POINTER_AXIS_SOURCE_ENUM_IS_VALID
#define WL_POINTER_AXIS_SOURCE_ENUM_IS_VALID
/**
* @ingroup iface_wl_pointer
* Validate a wl_pointer axis_source value.
@ -3784,7 +3844,7 @@ wl_pointer_axis_source_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_POINTER_AXIS_SOURCE_ENUM */
#endif /* WL_POINTER_AXIS_SOURCE_ENUM_IS_VALID */
/**
* @ingroup iface_wl_pointer
@ -4045,6 +4105,10 @@ enum wl_keyboard_keymap_format {
*/
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 = 1,
};
#endif /* WL_KEYBOARD_KEYMAP_FORMAT_ENUM */
#ifndef WL_KEYBOARD_KEYMAP_FORMAT_ENUM_IS_VALID
#define WL_KEYBOARD_KEYMAP_FORMAT_ENUM_IS_VALID
/**
* @ingroup iface_wl_keyboard
* Validate a wl_keyboard keymap_format value.
@ -4063,7 +4127,7 @@ wl_keyboard_keymap_format_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_KEYBOARD_KEYMAP_FORMAT_ENUM */
#endif /* WL_KEYBOARD_KEYMAP_FORMAT_ENUM_IS_VALID */
#ifndef WL_KEYBOARD_KEY_STATE_ENUM
#define WL_KEYBOARD_KEY_STATE_ENUM
@ -4083,6 +4147,10 @@ enum wl_keyboard_key_state {
*/
WL_KEYBOARD_KEY_STATE_PRESSED = 1,
};
#endif /* WL_KEYBOARD_KEY_STATE_ENUM */
#ifndef WL_KEYBOARD_KEY_STATE_ENUM_IS_VALID
#define WL_KEYBOARD_KEY_STATE_ENUM_IS_VALID
/**
* @ingroup iface_wl_keyboard
* Validate a wl_keyboard key_state value.
@ -4101,7 +4169,7 @@ wl_keyboard_key_state_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_KEYBOARD_KEY_STATE_ENUM */
#endif /* WL_KEYBOARD_KEY_STATE_ENUM_IS_VALID */
/**
* @ingroup iface_wl_keyboard
@ -4427,6 +4495,10 @@ enum wl_output_subpixel {
*/
WL_OUTPUT_SUBPIXEL_VERTICAL_BGR = 5,
};
#endif /* WL_OUTPUT_SUBPIXEL_ENUM */
#ifndef WL_OUTPUT_SUBPIXEL_ENUM_IS_VALID
#define WL_OUTPUT_SUBPIXEL_ENUM_IS_VALID
/**
* @ingroup iface_wl_output
* Validate a wl_output subpixel value.
@ -4453,7 +4525,7 @@ wl_output_subpixel_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_OUTPUT_SUBPIXEL_ENUM */
#endif /* WL_OUTPUT_SUBPIXEL_ENUM_IS_VALID */
#ifndef WL_OUTPUT_TRANSFORM_ENUM
#define WL_OUTPUT_TRANSFORM_ENUM
@ -4507,6 +4579,10 @@ enum wl_output_transform {
*/
WL_OUTPUT_TRANSFORM_FLIPPED_270 = 7,
};
#endif /* WL_OUTPUT_TRANSFORM_ENUM */
#ifndef WL_OUTPUT_TRANSFORM_ENUM_IS_VALID
#define WL_OUTPUT_TRANSFORM_ENUM_IS_VALID
/**
* @ingroup iface_wl_output
* Validate a wl_output transform value.
@ -4537,7 +4613,7 @@ wl_output_transform_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_OUTPUT_TRANSFORM_ENUM */
#endif /* WL_OUTPUT_TRANSFORM_ENUM_IS_VALID */
#ifndef WL_OUTPUT_MODE_ENUM
#define WL_OUTPUT_MODE_ENUM
@ -4558,6 +4634,10 @@ enum wl_output_mode {
*/
WL_OUTPUT_MODE_PREFERRED = 0x2,
};
#endif /* WL_OUTPUT_MODE_ENUM */
#ifndef WL_OUTPUT_MODE_ENUM_IS_VALID
#define WL_OUTPUT_MODE_ENUM_IS_VALID
/**
* @ingroup iface_wl_output
* Validate a wl_output mode value.
@ -4567,16 +4647,14 @@ enum wl_output_mode {
*/
static inline bool
wl_output_mode_is_valid(uint32_t value, uint32_t version) {
switch (value) {
case WL_OUTPUT_MODE_CURRENT:
return version >= 1;
case WL_OUTPUT_MODE_PREFERRED:
return version >= 1;
default:
return false;
}
uint32_t valid = 0;
if (version >= 1)
valid |= WL_OUTPUT_MODE_CURRENT;
if (version >= 1)
valid |= WL_OUTPUT_MODE_PREFERRED;
return (value & ~valid) == 0;
}
#endif /* WL_OUTPUT_MODE_ENUM */
#endif /* WL_OUTPUT_MODE_ENUM_IS_VALID */
/**
* @ingroup iface_wl_output
@ -4744,6 +4822,10 @@ enum wl_subcompositor_error {
*/
WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE = 0,
};
#endif /* WL_SUBCOMPOSITOR_ERROR_ENUM */
#ifndef WL_SUBCOMPOSITOR_ERROR_ENUM_IS_VALID
#define WL_SUBCOMPOSITOR_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_subcompositor
* Validate a wl_subcompositor error value.
@ -4760,7 +4842,7 @@ wl_subcompositor_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_SUBCOMPOSITOR_ERROR_ENUM */
#endif /* WL_SUBCOMPOSITOR_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_wl_subcompositor
@ -4815,6 +4897,10 @@ enum wl_subsurface_error {
*/
WL_SUBSURFACE_ERROR_BAD_SURFACE = 0,
};
#endif /* WL_SUBSURFACE_ERROR_ENUM */
#ifndef WL_SUBSURFACE_ERROR_ENUM_IS_VALID
#define WL_SUBSURFACE_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_wl_subsurface
* Validate a wl_subsurface error value.
@ -4831,7 +4917,7 @@ wl_subsurface_error_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* WL_SUBSURFACE_ERROR_ENUM */
#endif /* WL_SUBSURFACE_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_wl_subsurface

View file

@ -106,6 +106,29 @@ enum intf_A_foo {
#define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2
#endif /* INTF_A_FOO_ENUM */
#ifndef INTF_A_BAR_ENUM
#define INTF_A_BAR_ENUM
enum intf_A_bar {
/**
* this is the first
*/
INTF_A_BAR_FIRST = 0x01,
/**
* this is the second
*/
INTF_A_BAR_SECOND = 0x02,
/**
* this is the third
* @since 2
*/
INTF_A_BAR_THIRD = 0x04,
};
/**
* @ingroup iface_intf_A
*/
#define INTF_A_BAR_THIRD_SINCE_VERSION 2
#endif /* INTF_A_BAR_ENUM */
/**
* @ingroup iface_intf_A
* @struct intf_A_listener

View file

@ -106,6 +106,29 @@ enum intf_A_foo {
#define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2
#endif /* INTF_A_FOO_ENUM */
#ifndef INTF_A_BAR_ENUM
#define INTF_A_BAR_ENUM
enum intf_A_bar {
/**
* this is the first
*/
INTF_A_BAR_FIRST = 0x01,
/**
* this is the second
*/
INTF_A_BAR_SECOND = 0x02,
/**
* this is the third
* @since 2
*/
INTF_A_BAR_THIRD = 0x04,
};
/**
* @ingroup iface_intf_A
*/
#define INTF_A_BAR_THIRD_SINCE_VERSION 2
#endif /* INTF_A_BAR_ENUM */
/**
* @ingroup iface_intf_A
* @struct intf_A_listener

View file

@ -107,6 +107,10 @@ enum intf_A_foo {
* @ingroup iface_intf_A
*/
#define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2
#endif /* INTF_A_FOO_ENUM */
#ifndef INTF_A_FOO_ENUM_IS_VALID
#define INTF_A_FOO_ENUM_IS_VALID
/**
* @ingroup iface_intf_A
* Validate a intf_A foo value.
@ -131,7 +135,52 @@ intf_A_foo_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* INTF_A_FOO_ENUM */
#endif /* INTF_A_FOO_ENUM_IS_VALID */
#ifndef INTF_A_BAR_ENUM
#define INTF_A_BAR_ENUM
enum intf_A_bar {
/**
* this is the first
*/
INTF_A_BAR_FIRST = 0x01,
/**
* this is the second
*/
INTF_A_BAR_SECOND = 0x02,
/**
* this is the third
* @since 2
*/
INTF_A_BAR_THIRD = 0x04,
};
/**
* @ingroup iface_intf_A
*/
#define INTF_A_BAR_THIRD_SINCE_VERSION 2
#endif /* INTF_A_BAR_ENUM */
#ifndef INTF_A_BAR_ENUM_IS_VALID
#define INTF_A_BAR_ENUM_IS_VALID
/**
* @ingroup iface_intf_A
* Validate a intf_A bar value.
*
* @return true on success, false on error.
* @ref intf_A_bar
*/
static inline bool
intf_A_bar_is_valid(uint32_t value, uint32_t version) {
uint32_t valid = 0;
if (version >= 1)
valid |= INTF_A_BAR_FIRST;
if (version >= 1)
valid |= INTF_A_BAR_SECOND;
if (version >= 2)
valid |= INTF_A_BAR_THIRD;
return (value & ~valid) == 0;
}
#endif /* INTF_A_BAR_ENUM_IS_VALID */
/**
* @ingroup iface_intf_A

View file

@ -107,6 +107,10 @@ enum intf_A_foo {
* @ingroup iface_intf_A
*/
#define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2
#endif /* INTF_A_FOO_ENUM */
#ifndef INTF_A_FOO_ENUM_IS_VALID
#define INTF_A_FOO_ENUM_IS_VALID
/**
* @ingroup iface_intf_A
* Validate a intf_A foo value.
@ -131,7 +135,52 @@ intf_A_foo_is_valid(uint32_t value, uint32_t version) {
return false;
}
}
#endif /* INTF_A_FOO_ENUM */
#endif /* INTF_A_FOO_ENUM_IS_VALID */
#ifndef INTF_A_BAR_ENUM
#define INTF_A_BAR_ENUM
enum intf_A_bar {
/**
* this is the first
*/
INTF_A_BAR_FIRST = 0x01,
/**
* this is the second
*/
INTF_A_BAR_SECOND = 0x02,
/**
* this is the third
* @since 2
*/
INTF_A_BAR_THIRD = 0x04,
};
/**
* @ingroup iface_intf_A
*/
#define INTF_A_BAR_THIRD_SINCE_VERSION 2
#endif /* INTF_A_BAR_ENUM */
#ifndef INTF_A_BAR_ENUM_IS_VALID
#define INTF_A_BAR_ENUM_IS_VALID
/**
* @ingroup iface_intf_A
* Validate a intf_A bar value.
*
* @return true on success, false on error.
* @ref intf_A_bar
*/
static inline bool
intf_A_bar_is_valid(uint32_t value, uint32_t version) {
uint32_t valid = 0;
if (version >= 1)
valid |= INTF_A_BAR_FIRST;
if (version >= 1)
valid |= INTF_A_BAR_SECOND;
if (version >= 2)
valid |= INTF_A_BAR_THIRD;
return (value & ~valid) == 0;
}
#endif /* INTF_A_BAR_ENUM_IS_VALID */
/**
* @ingroup iface_intf_A

View file

@ -58,5 +58,12 @@
<entry name="negative" value="-1" since="2" summary="this is a negative value"/>
<entry name="deprecated" value="3" since="2" deprecated-since="3" summary="this is a deprecated value"/>
</enum>
<enum name="bar" bitfield="true">
<entry name="first" value="0x01" summary="this is the first"/>
<entry name="second" value="0x02" summary="this is the second"/>
<entry name="third" value="0x04" since="2" summary="this is the third"/>
</enum>
</interface>
</protocol>

View file

@ -924,7 +924,7 @@ TEST(versions)
}
static void
check_error_on_destroyed_object(void *data)
check_error_on_destroyed_object(void)
{
struct client *c;
struct wl_seat *seat;
@ -1043,7 +1043,7 @@ TEST(filtered_global_is_hidden)
1, d, bind_data_offer);
wl_display_set_global_filter(d->wl_display, global_filter, NULL);
client_create_noarg(d, get_globals);
client_create(d, get_globals, NULL);
display_run(d);
wl_global_destroy(g);
@ -1052,13 +1052,13 @@ TEST(filtered_global_is_hidden)
}
static void
get_dynamic_globals(void *data)
get_dynamic_globals(void)
{
struct client *c = client_connect();
struct wl_registry *registry;
registry = wl_display_get_registry(c->wl_display);
wl_registry_add_listener(registry, &registry_listener_filtered, data);
wl_registry_add_listener(registry, &registry_listener_filtered, NULL);
wl_display_roundtrip(c->wl_display);
/* Wait for the server to create a new global */
@ -1206,7 +1206,7 @@ static const struct wl_registry_listener zombie_fd_registry_listener = {
};
static void
zombie_client(void *data)
zombie_client(void)
{
struct client *c = client_connect();
struct wl_registry *registry;
@ -1376,7 +1376,7 @@ static const struct wl_registry_listener double_zombie_fd_registry_listener = {
};
static void
double_zombie_client(void *data)
double_zombie_client(void)
{
struct client *c = client_connect();
struct wl_registry *registry;
@ -1436,7 +1436,7 @@ static const struct wl_registry_listener bind_interface_mismatch_registry_listen
};
static void
registry_bind_interface_mismatch_client(void *data)
registry_bind_interface_mismatch_client(void)
{
struct client *c = client_connect();
struct wl_registry *registry;
@ -1598,7 +1598,7 @@ static const struct wl_registry_listener global_remove_before_registry_listener
};
static void
global_remove_before_client(void *data)
global_remove_before_client(void)
{
struct client *c = client_connect();
struct wl_registry *registry;
@ -1648,7 +1648,7 @@ static const struct wl_registry_listener global_remove_after_registry_listener =
};
static void
global_remove_after_client(void *data)
global_remove_after_client(void)
{
struct client *c = client_connect();
struct wl_registry *registry;
@ -1695,6 +1695,75 @@ TEST(global_remove)
display_destroy(d);
}
static void
dispatch_single_read_events(struct wl_display *d)
{
if (wl_display_prepare_read(d) < 0) {
return;
}
int ret = 0;
do {
ret = wl_display_flush(d);
} while (ret < 0 && (errno == EINTR || errno == EAGAIN));
assert(ret >= 0);
struct pollfd pfd[1];
pfd[0].fd = wl_display_get_fd(d);
pfd[0].events = POLLIN;
do {
ret = poll(pfd, 1, -1);
} while (ret < 0 && errno == EINTR);
assert(ret > 0);
wl_display_read_events(d);
}
static void
dispatch_single_client(void)
{
struct client *c = client_connect();
assert(wl_display_dispatch_pending_single(c->wl_display) == 0);
struct wl_registry *registry = wl_display_get_registry(c->wl_display);
dispatch_single_read_events(c->wl_display);
// [1815110.061] {Default Queue} wl_registry#3.global(1, "test", 1)
assert(wl_display_dispatch_pending_single(c->wl_display) == 1);
dispatch_single_read_events(c->wl_display);
// [1815110.067] {Default Queue} wl_registry#3.global(2, "wl_seat", 1)
assert(wl_display_dispatch_pending_single(c->wl_display) == 1);
// No more events
assert(wl_display_dispatch_pending_single(c->wl_display) == 0);
wl_registry_destroy(registry);
client_disconnect(c);
}
TEST(dispatch_single)
{
struct display *d = display_create();
struct wl_global *global = wl_global_create(d->wl_display,
&wl_seat_interface,
1, d, bind_seat);
client_create_noarg(d, dispatch_single_client);
display_run(d);
wl_global_destroy(global);
display_destroy(d);
}
static void
terminate_display(void *arg)
{

View file

@ -10,4 +10,19 @@ main(int argc, char *argv[]) {
assert(intf_A_foo_is_valid(INTF_A_FOO_THIRD, 2));
assert(intf_A_foo_is_valid(INTF_A_FOO_NEGATIVE, 2));
assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST, 1));
assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST, 2));
assert(intf_A_bar_is_valid(INTF_A_BAR_SECOND, 1));
assert(intf_A_bar_is_valid(INTF_A_BAR_SECOND, 2));
assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_SECOND, 1));
assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_SECOND, 2));
assert(!intf_A_bar_is_valid(INTF_A_BAR_THIRD, 1));
assert(!intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_THIRD, 1));
assert(intf_A_bar_is_valid(INTF_A_BAR_THIRD, 2));
assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_THIRD, 2));
assert(!intf_A_bar_is_valid(0xFF, 1));
assert(!intf_A_bar_is_valid(0xFF, 2));
}

View file

@ -54,7 +54,7 @@ tests_protocol_c = custom_target(
output: 'tests-protocol.c'
)
executable(
exec_fd_leak_checker = executable(
'exec-fd-leak-checker',
'exec-fd-leak-checker.c',
dependencies: test_runner_dep
@ -96,78 +96,116 @@ if get_option('scanner')
endif
tests = {
'array-test': [],
'client-test': [ wayland_server_protocol_h ],
'display-test': [
wayland_client_protocol_h,
wayland_server_protocol_h,
tests_server_protocol_h,
tests_client_protocol_c,
tests_protocol_c,
],
'connection-test': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
'event-loop-test': [ wayland_server_protocol_h ],
'fixed-test': [],
'interface-test': [ wayland_client_protocol_h ],
'list-test': [],
'map-test': [],
'sanity-test' : [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
'socket-test': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
'queue-test': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
'signal-test': [ wayland_server_protocol_h ],
'newsignal-test': [
# wayland-server.c is needed here to access wl_priv_* functions
files('../src/wayland-server.c'),
wayland_server_protocol_h,
],
'resources-test': [ wayland_server_protocol_h ],
'message-test': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
'compositor-introspection-test': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
'protocol-logger-test': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
'headers-test': [
wayland_client_protocol_h,
wayland_server_protocol_h,
'headers-protocol-test.c',
wayland_client_protocol_core_h,
wayland_server_protocol_core_h,
'headers-protocol-core-test.c',
],
'os-wrappers-test': [],
'proxy-test': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
'enum-validator-test': [],
'array-test': {},
'client-test': {
'extra_sources': [ wayland_server_protocol_h ],
},
'display-test': {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
tests_server_protocol_h,
tests_client_protocol_c,
tests_protocol_c,
],
},
'connection-test': {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
},
'event-loop-test': {
'extra_sources': [ wayland_server_protocol_h ],
},
'fixed-test': {},
'interface-test': {
'extra_sources': [ wayland_client_protocol_h ],
},
'list-test': {},
'map-test': {},
'sanity-test' : {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
'runtime_deps': [ exec_fd_leak_checker ],
},
'socket-test': {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
},
'queue-test': {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
},
'signal-test': {
'extra_sources': [ wayland_server_protocol_h ],
},
'newsignal-test': {
'extra_sources': [
# wayland-server.c is needed here to access wl_priv_* functions
files('../src/wayland-server.c'),
wayland_server_protocol_h,
],
},
'resources-test': {
'extra_sources': [ wayland_server_protocol_h ],
},
'message-test': {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
},
'compositor-introspection-test': {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
},
'protocol-logger-test': {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
},
'headers-test': {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
'headers-protocol-test.c',
wayland_client_protocol_core_h,
wayland_server_protocol_core_h,
'headers-protocol-core-test.c',
],
},
'os-wrappers-test': {
'runtime_deps': [ exec_fd_leak_checker ],
},
'proxy-test': {
'extra_sources': [
wayland_client_protocol_h,
wayland_server_protocol_h,
],
},
'enum-validator-test': {},
}
foreach test_name, test_extra_sources: tests
foreach test_name, test_extras : tests
test_extra_sources = test_extras.get('extra_sources', [])
test_runtime_deps = test_extras.get('runtime_deps', [])
test_sources = [ test_name + '.c' ] + test_extra_sources
test_deps = [test_runner_dep, epoll_dep]
bin = executable(test_name, test_sources, dependencies: test_deps)
test(
test_name,
bin,
depends: test_runtime_deps,
env: [
'TEST_SRC_DIR=@0@'.format(meson.current_source_dir()),
'TEST_BUILD_DIR=@0@'.format(meson.current_build_dir()),

View file

@ -40,6 +40,7 @@
#include "wayland-server.h"
#include "test-runner.h"
#include "test-compositor.h"
#include "../src/timespec-util.h"
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
@ -571,6 +572,68 @@ client_test_queue_names(void)
wl_display_disconnect(display);
}
static void
dispatch_timeout_sync_callback(void *data, struct wl_callback *callback,
uint32_t serial)
{
bool *done = data;
*done = true;
wl_callback_destroy(callback);
}
static const struct wl_callback_listener dispatch_timeout_sync_listener = {
dispatch_timeout_sync_callback
};
static void
client_test_queue_dispatch_simple(void)
{
struct wl_display *display;
struct timespec timeout;
struct wl_callback *callback;
bool done = false;
int ret = 0;
display = wl_display_connect(NULL);
assert(display);
callback = wl_display_sync(display);
assert(callback != NULL);
wl_callback_add_listener(callback, &dispatch_timeout_sync_listener, &done);
timespec_from_msec(&timeout, 1000);
while (!done) {
ret = wl_display_dispatch_timeout(display, &timeout);
assert(ret > 0);
}
wl_display_disconnect(display);
exit(0);
}
static void
client_test_queue_dispatch_timeout(void)
{
struct wl_display *display;
struct timespec timeout;
int ret = 0;
display = wl_display_connect(NULL);
assert(display);
timespec_from_msec(&timeout, 100);
ret = wl_display_dispatch_timeout(display, &timeout);
assert(ret == 0);
wl_display_disconnect(display);
exit(0);
}
static void
dummy_bind(struct wl_client *client,
void *data, uint32_t version, uint32_t id)
@ -709,3 +772,27 @@ TEST(queue_names)
display_destroy(d);
}
TEST(queue_dispatch_simple)
{
struct display *d = display_create();
test_set_timeout(2);
client_create_noarg(d, client_test_queue_dispatch_simple);
display_run(d);
display_destroy(d);
}
TEST(queue_dispatch_timeout)
{
struct display *d = display_create();
test_set_timeout(2);
client_create_noarg(d, client_test_queue_dispatch_timeout);
display_run(d);
display_destroy(d);
}

View file

@ -507,7 +507,7 @@ static const struct wl_registry_listener registry_listener =
NULL
};
struct client *client_connect()
struct client *client_connect(void)
{
struct wl_registry *reg;
struct client *c = calloc(1, sizeof *c);

View file

@ -118,5 +118,17 @@ struct client_info *client_create_with_name(struct display *d,
void *data,
const char *name);
#define client_create(d, c, data) client_create_with_name((d), (c), data, (#c))
static inline void noarg_cb(void *data)
{
void (*cb)(void) = data;
cb();
}
static inline struct client_info *client_create_with_name_noarg(struct display *d,
void (*client_main)(void),
const char *name)
{
return client_create_with_name(d, noarg_cb, client_main, name);
}
#define client_create_noarg(d, c) \
client_create_with_name((d), (void(*)(void *)) (c), NULL, (#c))
client_create_with_name_noarg((d), (c), (#c))