Compare commits

...

519 commits

Author SHA1 Message Date
llyyr
604fcdb1db render/vulkan: clip negative values before applying transfer function
Not all eotf or eotf inverse are well defined for values outside the
intended domain, so just ignore it and clamp it away.

An alternative solution would be to use sign preserving pow here (i.e.
sign(x) * pow(abs(x), p)), however I'm not sure that makes sense or is
defined anywhere. Negative values here are likely a result of colors
being outside the gamut range, so clipping them to 0 is more correct
than mirroring from 0.
2025-10-29 03:09:11 +05:30
David Turner
879243e370 xwm: Fix double-close
When an FD is passed to xcb_connect_to_fd(), xcb takes ownership of that
FD and is responsible for closing it, which it does when
xcb_disconnect() is called.  But the xwayland handler code also keeps a
copy of the FD and closes it via safe_close() in
server_finish_process().

This double-close can cause all sorts of problems if another part of
wlroots allocates another FD between the two closes - the latter close
will close the wrong FD and things go horribly wrong (in my case leading
to use-after-free and segfaults).

Fix this by setting wm_fd[0]=-1 after calling xwm_create(), and ensuring
that xwm_create() closes the FD if startup errors occur.
2025-10-20 14:02:29 +01:00
Simon Ser
989cffe70d scene: add software fallback for gamma LUT 2025-10-18 20:36:01 +02:00
Simon Ser
3e08e3be4a gamma_control_v1: introduce fallback_gamma_size 2025-10-18 20:36:01 +02:00
Simon Ser
91f4890ec2 gamma_control_v1: add wlr_gamma_control_v1_get_color_transform() 2025-10-18 20:36:01 +02:00
Simon Ser
74ce6c22a5 output: check for color transform no-op changes
This allows callers to always set this state and not care whether
the output currently has the same color transform applied.
2025-10-18 20:36:01 +02:00
Simon Ser
0b58bddf13 render/color: add wlr_color_transform_pipeline
Useful to apply multiple transforms in sequence, e.g. sRGB inverse
EOTF followed by gamma LUTs.
2025-10-18 20:36:01 +02:00
Simon Ser
3d36ab9211 render/color: add wlr_color_transform_eval()
Makes it so the Vulkan renderer can handle arbitrary color
transforms, and doesn't need to be updated each time a new one is
added.
2025-10-18 20:35:02 +02:00
Furkan Sahin
d786e07899 backend/session: use device boot_display
shouldn't need to check for `boot_vga` if newer, more general
sysfs `boot_display` is set.
closes https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4016
2025-10-17 17:35:51 +00:00
Simon Ser
6d63871f05 linux_drm_syncobj_v1: fix use-after-free in surface_commit_destroy()
surface_commit_destroy() accesses a field from
struct wlr_linux_drm_syncobj_surface_v1, however that struct may have
been free'd earlier:

    ==1103==ERROR: AddressSanitizer: heap-use-after-free on address 0x7cdef7a6e288 at pc 0x7feefaac335a bp 0x7ffc4de8f570 sp 0x7ffc4de8f560
    READ of size 8 at 0x7cdef7a6e288 thread T0
        #0 0x7feefaac3359 in surface_commit_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:195
        #1 0x7feefaac34cd in surface_commit_handle_surface_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:211
        #2 0x7feefbd194cf in wl_signal_emit_mutable (/usr/lib/libwayland-server.so.0+0x84cf) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1)
        #3 0x7feefaa52b22 in surface_handle_resource_destroy ../subprojects/wlroots/types/wlr_compositor.c:730
        #4 0x7feefbd1bb9f  (/usr/lib/libwayland-server.so.0+0xab9f) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1)
        #5 0x7feefaa46a18 in surface_handle_destroy ../subprojects/wlroots/types/wlr_compositor.c:65
        #6 0x7feef89afac5  (/usr/lib/libffi.so.8+0x7ac5) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90)
        #7 0x7feef89ac76a  (/usr/lib/libffi.so.8+0x476a) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90)
        #8 0x7feef89af06d in ffi_call (/usr/lib/libffi.so.8+0x706d) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90)
        #9 0x7feefbd17531  (/usr/lib/libwayland-server.so.0+0x6531) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1)
        #10 0x7feefbd1cd2f  (/usr/lib/libwayland-server.so.0+0xbd2f) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1)
        #11 0x7feefbd1b181 in wl_event_loop_dispatch (/usr/lib/libwayland-server.so.0+0xa181) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1)
        #12 0x7feefbd1d296 in wl_display_run (/usr/lib/libwayland-server.so.0+0xc296) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1)
        #13 0x555bf0a55a40 in server_run ../sway/server.c:615
        #14 0x555bf0a4a654 in main ../sway/main.c:376
        #15 0x7feef9227674  (/usr/lib/libc.so.6+0x27674) (BuildId: 4fe011c94a88e8aeb6f2201b9eb369f42b4a1e9e)
        #16 0x7feef9227728 in __libc_start_main (/usr/lib/libc.so.6+0x27728) (BuildId: 4fe011c94a88e8aeb6f2201b9eb369f42b4a1e9e)
        #17 0x555bf0a03f54 in _start (/home/leo/code/stuff/sway/build/sway/sway+0x390f54) (BuildId: e3d4e653af1aa0885f0426c403e16fc87c086d33)

    0x7cdef7a6e288 is located 8 bytes inside of 176-byte region [0x7cdef7a6e280,0x7cdef7a6e330)
    freed by thread T0 here:
        #0 0x7feefb71f79d in free /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:51
        #1 0x7feefaac29f1 in surface_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:84
        #2 0x7feefaac2e47 in surface_handle_resource_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:143
        #3 0x7feefbd1bb9f  (/usr/lib/libwayland-server.so.0+0xab9f) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1)
        #4 0x7feefaac2a12 in surface_handle_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:89
        #5 0x7feef89afac5  (/usr/lib/libffi.so.8+0x7ac5) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90)

    previously allocated by thread T0 here:
        #0 0x7feefb7205dd in calloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:74
        #1 0x7feefaac4abd in manager_handle_get_surface ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:313
        #2 0x7feef89afac5  (/usr/lib/libffi.so.8+0x7ac5) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90)

Fix this by storing the struct wlr_surface in the field.

Closes: https://github.com/swaywm/sway/issues/8917
2025-10-17 09:05:53 +00:00
tokyo4j
19c5d22beb util/box.c: use 1/256 instead of 1/65536 in wlr_box_closest_point()
This fixes the issue that a scrollbar in a maximized GTK/Chromium window
cannot be dragged when cursor is on the right/bottom edge of the output.

The issue was caused by rounding in `wl_fixed_from_double()` ([1]); if
`wlr_cursor_move()` constrains the x-position of the cursor to
`(output width)-1/65536`, `wl_fixed_from_double()` converts it to just
`(output width)`, which is perceived as outside of the window by
GTK/Chromium.

Using 1/256 (minimal unit of `wl_fixed_t`) instead of 1/65536 avoids
this rounding issue.

[1]: f246e619d1
2025-10-16 13:46:27 +00:00
Furkan Sahin
06275103f2 input-method-v2: Destroy keyboard grab before input method
Fixes race condition in where the keyboard grab tries to reference the
input manager after it's been set to null.
2025-10-16 12:07:47 +00:00
Simon Ser
03e7966650 ci: fix VKMS lookup after faux bus migration
VKMS has been migrated to the new faux bus. This causes breakage
in CI, because we used the platform bus to find the right device.

udev hasn't been updated yet to support the faux bus, so just use
sysfs instead.
2025-10-16 11:04:25 +02:00
llyyr
5529aae3e6 wlr_scene: fix direct scanout for gamma2.2 buffers
Fixes incorrectly rejecting scanout for gamma2.2 buffers when the output
has no image description set. This happens on `hdr off` mode on sway.

Also refactor the scanout check into its own function while at it to
make it easier to follow.
2025-10-05 23:53:25 +05:30
llyyr
6e1c8748ff render: introduce bt.1886 transfer function 2025-10-04 18:13:37 +05:30
Félix Poisot
d8fb7adcf0 scene, render: use Gamma 2.2 TF as default 2025-10-03 19:48:12 +00:00
Félix Poisot
c2d9ae2142 render: introduce Gamma 2.2 color transform 2025-10-03 19:39:17 +00:00
Simon Ser
6978509f64 Revert "wlr_scene: fix tf/prim comparison for scanout attempt"
This reverts commit dde07b6840.

This is incorrect as discussed here:
https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5163#note_3118744
2025-10-03 20:42:21 +02:00
llyyr
2252854297 wlr_scene: return scene_direct_scanout_result instead of bool 2025-10-02 13:51:41 +05:30
llyyr
dde07b6840 wlr_scene: fix tf/prim comparison for scanout attempt
We were incorrectly doing comparison with `!= 0` to detect non-sRGB
tf/primaries. Since these enums are bit flags, the default sRGB values
are 1, not 0, so sRGB buffers were incorrectly rejected.

Fixes: bf40f396bf ("scene: grab image description from output state")
2025-10-02 11:57:52 +05:30
Simon Ser
406aa5f7f5 backend/session: fix crash on udev device remove event
libwayland adds phantom listeners here:
d81525a235/src/wayland-server.c (L2378)

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3982
2025-10-01 17:05:10 +02:00
Simon Ser
2ec4012559 backend/drm: avoid error message when EDID is missing
We'd attempt to parse an EDID even when the connector has no EDID,
printing "Failed to parse EDID" in logs. Instead, don't attempt to
parse the EDID and print a more appropriate log message.
2025-10-01 15:14:46 +02:00
Simon Ser
d039ad8da3 backend/wayland: continue reading on hangup
If we stop immediately, we won't see any wl_display.error events.
Make sure we've read everything before handling hangup.
2025-09-30 09:25:14 -04:00
Simon Ser
3f0d338643 backend/wayland: log when getting disconnected from remote display
It can be a bit confusing to understand why a compositor is shutting
down on its own. Log a message when we get disconnected from the
parent compositor to explain the cause.
2025-09-30 09:25:14 -04:00
Simon Ser
60d72724cd render/color: fix bounds check in lut_1d_get()
i == len is out-of-bounds.

Fixes: 74217a4d93 ("render/color: introduce COLOR_TRANSFORM_LUT_3X1D")
2025-09-30 09:34:40 +02:00
Simon Ser
7cb3393e75 scene: send color_management_v1 surface feedback
Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3999
2025-09-23 09:22:00 -04:00
Simon Ser
26c1476827 color_management_v1: add destroy event to manager 2025-09-23 09:22:00 -04:00
Simon Ser
138210f01c color_management_v1: make from_wlr enum converters public
This can be useful for compositors to set surface feedback.
2025-09-23 09:22:00 -04:00
Simon Ser
845a7a581d color_management_v1: drop duplicated enum converters 2025-09-23 09:22:00 -04:00
Simon Ser
108d94f798 Add release script
This is useful to speed up the release process and avoid making
mistakes.
2025-09-22 10:12:43 -04:00
xurui
aaf82ee332 wlr_drag: drag motion signal also needs to be sent
Signed-off-by: xurui <xurui@kylinos.cn>
2025-09-22 10:04:30 -04:00
Simon Ser
d1c88e9497 render/vulkan: add linear single-subpass
When the TF is set to EXT_LINEAR, we can write out color values
straight up to a non-SRGB image view.
2025-09-20 10:50:22 +00:00
Simon Ser
3e88a79e6f render/vulkan: replace wlr_vk_render_pass.srgb_pathway with two_pass
The important bit here is whether this is using a single or two
sub-passes. The flag isn't used for anything else.

Preparation for an upcoming one-subpass codepath.
2025-09-20 10:50:22 +00:00
Simon Ser
b2d09cdee9 render/vulkan: add wlr_vk_render_pass.render_buffer_out
Simplifies the logic and prepares for a new render setup.
2025-09-20 10:50:22 +00:00
Simon Ser
35eba5f2fe render/vulkan: add wlr_vk_render_pass.render_setup
Simplifies the logic and prepares for a new render setup.
2025-09-20 10:50:22 +00:00
Simon Ser
a91f96b391 render/vulkan: introduce wlr_vk_render_buffer_out
Holds common state for final output buffer targets.
2025-09-20 10:50:22 +00:00
Simon Ser
6fee3623e4 render/vulkan: rename vulkan_setup_srgb_framebuffer() for linear
Rename to "one-pass" (to indicate no blending buffer is involved),
because this will get re-used when introducing a linear
single-subpass codepath.
2025-09-20 10:50:22 +00:00
Simon Ser
7f6d66ea62 render/vulkan: use sRGB image view when color transform is set
If the color transform is set to sRGB inverse EOTF, we can use the
sRGB image view just like when no color transform is passed in.
2025-09-20 10:50:22 +00:00
Simon Ser
54374b6fe6 render/vulkan: rename plain to two_pass
We will introduce a new subpass without any post-processing step.
Rename "plain" so that there's no confusion.
2025-09-20 10:50:22 +00:00
Kenny Levinsen
dd7f543189 render/vulkan: Handle multi-descriptor sets
A combined image sampler may need several descriptors in a descriptor
set. We are not currently checking how many descriptors are required,
nor is it presumably guaranteed that such multi-descriptor allocation
will not fail due to fragmentation.

If the pool free counter is not zero, try to allocate but continue with
the next pool and fall back to creating a new pool if the allocation
failed.

Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4010
2025-09-20 07:59:02 +00:00
JiDe Zhang
d7ae9a866b xwayland: fix assertion failure in wlr_xwayland_shell_v1
The issue occurred when `wlr_xwayland_shell_v1` was destroyed before
`wlr_xwayland`. This happened because `wlr_xwayland` didn't remove the
listener for the shell's destroy event in `handle_shell_destroy`.
2025-09-16 13:59:06 +08:00
Simon Ser
462046ffdc cursor: use source buffer to signal release timeline point
Same as 128cd07e91 ("scene/surface: use source buffer to signal
release timeline point"), but for the cursor.
2025-09-11 12:33:04 +00:00
Simon Ser
bd566225ea scene/surface: fix NULL deref when source buffer is destroyed
Fixes the following crash, witnessed after a GPU reset:

    #0  0x00007fba9a32774c n/a (libc.so.6 + 0x9774c)
    #1  0x00007fba9a2cddc0 raise (libc.so.6 + 0x3ddc0)
    #2  0x00007fba9a2b557a abort (libc.so.6 + 0x2557a)
    #3  0x00007fba9a2b54e3 n/a (libc.so.6 + 0x254e3)
    #4  0x00007fba9a53fb78 wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer (libwlroots-0.20.so + 0x26b78)
    #5  0x00007fba9a590846 surface_reconfigure (libwlroots-0.20.so + 0x77846)
    #6  0x00007fba9a590cbb scene_surface_set_clip (libwlroots-0.20.so + 0x77cbb)
    #7  0x00007fba9a590efa subsurface_tree_set_clip (libwlroots-0.20.so + 0x77efa)
    #8  0x00007fba9a590f1f subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f1f)
    #9  0x00007fba9a590f1f subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f1f)
    #10 0x00007fba9a590f8d wlr_scene_subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f8d)

Reported-by: Hubert Hirtz <hubert@hirtz.pm>
2025-09-11 12:33:04 +00:00
Simon Ser
b62c6878e1 scene/surface: simplify single-pixel-buffer check in surface_reconfigure()
No need to call wlr_client_buffer_get() on wlr_client_buffer.base:
we're already manipulating a wlr_client_buffer.
2025-09-11 12:33:04 +00:00
Simon Ser
fd069ad4f2 output/cursor: fix missing second cursor
When attaching more than one cursor to wlr_output, the first one
will pick the output's hardware cursor, then for the second one
output_set_hardware_cursor() would fail (since the hardware cursor
was already taken), but we still ended up resetting the current
hardware cursor (by calling output_disable_hardware_cursor() below).
As a result only the second cursor would be displayed.

To fix this, move the current hardware cursor check to the caller.

Fixes: 510664e79b ("output: disable hardware cursor when falling back to software")
2025-09-11 12:21:52 +00:00
liupeng
5e5842cb1a drm_lease_v1: initialize device resource link during abnormal exit
Signed-off-by: liupeng <liupeng01@kylinos.cn>
2025-09-11 11:05:39 +08:00
rewine
cdd2c7e006 protocols: sync with wlr-protocols, apply non-breaking updates and doc improvements
This sync includes minor non-breaking updates from recent years:

- Fix typos and grammatical issues (e.g. "a an" → "an", "inexistent" → "nonexistent")
- Improve description consistency with `wl_output` (e.g. name, description, make, model, serial)
- Add `destructor` annotation to relevant events (e.g. `finished` in foreign-toplevel)
- Clarify event emission timing and behavior for output management
- No functional or semantic protocol changes introduced

These changes improve the accuracy and consistency of protocol descriptions without impacting compatibility.
2025-09-10 09:50:09 -04:00
David Turner
905465b0fa color-representation-v1: Actually set supported_*_len 2025-09-09 15:19:30 +01:00
tokyo4j
102a6bd415 input-method: use NULL when emitting signals 2025-09-08 10:56:57 -04:00
tokyo4j
06aacb2a6f input-method: rename input_method event to new_input_method 2025-09-08 10:56:57 -04:00
Simon Zeni
0166fd9eb7 drm-lease-v1: remove connector active_lease & lease connectors
Upon leasing, the wlr_drm_lease_connector_v1 will be automatically clean up by the wlr_output
destroy handler. There is no need for the wlr_drm_lease_manager to keep track of leased connectors.
2025-08-28 16:33:35 +00:00
Simon Zeni
423afc3fc9 types: deprecate wlr-screencopy-unstable-v1 2025-08-28 16:23:56 +00:00
Simon Ser
122310a2de build: add wayland-protocols to dependencies array
We grab header files from there, ensure include directories are
properly set up when building wlroots.

Fixes missing header files when a wayland-protocols subproject is
used.
2025-08-27 16:11:49 -04:00
rewine
b799ffc6ae docs: deprecate legacy wlr_data_control_v1 interface
Add deprecation notice for wlr_data_control_v1, indicating that
it's superseded by ext-data-control-v1.

Related: https://gitlab.freedesktop.org/wlroots/wlr-protocols/-/merge_requests/136
2025-08-27 14:53:28 -04:00
Simon Ser
e95117b700 render/vulkan: remove hardcoded counts
Use the array size instead.
2025-08-27 14:26:21 -04:00
Simon Ser
1a18e47efa render/vulkan: fix VkPushConstantRange for wlr_vk_frag_texture_pcr_data
We pass an alpha multiplier plus a luminance multiplier now.

Fixes the following validation layer error:

    vkCmdPushConstants(): is called with
    stageFlags (VK_SHADER_STAGE_FRAGMENT_BIT), offset (80), size (72)
    but the VkPipelineLayout 0x510000000051 doesn't have a VkPushConstantRange with VK_SHADER_STAGE_FRAGMENT_BIT.
    The Vulkan spec states: For each byte in the range specified by offset and size and for each shader stage in stageFlags, there must be a push constant range in layout that includes that byte and that stage (https://docs.vulkan.org/spec/latest/chapters/descriptorsets.html#VUID-vkCmdPushConstants-offset-01795) (VUID-vkCmdPushConstants-offset-01795)

Fixes: 56d95c2ecb ("render/vulkan: introduce wlr_vk_frag_texture_pcr_data")
2025-08-27 14:26:21 -04:00
Simon Ser
bbd9a49bdf tinywl: stop generating xdg-shell header
We don't need to do this anymore for wayland-protocols.
2025-08-27 14:21:23 -04:00
xurui
7bf5ff4c02 wlr_xdg_toplevel_icon_v1: check the correct resource
Signed-off-by: xurui <xurui@kylinos.cn>
2025-08-27 20:01:50 +08:00
xurui
b0c886ec77 render/allocator/gbm: insert buffer after export gbm bo
Signed-off-by: xurui <xurui@kylinos.cn>
2025-08-25 14:05:13 +08:00
Simon Ser
7431d840d0 color-management-v1: handle inert outputs in get_output
wlr_output_from_resource() can return NULL if the outputs no longer
exists on the compositor side.

Closes: https://github.com/swaywm/sway/issues/8847
2025-08-18 14:09:13 +02:00
Kirill Primak
bb1f8673b3 compositor: use wl_resource_post_error_vargs() 2025-08-13 20:59:13 +00:00
Kirill Primak
ad1b2f2819 Avoid including generated headers publicly where possible
This is possible now that w-p ships enum headers. The remaining includes
are from wlr-protocols.
2025-08-13 22:46:37 +02:00
Kirill Primak
812675ba34 fixes: add implementation 2025-08-13 20:41:21 +00:00
Simon Ser
7392b3313a backend, output: send commit events after applying all in wlr_backend_commit()
We were iterating over involved outputs, applying the new state and
sending the commit event for each one. This resulted in commit
events being fired while we weren't done applying the new state for
all outputs.

Fix this by first applying all of the states, then firing all of
the events.

Closes: https://github.com/swaywm/sway/issues/8829
2025-08-12 19:04:05 +02:00
Jesper Jensen
07e92fb868 output/cursor: Fix double cursor bug
When we fail to render the cursor (in my case because the cursor is too
large) we bail out of the output_cursor_attempt_hardware function. This
causes output_cursor_set_texture to clean up after us, but we've already
cleared the hardware_cursor, and so output_disable_hardware_cursor
thinks we don't have a hardware cursor to disable.

We shouldn't modify the hardware_cursor variable before we've
successfully changed the hardware cursor, this way the caller can clean
up after us like it expect to.

This was brought up by an actual bug when playing the game Kaizen. Which
uses oddly sized cursors, that fell back to software cursors for me, and
left the hardware cursor hanging around. This change has been tested to
fix that.

During the testing of this change, I have noticed that the previous code
worked fine the first time the cursor was switch to software. It only
failed on subsequent attempts. I haven't figured out why that is.
2025-08-07 22:25:39 +02:00
Simon Ser
12316417b0 ext_image_capture_source_v1: advertise fallback {A,X}RGB8888 formats
We can't expect all clients to support all fancy formats.
WebRTC's reference implementation doesn't support 10-bit formats
yet. More generally, clients are limited by the libraries they
use: for instance, Pixman doesn't implement all OpenGL/Vulkan
formats.

Another MR [1] suggests advertising all render formats. This is a
bit heavy-handed because:

- Upgrading a 8-bit buffer to a 10-bit buffer doesn't make a lot
  of sense. I don't think the compositor should expose arbitrary
  pixel format conversions.
- The protocol has no preference order. Clients generally pick the
  first format they receive and support.

As an alternative, only advertise two fallback formats, ARGB8888
and XRGB8888. These two are already hard-required by wl_shm and
all clients should be able to handle them.

[1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5019
2025-07-31 14:13:24 +00:00
Christopher Snowhill
dd3c63f5e6
color-representation-v1: Fix missing destroy signal init
Fixes #4001

Reported-by: CreeperFace / @dy-tea
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-07-31 06:46:47 -07:00
rewine
c8b7600adc wlr_ext_data_control_v1: Make all listeners private
For more context, see: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4873
2025-07-30 12:37:19 +00:00
Christopher Snowhill
51a78cb0ed
color_management_v1: set output color properties
This reports the output properties according to the current image description.
Firefox needs this to report HDR support to documents, at least.

v2: Move abort() calls out of switch to eliminate default case. Rename
    functions so they don't use a wlr_ prefix like public functions do.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-07-29 02:03:18 -07:00
1beb25a1c8 tinywl: fix cursor disappears when focused window is closed
Add listener for wlr_seat->pointer_state.events.focus_change

Fix #3802
2025-07-25 12:56:05 +00:00
Simon Ser
2f2c0dfcc6 scene: use helpers to convert TF/primaries enums 2025-07-24 15:02:39 +00:00
Simon Ser
47a90d6f1a color_management_v1: add helpers to convert TF/primaries enums
This makes it easier for protocol implementers to tie everything
together with wlroots backends and renderers.
2025-07-24 15:02:39 +00:00
llyyr
db5e9ca04c meson: bump minimum wayland-protocols version
color-representation was added in 1.44.

Fixes: eff620770c
2025-07-24 14:51:37 +00:00
rewine
efb17980a8 ext_image_capture_source_v1: remove unused struct definition
Remove the redundant wlr_ext_foreign_toplevel_image_capture_source_v1
struct that was not used anywhere in the codebase.
2025-07-24 14:49:27 +00:00
liupeng
be5e266211 cursor: update output cursor even if output is disabled
During suspend, we first disable output and then remove the input device.
This causes cursor->state->surface released while cursor->texture leaves.
Which leads to use-after-free after resume.
2025-07-24 14:47:32 +00:00
Andri Yngvason
80c7e0f772 ext-image-capture-source: output: Apply transform to cursor
The cursor can be expected to also be transformed if the output is
transformed.
2025-07-23 15:06:24 +00:00
Yixue Wang
ccec4116b3 types/color_management: check on invalid image description
Check if image description is valid. If not, post error to client.
2025-07-20 23:01:58 +08:00
Yixue Wang
d2007d7dc1 types/color_representation: correctly cleanup in manager create
Global should be created after all other initialization finished.
Free manager in err_options.
2025-07-18 18:12:40 +08:00
David Turner
a4eb2cff46 color-representation-v1: Add wlr enums + converters
Add wlr-internal enums for the properties specified in
color-representation-v1 (encoding, range, chroma siting, alpha mode) so
that other parts of wlroots can use these without depending on the
protocol specifics and without needing to include the protocol headers.
Also add conversion functions to convert the protocol enum values into
the wlroots enum values.
2025-07-17 16:41:14 +01:00
David Turner
eff620770c color-representation-v1: new protocol
Add a minimal implementation of the color-representation-v1 protocol
(not including anything to actually use the new properties in
rendering/scanout)
2025-07-17 16:40:45 +01:00
David Turner
f5dc6416f0 util/mem: Move memdup to new util/mem.c file
This seems handy and I want to use this in wlr_color_representation.
2025-07-17 16:39:58 +01:00
YaoBing Xiao
c14aa1d0b8 render/vulkan: destroy vulkan instance when drm phdev mismatch 2025-07-15 08:59:37 +08:00
Consolatis
c39b3ce7a3 transient_seat: initialize seat destroy listener
This fixes a `wl_list_remove()` from an uninitialized listener
when using `wlr_transient_seat_v1_deny()` in a `create_seat`
handler.
2025-07-10 19:52:45 +02:00
Simon Ser
f4327f52cf xdg-toplevel-tag-v1: new protocol
References: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/238
2025-07-07 09:10:37 +00:00
Consolatis
6aa654b728 wlr_text_input_v3: remove event arguments from header
Fixes 2d5492c737
2025-07-06 14:10:25 +02:00
Félix Poisot
31b78a4f3a scene: fix output transfer functions
fixes: bf40f396b
2025-07-03 12:29:27 +00:00
David Turner
58c3680d96 scene: Block damage on single-pixel buffer textures
We cache whether buffers are single-pixel buffers (and if so what color
they are) to allow rendering optimizations.  But this breaks if the
client changes out the single-pixel buffer for one with a different
color, because this updates the texture in-place instead of actually
changing the buffer.

We can fix this by blocking in-place texture updates for single pixel
buffers.

Original bug: https://codeberg.org/ifreund/waylock/issues/121
See also: !5092
2025-07-01 11:38:56 +01:00
Simon Ser
48bd1831fe render/egl: fix software rendering check
Commit b4ce0d8b39 ("render/egl: accept negative DRM FD to select
software rendering") added an EXT_device_drm check to figure out
whether the user selected a device with a DRM FD or without one.
However, for KMS-only devices, Mesa will never advertise the
selected KMS node:
3f1d40d230/src/egl/main/egldevice.c (L109)

Instead, pass down a parameter to indicate whether a DRM FD was
passed in.

Fixes: b4ce0d8b39 ("render/egl: accept negative DRM FD to select software rendering")
2025-06-29 10:57:56 +02:00
Simon Ser
8c7041c4e8 backend/drm: relay full HDR metadata 2025-06-26 11:02:26 +00:00
Simon Ser
aecb867098 output: add full HDR metadata to wlr_output_image_description
This allows sinks to improve their tone mapping.
2025-06-26 11:02:26 +00:00
Simon Ser
bf40f396bf scene: grab image description from output state
Alternative to https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5089
2025-06-26 09:56:12 +00:00
Simon Ser
2498036e67 output: add output_pending_image_description() 2025-06-26 09:56:12 +00:00
Simon Ser
e76f8ac2b3 output: add wlr_output.image_description
Stores the current image description.
2025-06-26 09:56:12 +00:00
tokyo4j
6d8bb66f98 xwm: add support for _NET_WM_ICON 2025-06-26 08:50:02 +00:00
Isaac Freund
f5e7caf599 util/box: set dest to empty if boxes don't intersect
Currently if both box_a and box_b are non-empty but do not intersect,
this function does not set dest to an empty box. This contradicts the
doc comments and is surprising for users.
2025-06-21 09:54:24 +00:00
Simon Ser
98af337175 output: shorten output enabled checks
Use a more concise loop instead of repeated logic.

No behavior change.
2025-06-20 19:02:54 +02:00
Simon Ser
0c272a3842 scene: add support for color-management-v1 primaries 2025-06-18 19:37:55 +00:00
Simon Ser
071773cb27 scene: add primaries support to wlr_scene_buffer 2025-06-18 19:37:55 +00:00
Simon Ser
ae85c31176 render/vulkan: add luminance multiplier for texture shader 2025-06-18 19:37:55 +00:00
Simon Ser
fa1feb447f render, render/vulkan: add primaries to wlr_render_texture_options 2025-06-18 19:37:55 +00:00
Simon Ser
a8144088df render/vulkan: add support for PQ for textures 2025-06-18 19:37:55 +00:00
Simon Ser
3a51a5c623 render/vulkan: add texture color transformation matrix 2025-06-18 19:37:55 +00:00
Simon Ser
56d95c2ecb render/vulkan: introduce wlr_vk_frag_texture_pcr_data
Contains UBOs for texture.frag.
2025-06-18 19:37:55 +00:00
Simon Ser
ec422ac389 render/vulkan: prepare texture shader for new transforms 2025-06-18 19:37:55 +00:00
Simon Ser
7a1161438c scene: add support for color-management-v1 transfer functions 2025-06-18 19:37:55 +00:00
Simon Ser
4efec11721 scene: add transfer function support for wlr_scene_buffer 2025-06-18 19:37:55 +00:00
Simon Ser
8d1c6e42ac render/vulkan: add support for texture transfer functions 2025-06-18 19:37:55 +00:00
Simon Ser
b1a9dab03e render/vulkan: fix typo in wlr_vk_texture.views comment 2025-06-18 19:37:55 +00:00
Simon Ser
dd3d9be41e render/pass: add wlr_render_texture_options.transfer_function
Also add a bit in wlr_renderer.features to indicate support.
2025-06-18 19:37:55 +00:00
Simon Ser
c8d94000a6 color-management-v1: add EXT_LINEAR 2025-06-18 19:37:55 +00:00
Simon Ser
0ee0452af0 render/color, render/vulkan: add EXT_LINEAR to enum wlr_color_transfer_function 2025-06-18 19:37:55 +00:00
Simon Ser
f5a0992686 render/vulkan: fix multiplication order for output color matrix
This had the same bug as the texture side, but I forgot to fix it.

See:
https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4988#note_2867416

Fixes: f3524de980 ("render, render/vulkan: add primaries to wlr_buffer_pass_options")
2025-06-18 21:25:37 +02:00
Simon Ser
7b6eec530c render/vulkan: add luminance multipler for output shader 2025-06-18 00:11:33 +02:00
Simon Ser
b482e9089b backend/drm: add support for image description transfer function 2025-06-18 00:11:33 +02:00
Simon Ser
dc258b2237 output: add transfer function to image description 2025-06-18 00:11:33 +02:00
Simon Ser
4470683591 render/color, render/vulkan: add support for PQ transfer function 2025-06-18 00:11:31 +02:00
Simon Ser
8430a1922d render/vulkan: add PQ inverse EOTF to output shader 2025-06-18 00:09:25 +02:00
Simon Ser
f024d1b8c8 backend/drm: add support for color primaries 2025-06-18 00:07:58 +02:00
Simon Ser
e64de4d55f output: add color primaries to output state 2025-06-18 00:07:58 +02:00
Simon Ser
f3524de980 render, render/vulkan: add primaries to wlr_buffer_pass_options 2025-06-18 00:07:35 +02:00
Simon Ser
a5706e2fb9 render/vulkan: use array declaration in encode_proj_matrix()
This makes it more obvious what the final layout of the matrix will
be.
2025-06-17 19:41:29 +02:00
Simon Ser
1df2274f6c render/vulkan: rename mat3_to_mat4() to encode_proj_matrix()
This function is specific to projection matrices.
2025-06-17 19:35:16 +02:00
Simon Ser
30c6efedf1 render/vulkan: use output_pipe_srgb for non-NULL sRGB color transform 2025-06-17 18:50:25 +02:00
Simon Ser
2ea0e386c4 render/vulkan: add color transformation matrix 2025-06-17 18:47:50 +02:00
Simon Ser
a30c102163 output: drop gamma LUT from state
This has been superseded by color transforms.
2025-06-16 09:06:10 +00:00
Simon Ser
bfcb4211f6 wlr_gamma_control_v1: use color transforms 2025-06-16 09:06:10 +00:00
Simon Ser
f10dd1da1c backend/drm: add support for color transforms 2025-06-16 09:06:10 +00:00
Simon Ser
97f6946c8d output: add color transform to state
Color transforms are better suited than raw gamma tables, because:

- They don't need to get copied around: they are ref'counted.
- They can represent more color operations (will be useful for the
  upcoming KMS color pipeline API, and for the Wayland color
  management protocol).
2025-06-16 09:06:10 +00:00
Simon Ser
74217a4d93 render/color: introduce COLOR_TRANSFORM_LUT_3X1D
This will be useful to apply LUTs applied via wlr_gamma_control_v1,
and to add wlr_color_transform support to wlr_output.
2025-06-16 09:06:10 +00:00
Simon Ser
3665b53e29 render/color: replace COLOR_TRANSFORM_LUT_3D with COLOR_TRANSFORM_LCMS2
Converting the LCMS2 transform to a 3D LUT early causes issues:

- It's a lossy process, the consumer will not be able to pick a
  3D LUT size on their own.
- It requires unnecessary conversions and allocations: an intermediate
  3D LUT is allocated, but the renderer already allocates one.
- It makes it harder to support arbitrary color transforms in the
  renderer, because each type needs to be handled differently.

Instead, expose a function to evaluate a color transform, and use
that to build the 3D LUT in the renderer.
2025-06-16 09:06:10 +00:00
Simon Ser
9b97e2607d render/color: use variable instead of type in sizeof()
Conforms to the wlroots code style.
2025-06-16 09:06:10 +00:00
Simon Ser
d421538b4a render/color: add wlr_color_transform_init() 2025-06-16 09:06:10 +00:00
Simon Ser
c6133f9912 scene: send surface preferred transform alongside DMA-BUF feedback
wl_surface.preferred_buffer_transform is mainly useful to make
direct scan-out more likely. It shouldn't make a difference with
GL/Vulkan rendering.
2025-06-16 07:54:00 +00:00
Simon Ser
6204fc3278 scene: use output with highest refresh rate for frame pacing
If a surface is mirrored on two outputs, we don't want to pick the
first output if the second has a higher refresh rate.

Also fixes duplicate frame/feedback events when a surface is added
to multiple scenes.
2025-06-16 07:54:00 +00:00
Simon Ser
51d051497d scene: filter frame_done primary output in surface handler
This lets the surface handler decide which output to send frame
callbacks from. The output_sample event already works this way.

Introduce wlr_scene_surface_send_frame_done() as a replacement for
wlr_scene_buffer_send_frame_done() when a compositor doesn't have
an output at hand.
2025-06-16 07:54:00 +00:00
Simon Ser
8713ac72fb scene: configure clients with the highest output scale
If a surface appears on two outputs with the same intersection
area, or even if a surface appears on an output with a small
intersection area, we want to use the highest scale.

Fixes flip-flop when a surface is added to multiple scenes.

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3901
2025-06-16 07:54:00 +00:00
Simon Ser
95b2771bfd scene: ignore outputs with too small intersection with nodes
If a node has a very small intersection with an output, there's no
point in trying to adapt the node's rendering to that output.
2025-06-16 07:54:00 +00:00
Simon Ser
da820070f4 ext_image_capture_source_v1: add helper to capture scene nodes 2025-06-16 07:54:00 +00:00
Simon Ser
b066fd6b5a ext_image_capture_source_v1: add support for foreign toplevels 2025-06-16 07:54:00 +00:00
Simon Ser
8fb4e4dabb swapchain: assert that size is not empty at creation time
Failing later (at buffer allocation time) makes it more difficult
to track down where the issue comes from.
2025-06-16 07:54:00 +00:00
Simon Ser
bb50c7a5a4 render/allocator/gbm: require GBM 21.1
Mesa 21.1 was released back in 2021. Let's require it so that we
can simplify our build and remove the workaround.
2025-06-16 07:43:01 +00:00
Simon Ser
221b37355f xwayland: require xcb-xfixes 1.15
xcb-xfixes 1.15 was released back in 2022. Let's simplify our build
setup by requiring it.
2025-06-16 07:40:04 +00:00
Simon Ser
37992cf3b8 idle_notify_v1: drop trailing spaces 2025-06-15 12:25:36 +00:00
Rémi Bernon
6c78225160 xwayland: Activate no_focus_window when a Wayland window is activated
None active window might be interpreted from an X point of view as a
transient focus state, and is used by multiple X window managers when
a temporary focus change is in progress, or simply when grabbing the
keyboard.

From Wine side, we translate any active window change to the Win32
application, and handling None active window as an actual window
deactivation and focus loss creates spurious events and an undesired
feedback loop, as apps might react to it.

We still want to be able to detect actual focus loss under an XWayland
session, and having XWayland window manager focus an actual X window
instead will make the distinction possible.
2025-06-12 08:56:09 +00:00
Rémi Bernon
83a5bdf5d5 xwayland: Create a dummy no_focus_window to use for non-X window focus 2025-06-12 08:56:09 +00:00
Kirill Primak
afe427d149 xdg-shell: add support for v7 2025-06-05 16:25:25 +00:00
DreamMaoMao
a08acfcee0 render/pass: Ensure the precision is consistent during comparison 2025-05-29 21:50:21 +08:00
Kirill Primak
af43d3b9e7 cursor-shape-v1: bump to version 2 2025-05-22 20:35:30 +03:00
Kirill Primak
aaeffe9769 cursor-shape-v1: use generated enum validator 2025-05-22 20:35:15 +03:00
David Turner
fae4c5097d xwayland: Remove has_utf8_title field 2025-05-22 15:10:26 +00:00
tokyo4j
170f7e0706 backend/libinput: don't leak udev_device 2025-05-20 01:15:19 +09:00
Guido Günther
2d5492c737 text-input-v3: Use NULL when emitting signals
Listeners can use `wl_container_of`.

Helps: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3851

Signed-off-by: Guido Günther <agx@sigxcpu.org>
2025-05-15 13:36:58 +00:00
Guido Günther
536100488f text-input-v3: Name new text input event correctly
Helps: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3851

Signed-off-by: Guido Günther <agx@sigxcpu.org>
2025-05-15 13:36:58 +00:00
Martin Rys
62c86fb975 Add support for XKB_LED_NAME_COMPOSE and XKB_LED_NAME_KANA USB HID LEDs
Requires xkbcommon 1.8.0
2025-05-15 09:14:48 +00:00
Simon Ser
c2327248f8 output: don't send make/model
- These are legacy wl_output properties [1]
- wl_output exposes name and description, which are better defined
- It's not clear what make/model/serial are for a virtual output
- Clients shouldn't rely on these fields

[1]: 8f1795f911/protocol/wayland.xml (L2508)

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/1623
2025-05-15 09:04:33 +00:00
David Turner
c9f0dbc159 Change all timespec pointers in events to owned
Follow-up from !4803.  Make things consistent by making all `struct
timespec`s in events owned.  Reduces the need for thinking about
ownership/lifetimes.
2025-05-15 08:59:56 +00:00
Simon Ser
f04ef79f61 build: bump version to 0.20.0-dev 2025-05-15 10:54:23 +02:00
Simon Ser
13a62a23a2 build: bump version to 0.19.0 2025-05-15 10:47:57 +02:00
Simon Ser
af34aaad53 xwayland: handle unset _NET_WM_NAME
The spec says [1]:

> If set, the Window Manager should use this in preference to WM_NAME.

However we overwrite WM_NAME with NULL when _NET_WM_NAME is unset.
Fix this by storing both WM_NAME and _NET_WM_NAME, so that we
handle properly all combinations of events (e.g. a client setting
both and later clearing one).

[1]: https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html#id-1.6.2
2025-05-15 10:23:01 +02:00
David Turner
2420bfef0b backend/drm: Fix segfault in libliftoff startup
With labwc and WLR_DRM_FORCE_LIBLIFTOFF=1, a segfault is seen on startup
because we call output_state_get_buffer_src_box() when there is no
buffer set in the output state.  Fix this by getting the src/dst box
from state->primary_viewport instead.
2025-05-08 10:13:57 +00:00
YaoBing Xiao
70add22e74 render/pixman: null check on newly created image 2025-05-07 21:33:22 +08:00
YaoBing Xiao
f36f856cdb render/drm_syncobj: fix return type mismatch 2025-05-06 17:48:54 +08:00
Kenny Levinsen
aef4de2ced wlr-foreign-toplevel-management-v1: Simply leave and destroy code
We send the output_leave event and destroy a toplevel_output both if our
output_leave listener is called, or if the underlying wlr_output is
destroyed.

We somewhat clumsily reused the output_leave listener, which meant that
even though we had the toplevel output, we went out of our way to let
the output_leave handler find said toplevel_output again.

Simplify both pathways by adding a toplevel_output_leave function.
Should have no functional changes.
2025-05-06 00:45:09 +02:00
Simon Ser
e57dd9c5ef render/vulkan: check binary semaphore in vulkan_sync_render_buffer()
Ensure the binary semaphore is available when we need it.
2025-05-05 22:17:11 +02:00
Simon Ser
d4e4c9f64b render/vulkan: create binary semaphore when signal timeline is supplied
We were only creating the binary semaphore when implicit sync
interop was available. We also use the binary semaphore when
explicit sync is enabled.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3973
2025-05-05 22:17:04 +02:00
YaoBing Xiao
22db307e4c backend/drm: assign plane_id from function parameter instead of drm_plane 2025-04-30 17:10:45 +08:00
Simon Ser
156d47c866 build: bump version to 0.19.0-rc3 2025-04-27 22:48:44 +02:00
Isaac Freund
80f33cd350 presentation-time: make version a uint32_t
This is consistent with wlr_xdg_shell_create(), wlr_shm_create(),
wlr_layer_shell_create(), etc.
2025-04-27 20:42:57 +00:00
Isaac Freund
7dd8fdf76c tablet-v2: cleanup focused surface on tool destroy
Currently the surface_destroy listener may not get removed if
destroy_tablet_tool_v2() is called while the tool is in proximity to a
surface.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3969
Reported-by: Hooman Ise
2025-04-27 16:57:50 +00:00
YaoBing Xiao
648aee65ad backend/drm: rename conn_name to conn_type_name for clarity 2025-04-27 15:33:36 +00:00
tokyo4j
86976870bd wlr_keyboard_group: fix leak of wlr_keyboard_group->keys
If the underlying wlr_keyboard emits duplicated key-presses,
wlr_keyboard_group->keys might not be empty even after calling
wlr_keyboard_group_remove_keyboard() for all of the keyboards.
2025-04-25 03:23:26 +09:00
tokyo4j
bd8454d3bc Revert "wlr_keyboard: don't emit key event for duplicated keycodes"
This reverts commit 86eaa44a3a.

That commit caused a regression for IME users in many compositors:
when a input_method is activated while a key is pressed, and a virtual
keyboard is created by IME, the following key-release event via the
virtual keyboard is missed since the key in the virtual keyboard haven't
been pressed. For example, pressing and releasing Ctrl+F in Firefox with
fcitx5 running triggered repeated keys (ffffff...) in the opened input
box.
2025-04-25 03:23:21 +09:00
Isaac Freund
e9450a9947
xdg-activation: make wl_global public
This is currently inconsistent with everything else in wlroots.
2025-04-24 13:20:11 +02:00
Simon Ser
78aaaaf7b6 Revert "xwayland: Reset signal mask and handlers before exec"
This reverts commit 954dba3968.

Motivations:

- This only resets some state, but other global state such as other
  signal handlers, process limits (e.g. NOFILE) or system-specific
  settings are left as-is. The chunk of state which does get reset
  is opinionated.
- Compositors have other ways to do this, e.g. with pthread_atfork()
  or with empty signal handler callbacks.
2025-04-20 21:12:33 +02:00
Simon Ser
b9d3ee9a2c build: bump version to 0.19.0-rc2 2025-04-20 13:21:10 +02:00
Kirill Primak
7bf669fb5c xdg-surface: fix geom for surfaces that don't set it explicitly
If a surface which relies on the default window geometry (e.g. wlroots'
Wayland backend output) gets resized, the geometry doesn't get updated.
This commit fixes that. Additionally, the fallback is the explicitly
set window geometry now, not the extents; this works better for
Chromium.
2025-04-19 16:42:56 +03:00
M Stoeckl
8dab5f838d color-management-v1: implement surface_feedback_v1::get_preferred_parametric 2025-04-19 08:20:37 -04:00
YaoBing Xiao
3ad4374a54 backend/drm: add DRM object and property IDs to error 2025-04-18 17:36:05 +08:00
Kirill Primak
ed2406621a xdg-surface: don't disconnect clients for bad effective geom yet
Preferably, the geometry computation would've been done at the client
commit time, but this requires correct subsurface state management which
we don't have at the moment. The next best solution, which is computing
the geometry on server commit time, doesn't currently have a way to
prevent user commit handlers from firing, meaning that compositors might
get an invalid surface state. Additionally, Chromium and gtk-layer-shell
turned out to violate the protocol in this regard, so client
disconnection leads to really bad UX.

As such, complain via a log message instead, and ignore invalid
geometry, falling back to the bounding rectangle.
2025-04-17 15:17:31 +03:00
Philipp Kaeser
f8c639f19a output_layout: Fixes docstring for wlr_output_layout_add[_auto]. 2025-04-16 19:57:33 +02:00
Kirill Primak
5fd43add1c Fix/cleanup includes a bit 2025-04-15 20:13:29 +03:00
Kenny Levinsen
4277d8cfdc scene: Fix scanout log to respect SCANOUT_SUCCESS
scene_entry_try_direct_scanout returns a tristate value, but the log
message was not updated to account for this.

Compare whether or not the state is specifically SCANOUT_SUCCESS for
logging purposes.

Fixes: c450991c4b
2025-04-14 20:36:09 +02:00
Kenny Levinsen
867960d6f4 compositor: Clean up surface current_outputs last
During surface resource cleanup, several signals will be emitted. If any
of these end up calling wlr_surface_send_enter, a new output could be
added to the current_outputs list. This would result in a leaked
surface_output and a dangling wlr_surface pointer.

Clean up current_outputs last.

References: https://github.com/swaywm/sway/issues/8650
2025-04-14 12:46:02 +02:00
Yixue Wang
d7527778bb wlr_client_buffer: add get_shm implementation 2025-04-13 20:48:59 +00:00
Kirill Primak
5dc73937ff xdg-surface: ensure that the effective geom is not empty
Fixes: 5c98d1a04a
2025-04-13 13:26:26 +03:00
Simon Ser
7161bcfabc build: bump version to 0.19.0-rc1 2025-04-12 22:50:55 +02:00
Olivier Tilloy
1e7baefe96 seat/keyboard: optimize wlr_seat_set_keyboard to send the keymap only if it has changed 2025-04-11 16:56:36 +02:00
Kirill Primak
9c9bf2efee xdg-system-bell-v1: set proper global/resource user data
See https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3963.
2025-04-11 14:04:23 +00:00
David Turner
0bf0c55ad2 scene: Move single-pixel buffer things to scene_buffer
Move single-pixel buffer status cache from wlr_scene_surface to
wlr_scene_buffer, it makes more sense there and means the optimisations
will still work if wlr_scene_buffer is used without wlr_scene_surface.
2025-04-11 14:01:35 +00:00
Kenny Levinsen
c450991c4b wlr_scene: Debounce dmabuf feedback on scanout
Direct scanout can be enabled and disabled on a frame-by-frame basis,
and so we could end up sending different feedback to a surface on every
other frame. Reacting to new feedback is expensive, as the client may
need to reallocate their swapchain.

Debounce the state change a number of frames, for now set to 30, to
avoid immediate reaction to scanout (or composition) that only lasts a
few frames.

A timer could be used instead, but it did not seem worth the complexity.
What just want to know that the state has been stable across a
reasonable number of samples, and a counter seems sufficient for that.
2025-04-10 12:44:03 +00:00
David Turner
792bee9657 scene: Optimize rendering of single-pixel buffers
The single-pixel buffer protocol is used to allow wayland clients to
easily draw solid-color rectangles by presenting a 1x1-pixel buffer and
scaling it to the desired size.  This patch improves how these buffers
are then handled in the scene-tree renderer.

We already ignore opaque black rectangles at the very bottom (and
anything under them) because we assume we'll be rendering on a black
background.  This patch detects black opaque single-pixel buffers and
handles them in the same way as black opaque rectangles.  It also
renders single-pixel buffers as rectangles rather than buffers because
this is probably more efficient in the underlying renderer.

In wlr_scene_surface we cache whether the attached buffer is a
single-pixel buffer.  This is done because the
wlr_single_pixel_buffer_v1 will be destroyed after texture upload, after
which it becomes much more annoying to check if the buffer is a
single-pixel buffer.
2025-04-07 13:28:51 +01:00
David Turner
5563d23b81 single-pixel-buffer: Add try_from_buffer() function
Add wlr_single_pixel_buffer_v1_try_from_buffer() and move `struct
wlr_single_pixel_buffer_v1` to wlr_buffer.h. This allows other code to
find out if a wlr_buffer is a single-pixel buffer and, if so, find out
what color it is.
2025-04-07 13:25:42 +01:00
Simon Ser
709fc8fd8e ext-data-control-v1: fix types in wl_list/wl_signal comments
These refer to the wlr protocol types.
2025-04-06 10:09:54 +00:00
Kirill Primak
9c51424f8d ext-data-control: add missing listener list assertion 2025-04-06 10:13:40 +03:00
Kirill Primak
84fc6aaf5a Drop region.h
This header has been deprecated a while ago.
2025-04-01 15:42:39 +03:00
Kirill Primak
582f487b22 meson: add a missing src file 2025-04-01 11:41:56 +00:00
YaoBing Xiao
5f65b1194c ext_image_capture_source_v1: remove unused wlr_renderer include 2025-03-31 17:42:00 +08:00
Simon Ser
fa6cd856e3 color-management-v1: add setter for surface feedback 2025-03-30 16:31:44 +02:00
Simon Ser
95c85af87c color-management-v1: add support for mastering display metadata 2025-03-30 16:31:44 +02:00
Simon Ser
dcf38e3ea9 color-management-v1: add wlr_surface_get_image_description_v1_data() 2025-03-30 16:31:44 +02:00
Simon Ser
ab4ed32c06 color-management-v1: add support for parametric image desc creator 2025-03-30 16:31:37 +02:00
Simon Ser
6d4737a7f6 color-management-v1: add struct wlr_image_description_v1 2025-03-30 16:31:27 +02:00
Simon Ser
0ab3c1d060 color-management-v1: new protocol
This implements the bare minimum to expose the protocol interfaces.

References: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/14
2025-03-30 16:31:17 +02:00
Simon Ser
10b8880fc7 render/color: add wlr_color_transfer_function_get_default_luminance() 2025-03-30 16:21:26 +02:00
Simon Ser
50537e2e6f render/color: introduce enum wlr_color_transfer_function 2025-03-30 16:21:26 +02:00
Simon Ser
e11012a024 render/color: introduce wlr_color_primaries_from_named() 2025-03-30 16:21:26 +02:00
Simon Ser
7d076d0bc9 render/color: introduce wlr_color_named_primaries 2025-03-30 16:21:26 +02:00
Simon Ser
156201fe71 render/color: add wlr_color_primaries_to_xyz() 2025-03-30 16:21:26 +02:00
Simon Ser
d8ad4809fc render/color: include public header from private one 2025-03-30 16:21:26 +02:00
Simon Ser
420b60f203 util/matrix: add matrix_invert() 2025-03-30 16:21:26 +02:00
Kirill Primak
9dbf5b9f6b fullscreen-shell: remove
The protocol implementation has been marked as deprecated in the
previous release.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3846
2025-03-30 01:15:09 +03:00
Simon Ser
99da6ccc87 backend/drm: pass DRM_MODE_ATOMIC_NONBLOCK for test commits
The kernel performs some additional checks when
DRM_MODE_ATOMIC_NONBLOCK is supplied: it requires that none of the
planes are still busy with a previous page-flip.

Pass the flag during test-only commits so that we don't end up
performing a commit which will fail.
2025-03-24 12:45:47 +00:00
xurui
db2c907f93 xwayland/selection/dnd: always send finished event
Signed-off-by: xurui <xurui@kylinos.cn>
2025-03-23 15:17:10 +01:00
Thomas Frans
221bc5f6aa
git: fix incorrect subprojects ignore in .gitignore
Ignoring the entire `/subprojects/` directory prevents the next rule
from including just the Meson wrap files. Instead, ignore all the files
in the directory which allows the intended behavior.
2025-03-23 13:24:15 +01:00
Simon Ser
128cd07e91 scene/surface: use source buffer to signal release timeline point
We were signaling the release timeline point when the
wlr_client_buffer was released. However, the wlr_client_buffer isn't
necessarily released at the same time as the underlying source
wlr_buffer. For instance, with wl_shm the source buffer is released
before the wlr_client_buffer, and with linux-dmabuf-v1 the source
buffer is released after the wlr_client_buffer. However, we want
to signal the release timeline point exactly at the same time we
send the wl_buffer.release event to the client.

Use surface->buffer->source instead of &surface->buffer->base to
fix this.

linux-drm-syncobj-v1 can only be used with DMA-BUFs, and
wlr_client_buffer.texture will keep the source locked, so
surface->buffer->source is guaranteed to be non-NULL and unreleased.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3940
Fixes: 9e71c88467 ("scene: unwrap wlr_client_buffer for direct scan-out")
2025-03-22 23:55:24 +00:00
Chris Ever
ba7ac3efe5
fix comment typo in wlr_text_input_v3.h
fixes #3951
2025-03-15 00:11:47 +08:00
Kenny Levinsen
954dba3968 xwayland: Reset signal mask and handlers before exec
Certain signal-related properties, such as the signal mask and handlers
that are set to ignore, are not reset by exec and therefore affect the
new process image.

In case of Xwayland, a compositor setting SIGCHLD to SIG_IGN causes
keyboard compilation to fail as it expects waitpid to work by default.

Reset the signal mask and the two signals that sway is known to set.
2025-03-13 01:37:20 +01:00
Simon Ser
50edd3a42d Document config.h 2025-03-10 15:02:26 +00:00
liupeng
31f9d6bb97 screencopy-v1: drop output_enable listener 2025-03-10 15:33:46 +08:00
Simon Ser
94cb8e2bc7 backend/drm: fix enabling an output with a custom mode set
Since 5567aefb1c ("backend/drm: Don't add pollute fixed modes
list with custom modes"), when a custom mode is set on an output,
current_mode will be NULL.

Instead of checking current_mode, check width/height.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3946
2025-03-07 13:00:06 +00:00
Simon Ser
ca1f9f86e6 backend/drm: handle custom modes in connect_drm_connector()
On startup, some connectors might be already lit up with a custom
mode. We weren't correctly populating the current output state in
this case.
2025-03-07 13:00:06 +00:00
Simon Ser
08c74f36a9 xwayland/xwm: log when property type is invalid 2025-03-06 15:22:49 +01:00
Simon Ser
5175b6e94e xwayland/xwm: handle deleted properties
X11 clients might delete window properties. In that case, reset
our state to the initial value.
2025-03-06 15:21:13 +01:00
Guido Günther
e752e3ec06 xwm: Handle NET_WM_WINDOW_OPACITY
Lot of clients use it (e.g. both Qt and GTK) although it never made it
into the spec at

  https://specifications.freedesktop.org/wm-spec/latest-single/

until recently

  https://gitlab.freedesktop.org/xdg/xdg-specs/-/merge_requests/97

Signed-off-by: Guido Günther <agx@sigxcpu.org>
2025-03-06 14:18:20 +00:00
yuiiio
a63e21d94c output: don't update disabled cursor texture 2025-03-06 13:55:53 +00:00
Simon Ser
3c76b93272 output/cursor: drop output_cursor_reset()
This function is ill-named: it doesn't reset anything. It probably
contained more logic in the past and got stripped down.
2025-03-06 13:30:03 +00:00
Simon Ser
3505079823 ci: fetch gyosu from new upstream
gyosu has moved to Codeberg.
2025-03-06 10:32:09 +01:00
Kirill Primak
a9542b9565 scene: don't mention damage in region-scaling functions 2025-03-03 17:57:48 +03:00
Kirill Primak
602a00ec1f scene: don't always round scaled regions up
It makes sense for damage, less so for opaque regions.
2025-03-03 14:50:57 +03:00
Kirill Primak
66dfb7f49b xdg-shell,layer-shell: assert that configure events are only sent to initialized surfaces
This helps to ensure that compositors behave correctly and don't confuse
clients.
2025-03-01 16:07:11 +00:00
Kirill Primak
b13fe9b3a1 backend/wayland: use a separate event queue for busy loops
This avoids processing events which we're not interested in.
Specifically, this fixes a case where output_commit() could be
indirectly called from itself either from import_dmabuf() or while
waiting for a configure event when enabling the output.
2025-03-01 16:03:52 +00:00
Kirill Primak
35ff09a754 backend/output: extract backend to a variable in output_commit() 2025-03-01 16:03:52 +00:00
Kirill Primak
90b5f0dde5 backend/wayland: don't send request_state when enabling an output 2025-03-01 16:03:52 +00:00
Simon Ser
e83b06e732 render/vulkan: unify alpha pre-multiplication in output shader
Instead of handling alpha pre-multiplication in each branch, add
some common logic before and after handling OUTPUT_TRANSFORM.
2025-02-23 15:41:23 +01:00
David Turner
c9d6339b60 Fix includes of wlr_output.h
Remove unneeded includes of wlr_output.h from wlr_compositor.h and
wlr_cursor.h (unneeded now that we forward-declare struct wlr_surface)
and put the actually-required includes in the right places.
2025-02-21 13:44:42 +00:00
David Turner
1380a48b4d Declare struct wlr_surface in a less weird place
wlr_compositor.h contains references to `struct wlr_surface` in function
arguments before it actually defines it.  This generally works because
wlr_compositor.h includes wlr_output.h which contains a
forward-declaration for `struct wlr_surface` (despite not actually
referencing it).

This is all pretty weird, and gives very confusing errors if you manage
to end up with wlr_output.h including wlr_compositor.h (eg. via an
indirect route) so make it less weird.
2025-02-21 13:44:42 +00:00
Kenny Levinsen
dc7dba8b1f scene/surface: Do not use buffer dimensions for clip
The surface's buffer dimensions were used to scale the clip's x/y
offset. If a surface had a larger buffer than src_box, the calculations
to scale the x/y portion of the clip would be incorrect, yielding
graphical glitches.

This was noticed with Chromium in sway, which during resize uses a
viewport with a src_box to avoid immediate buffer reallocation. While
the viewport was in use, the surface would be shifted so that too much
content was cropped in the upper left, and damage glitching was visible
in the lower right.

Use the buffer source box dimensions instead.
2025-02-18 14:39:17 +00:00
James Ramsey
edd8df76d8 Bump required w-p version to 1.40
Signed-off-by: James Ramsey <james.jehiel.ramsey@gmail.com>
2025-02-14 18:57:37 +00:00
James Ramsey
16f607f008 Implement updated version of ext-idle-notify protocol
Signed-off-by: James Ramsey <james.jehiel.ramsey@gmail.com>
2025-02-14 18:57:37 +00:00
Guido Günther
fef4f3637a seat: Don't forget to destroy touch points on touch up
Otherwise the number of touch points goes up constantly and d'n'd via
touch can't work as validation always fails.

Fixes 75ecba44 ("seat: add serials to touch up events")

Signed-off-by: Guido Günther <agx@sigxcpu.org>
2025-02-13 16:41:05 +00:00
Alexander Orzechowski
d305934ebe ext_data_control: Add protocol implementation 2025-01-31 18:39:17 +00:00
Simon Ser
714a0264a6 ci: generate and publish HTML documentation 2025-01-31 15:27:03 +00:00
Simon Ser
a64e1a58b1 backend/drm: log when creating multi-GPU renderer
Creating a renderer results in lots of logs. Make it clear that
the logs belong to a multi-GPU renderer (as opposed to a primary
renderer created by the compositor).
2025-01-30 02:10:58 +00:00
Simon Ser
83c5b15194 Remove all calls to pixman_region32_not_empty()
Replace them with pixman_region32_empty(), which avoids using a
double-negative when checking if a region is empty. Also use that
new function when checking for non-empty regions so that only one
variant of the Pixman API is used.
2025-01-29 23:37:06 +01:00
Simon Ser
a818251aec render/pixman: drop cast for pixman_image_set_clip_region32()
The new Pixman release has made this const.
2025-01-29 23:33:50 +01:00
Simon Ser
0d056a0315 util/matrix: remove unnecessary include 2025-01-27 17:50:18 +01:00
Simon Ser
639ca05d35 matrix: move to util/
wlr_matrix is not a standalone type like other headers in types/,
it's more of an internal utility. Move it to the appropriate place.
2025-01-27 17:48:18 +01:00
Alexander Orzechowski
c1eb053f5e render/drm_syncobj: Remove the ready signal from timeline_waiter
It's unused.
2025-01-26 18:02:14 -05:00
Alexander Orzechowski
82223e451a render/drm_syncobj: Add a callback when ready
The old approach of using a signal is fundamentally broken for a common
usecase: When the waiter is ready, it's common to immediately finish and
free any resources associated with it.
Because of the semantics of wl_signal_emit_mutable() this is UB.
wl_signal_emit_mutable() always excepts that the waiter hasn't been freed
until the signal has finished being emitted.

Instead of over engineering the solution, let's just add a callback required
by wlr_drm_syncobj_timeline_waiter_init(). In this callback, the implementation
is free to finish() or free() any resource it likes.
2025-01-26 18:02:05 -05:00
Simon Ser
211eb9d60e matrix: drop rotation
It's unused.
2025-01-26 17:56:04 +01:00
Simon Ser
7d1f535e49 matrix: drop wlr_matrix_transpose()
It's unused.
2025-01-26 17:52:39 +01:00
Simon Ser
9b55737cf5 Make wlr_matrix private API
36cc698bc5 ("matrix: deprecate") has deprecated wlr_matrix more
than one year ago. It's now time to drop it from our public API.
2025-01-26 17:46:50 +01:00
Guido Günther
fa97f7f1f0 buffer: Move wlr_buffer_is_opaque to public header
Fixes: 1ee3ed43 ("buffer: Make wlr_buffer_is_opaque public")

Signed-off-by: Guido Günther <agx@sigxcpu.org>
2025-01-26 00:17:40 +01:00
Guido Günther
1ee3ed4310 buffer: Make wlr_buffer_is_opaque public
It's useful for compositors.
2025-01-25 23:42:49 +01:00
Kirill Primak
1c2cb4c802 drm-syncobj: return false instead of NULL 2025-01-24 21:57:28 +03:00
Kirill Primak
4f6dd01e5a backend/wayland: remove syncobj waiter on buffer destroy 2025-01-24 21:46:31 +03:00
Kirill Primak
980ac9e4c8 CONTRIBUTING.md: mention listener list assertions 2025-01-20 16:17:12 +03:00
Kirill Primak
b25f98d583 pointer-constraints: use wlr_surface_synced.commit hook
This fixes a problem where an outdated surface input region was used to
compute the effective confinement region.

Additionally, this commit fixes a bug in pointer_constraint_create()
which caused the initial region to not be applied immediately.

This is a breaking change: set_region is now emitted before the role
commit hook is called, and it's not emitted if the region hasn't
actually changed.
2025-01-17 16:30:34 +00:00
Kirill Primak
f95270bb5e pointer-constraints: rewrite doc comments 2025-01-17 16:30:34 +00:00
Kirill Primak
c3224d4160 compositor: add wlr_surface_synced commit hook 2025-01-17 16:30:34 +00:00
Kirill Primak
e3596abc9a pointer-constraints: fix deactivating oneshot constraint on destroy 2025-01-17 16:03:36 +00:00
llyyr
9ab87167b5 backend/drm: don't leak mgpu_formats 2025-01-16 14:58:59 +00:00
Kirill Primak
8f56f7ca43 Assert (almost all) signals have no attached listeners on destroy 2025-01-15 19:53:11 +03:00
Kirill Primak
b03b05d2b3 xdg-dialog: add missing wm destroy signal 2025-01-15 19:51:05 +03:00
Kirill Primak
bcf8e467db xdg-toplevel-icon-v1: add implementation 2025-01-14 18:46:43 +00:00
Simon Ser
061aa1bd15 ext-image-copy-capture-v1: implement PAINT_CURSORS flag
This is unreliable because this is first come, first served: the
first capture stream decides whether or not cursors will be
included. Moreover, if the output lacks hw cursor support, cursors
will always be included. But it's the best we're going to get with
automatic wlr_output sources (and has bug parity with
wlr-screencopy-unstable-v1).
2025-01-14 16:42:12 +01:00
Simon Ser
6bb8bb1cb7 ext-image-capture-source-v1: add start/stop hooks
This allows the source to change its behavior when actively
captured.
2025-01-14 16:42:12 +01:00
Simon Ser
82f9cd5310 backend/headless: accept hardware cursors
When running headless, remoting programs (e.g. VNC servers) might
want to capture outputs without the cursor, and send the cursor
image separately.
2025-01-14 16:42:12 +01:00
Simon Ser
248e837cb3 ext-image-capture-source-v1: add output cursor source 2025-01-14 16:42:12 +01:00
Simon Ser
c0881bdc01 output: require commit after hardware cursor update
Up until now only the DRM backend required an output commit after
updating the cursor. Unify this for all backends, because:

- Screen capture can now catch cursor updates listening for output
  commits
- In the future we want to make the cursor a regular wlr_output_layer,
  which would need an output commit to be updated anyways
2025-01-14 16:42:12 +01:00
Simon Ser
08e14deeca ext-image-capture-source-v1: add output source 2025-01-14 16:42:12 +01:00
Simon Ser
855b3fd607 ext-image-capture-source-v1: add wlr_ext_image_capture_source_v1_set_constraints_from_swapchain() 2025-01-14 16:35:52 +01:00
Simon Ser
c24efad6df ext-image-copy-capture-v1: add wlr_ext_image_copy_capture_frame_v1_copy_buffer() 2025-01-14 16:35:52 +01:00
Simon Ser
4e4155ccbe ext-image-copy-capture-v1: new protocol implementation
Co-authored-by: Andri Yngvason <andri@yngvason.is>
2025-01-14 16:35:50 +01:00
Simon Ser
6712e774d4 ext-image-capture-source-v1: add source pointer cursors 2025-01-14 16:34:11 +01:00
Simon Ser
dadcbf65e6 ext-image-capture-source-v1: add base interface 2025-01-14 16:34:11 +01:00
Kirill Primak
eb85831284 text-input: fix wlr_text_input_manager_v3.text_inputs comment 2025-01-11 21:48:20 +00:00
Kirill Primak
a231bf7f62 backend: drop wl_display refs from docs 2025-01-11 21:36:29 +00:00
Kirill Primak
4a67628cb0 session: drop unused wlr_session.vtnr 2025-01-11 22:42:18 +03:00
llyyr
62ecec6d53 wlr_drag: cleanup icon_destroy listener when destroying icon 2025-01-11 20:20:10 +05:30
Kirill Primak
89000b7df0 backend/session: simplify udev event action matching
This avoids extra strcmp() calls and is easier to read.
2025-01-08 20:45:27 +03:00
Kirill Primak
d5d650f9f6 output: introduce wlr_output_finish() 2025-01-08 16:06:12 +00:00
Kirill Primak
7963ba6a0d buffer: introduce wlr_buffer_finish() 2025-01-08 16:06:12 +00:00
sunzhguy
5eed5d622d session: skip adding duplicate DRM device
Signed-off-by: sunzhguy <sunzhigang1@kylinos.cn>
2025-01-06 10:10:46 +08:00
Kirill Primak
38923826c3 xwayland,render/vulkan: fix some size assertions 2024-12-30 12:47:54 +03:00
liupeng
fa4d8bbad7 render/egl: attribs len could be equal to size
Exts.KHR_display_reference makes assert fail.
2024-12-30 17:34:02 +08:00
Simon Ser
6f6268988b backend: document that buffer_caps/features are mutable 2024-12-25 18:40:05 +00:00
Simon Ser
b908d865b1 backend: replace get_buffer_caps hook with a struct field
Do the same as wlr_renderer: the supported buffer capabilities are
static for the lifetime of the backend.
2024-12-25 18:40:05 +00:00
Simon Ser
9fdffba170 render/allocator: use udmabuf allocator automatically
It makes sense to use udmabuf when the backend wants shm, the
renderer wants DMA-BUFs, and we don't have a DRM FD because we're
running with a software renderer (e.g. llvmpipe or lavapipe).
2024-12-22 21:37:57 +00:00
Simon Ser
cfcf06b8b0 render: add WLR_RENDERER_FORCE_SOFTWARE env var
Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3693
2024-12-22 21:37:57 +00:00
Simon Ser
24232e8e98 render/vulkan: accept negative DRM FD to select software rendering 2024-12-22 21:37:57 +00:00
Simon Ser
b4ce0d8b39 render/egl: accept negative DRM FD to select software rendering 2024-12-22 21:37:57 +00:00
Simon Ser
7cf8e80ffe render/allocator: add udmabuf allocator
udmabuf can create a DMA-BUF backed by a memfd. This is useful
when running with a software implementation of GL/Vulkan: the memfd
can be passed to the parent compositor via wl_shm and the DMA-BUF
can be imported via the usual APIs into GL/Vulkan.
2024-12-22 21:37:57 +00:00
Kirill Primak
1c604207c6 seat: don't generate serial 0
0 is reserved to mean "no event has been sent to the client".
2024-12-22 21:33:11 +00:00
Simon Ser
428279a319 build: add Meson wrap manifests for our dependencies
Add Meson wrap manifests for all of our dependencies which can
fallback to a subproject. This makes it easier to build wlroots
on platforms where system packages are outdated. Users can now
opt-in via `meson configure --wrap-mode=default` and Meson will
download and build any missing dependency.

Don't download by default because this can be quite surprising and
undesirable for some users (e.g. if they are just missing some -dev
package).
2024-12-22 21:24:03 +00:00
Kirill Primak
90530d43fe xdg-dialog-v1: add implementation 2024-12-22 20:46:19 +00:00
Consolatis
be555a9a99 backend/drm: make drm_lease implementation follow docs
This ensures compositors get a output_destroy signal when the
lease is granted and a new_output signal when the lease is revoked.
2024-12-21 01:00:12 +01:00
Consolatis
c6de47d415 wlr-foreign-toplevel: version guard fullscreen state 2024-12-21 01:20:53 +03:00
Consolatis
bd3724aa26 wlr-foreign-toplevel: convert to stack backed states array 2024-12-21 01:20:53 +03:00
Consolatis
648c64b7e4 wlr-foreign-toplevel: slight refactor 2024-12-21 01:19:17 +03:00
Consolatis
41e2331843 xwm: add missing NET_WM_STATE states
https://specifications.freedesktop.org/wm-spec/latest/ar01s05.html#id-1.6.8
2024-12-20 21:47:17 +00:00
Simon Ser
776f2c4e4d output: adjust warning when hardware cursors are unsupported
We'd print "cursor texture size exceeds hardware limitations" when
some hardware doesn't support cursors at all. Change the message
to better indicate the cause.
2024-12-20 10:39:42 +00:00
M Stoeckl
b97106ddcb wlr_cursor: use default shape if requested shape missing 2024-12-16 15:34:45 +00:00
Simon Ser
2b4f30dc1d render/gles2: validate shaders at build time
Use glslang to validate GLSL shaders at build time. This is
optional: if glslang is not found, shader validation is skipped.
2024-12-15 13:59:42 +01:00
Consolatis
546c5d000d xwayland: fix xdg->xwayland drag-and-drop
As struct wlr_drag is destroyed on drop and in the process resets
the focus, a xwayland dnd listener would also reset xwm->drag_focus.

This prevents the xcb replies from being processed and also prevents
the transfer if a compositor would not additionally request new focus
in its wlr_drag destroy handler (which is something usually only done
when in a focus-follows-mouse setting).

This patch creates a new xwm->drop_focus pointer which is a copy of
xwm->drag_focus at drop time. The xcb reply handler and transfer
logic now use the new xwm->drop_focus for their authorization checks.
2024-12-11 08:51:10 +01:00
Simon Ser
71943b3b1e data-device: reset focused surface when destroyed
The surface may be destroyed before the struct wlr_seat_client.

Spotted by Consolatis.
2024-12-11 07:15:02 +00:00
Simon Ser
a7ebe7c026 xwayland: remove loop to find drag focus surface
We can just use wlr_xwayland_surface_try_from_wlr_surface() here
instead.
2024-12-07 14:49:26 +01:00
Simon Ser
c7acfe906b xwayland: listen to drag focus destroy signal
The wlr_drag takes care of resetting the focused wlr_surface when
it's destroyed, however we store the wlr_xwayland_surface, which
may be destroyed before.
2024-12-07 14:49:26 +01:00
Simon Ser
9649fbe443 render/vulkan: fix crash on OOM
Closes: https://github.com/swaywm/sway/issues/8485
2024-12-06 14:28:40 +00:00
Tycho Andersen
631e5be0d7 xwayland: don't fail on SIGCHLD
SIGCHLD here isn't fatal: we have other means of notifying that things were
successful or failure, and it causes many compositors to have to do a bunch
of extra work:

https://github.com/qtile/qtile/issues/5101
https://github.com/flacjacket/pywlroots/pull/207#issuecomment-2502680133
https://github.com/djpohly/dwl/pull/212

Signed-off-by: Tycho Andersen <tycho@tycho.pizza>
2024-12-06 14:09:28 +00:00
Consolatis
be3d2b74cf dnd: ensure internal dnd handlers are unlinked on xwm_destroy()
Fixes #3925
2024-11-29 13:42:55 +00:00
Simon Ser
c6dd5e3c2e backend/drm: check buffer format for multi-GPU
Fixes on-screen corruption when displaying a fullscreen client
with an implicit modifier on the secondary GPU.

What happens here:

- Client allocates a buffer with an INVALID modifier on primary GPU.
- Compositor attempts to scan-out this buffer on an output connected
  to secondary GPU.
- Buffer is imported to secondary GPU, and is interpreted as if it had
  the secondary GPU's implicit tiling, even though it has the primary
  GPU's implicit tiling.

We need to forbid cross-device imports with implicit modifiers.
The mgpu_formats list is stripped from any INVALID modifier so
checking that fixes the bug.

Using the mgpu_formats list has an additional benefit: the buffer
is rejected in the test commit if it doesn't have a format supported
by the multi-GPU renderer.

Requires this Mesa bugfix:
https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/31725
2024-11-28 20:27:48 +01:00
Simon Ser
2424b1ecdd render/drm_format_set: fix corruption in wlr_drm_format_set_remove()
A single byte was moved instead of a full uint64_t. This results
in corrupted lists containing bogus modifiers.
2024-11-28 19:14:31 +00:00
tokyo4j
e21899037a wlr_keyboard: don't emit key event for duplicated keycodes
This fixes the memory leak in wlr_keyboard_group.keys. The leak happened
because wlr_keyboard.keycodes never contains duplicated keycodes while
wlr_keyboard_group.keys can, so calling wlr_keyboard_finish() for all
the wlr_keyboards in wlr_keyboard_group doesn't always free all the keys
in wlr_keyboard_group.keys.
2024-11-28 16:24:57 +00:00
Kirill Primak
c0d4d7217b Add missing destroy events on automatically destroyed objects 2024-11-26 17:49:22 +00:00
Simon Ser
3e651b4642 backend/drm: fix drmModePageFlip() when disabling CRTC on legacy uAPI
drmModePageFlip() will fail with EBUSY on a disabled CRTC.

We can't fix this by clearing the DRM_MODE_PAGE_FLIP_EVENT when
performing a commit which disables CRTCs, because some device-wide
commits might also page-flip other enabled CRTCs (and skipping the
page-flip event would mess up our buffer tracking).

Fix this by immediately completing page-flips which disable a CRTC
on the legacy uAPI.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3918
2024-11-23 15:29:44 +01:00
Kirill Primak
9aca985865 tinywl: don't pass time to cursor mode motion handlers
It's unused.
2024-11-23 00:50:28 +03:00
Simon Ser
b2c3c371fc scene: assert on bad wlr_scene_buffer params
We assert in wlr_renderer if these are invalid. Assert earlier to
make debugging easier.
2024-11-22 18:49:50 +00:00
Alexander Orzechowski
0d6cc471e9 scene: Assert all signals are clear when destroying 2024-11-22 09:55:51 -05:00
Kirill Primak
24597bb971 meson: require wayland >=1.23.1 2024-11-21 16:46:55 +00:00
Kirill Primak
2ff95e5c97 meson: use kwargs for wayland deps 2024-11-21 16:46:55 +00:00
Kirill Primak
f233d25e86 tinywl: don't pass surface to focus_toplevel()
It's not required.
2024-11-16 22:05:01 +03:00
Kirill Primak
0b720ae5ea seat: drop last_event 2024-11-12 08:28:50 +00:00
Guido Günther
85e2b662f1 renderer: Drop mention of wlr_renderer_begin_with_buffer
It got removed in 3ed1268f ("render: Nuke old read pixels API")

Signed-off-by: Guido Günther <agx@sigxcpu.org>
2024-11-11 22:48:50 +00:00
Simon Ser
2c3053370c output-management-v1: only create custom mode object for enabled heads
We were always creating a custom mode object when the output didn't
have a fixed mode. This is important to handle two cases:

- Virtual outputs with no concept of fixed modes.
- DRM outputs with a list of fixed modes but with a custom mode set.

However, in the case where an output didn't have a fixed mode and
was disabled, we were also creating the custom mode object. Clients
would then see a "ghost" mode: a mode object with no properties at
all.

Fix this by only creating the custom mode object if the output is
enabled.

Fixes: 5de9e1a99d ("wlr-output-management: Send custom modes to clients")
Closes: https://github.com/swaywm/sway/issues/8420
2024-11-11 19:18:32 +00:00
Simon Ser
e9a6b3b85d backend/wayland: add support for explicit sync
wl_buffer.release event delivery becomes undefined when using the
linux-drm-syncobj-v1 protocol, so we need to wait for buffer
release via a timeline point instead.

The protocol requires both wait and signal timelines to be set, so
we need to create one when the compositor only supplies a wait
timeline.
2024-11-11 15:22:22 +00:00
Simon Ser
ca29f43a54 render/drm_syncobj: add addon set 2024-11-11 15:22:22 +00:00
Simon Ser
8e36040e88 render/drm_syncobj: de-duplicate drm_syncobj timeline init 2024-11-11 15:22:22 +00:00
Kenny Levinsen
c3acef0dc0 scene: Only unwrap client buffer when underlying buffer is held
Client buffers backed by wl_shm is aggressively released, in which case
we are not allowed to access it. Locking an already released buffer and
later unlocking it will also re-trigger release, confusing clients.

As a quick workaround, guard the unwrap by checking if the buffer is
locked, which will be the case for non-wl_shm buffers.
2024-11-10 00:22:17 +01:00
Kenny Levinsen
f440c60128 backend/drm: Remove redundant error logs
wlr_renderer and wlr_allocator will print out errors as they go, and end
with a final error if they fail to create anything. The caller of this
function will also log when it fails.

Skip the redundant errors emitted here.
2024-11-08 15:14:08 +01:00
Kenny Levinsen
0108506c77 backend/drm: Allow proceeding with render-less mgpu
If an mgpu device does not have a renderer, continue without one rather
than ignoring it entirely. It is not guaranteed that we will be able to
scan out to it in any particular configuration, but that is true for any
output regardless, and having the output visible is not harmful even if
it cannot light up.

To proceed safely, we strip implicit modifier support from all planes,
while avoiding duplication of the same logic for mdgpu_formats.

This helps GUD and DisplayLink scenarios.
2024-11-08 15:14:08 +01:00
Kenny Levinsen
66ddd62e42 backend/drm: Move mgpu renderer setup to function
This helps readability a bit and will make error handling in a coming
commit easier.
2024-11-08 15:14:08 +01:00
Kenny Levinsen
b2bb111f03 backend/drm: Check for mgpu_renderer instead of parent
The presence of the renderer is what matters with respect to blitting.
Having a parent without the need to blit will be allowed later.
2024-11-08 15:14:08 +01:00
Kenny Levinsen
1dd05437bf backend/drm: Bail if renderer cannot import DMA-BUFs
We create a renderer for the sole purpose of blitting buffers from a
primary renderer that we might not be able to scan-out from. If we end
up with the pixman renderer, it either won't work becuase it cannot
import dmabufs from the primary renderer, or won't have any effect
because the primary renderer already uses dumb buffers.

We test for DMA-BUF capabilities specifically to make it clear what our
interest is, rather than focusing too much on the pixman renderer.
2024-11-08 15:14:08 +01:00
Kenny Levinsen
55f15d1abd backend/drm: Ensure renderer is set to NULL on error
If init_drm_renderer failed, it would destroy the renderer but would not
set it to NULL, leading to use-after-free.

NULL the renderer after destroying it.
2024-11-08 13:45:05 +01:00
Kenny Levinsen
70d3635985 drm_format_set: Add wlr_drm_format_set_remove
wlr_drm_format_set_remove lets you remove a modifier from the specified
format, useful for filtering implicit modifiers.
2024-11-08 13:45:05 +01:00
Kenny Levinsen
014023c14f backend/drm: Set timeline support based on capability
We assumed that all atomic backends supported syncobj, but gud does not.
Instead, query DRM_CAP_SYNCOBJ_TIMELINE when using the atomic backend.
2024-11-08 13:45:05 +01:00
Simon Ser
c0945b6613 render/allocator: drop allocator_autocreate_with_drm_fd()
This is now unused.
2024-11-07 18:55:01 +01:00
Simon Ser
4c4d74a564 backend/drm: use public wlr_allocator_autocreate()
We don't need any custom behavior since d8c0707e27 ("backend/drm: return
secondary backend DRM FD").
2024-11-07 18:50:25 +01:00
Simon Ser
4ec1defb3e all: use public <wlr/render/allocator.h>
We don't need to use the private header in these files.
2024-11-07 18:48:20 +01:00
Simon Ser
baeecc8dbd backend/wayland: handle DMA-BUF import failures
create_immed() is a bit dangerous because on failure, either the
connection is closed or the buffer is silently ignored.

Use create() with a roundtrip to figure out whether the import
succeeded.
2024-11-07 14:42:01 +00:00
Simon Ser
9e71c88467 scene: unwrap wlr_client_buffer for direct scan-out
Passing the wlr_client_buffer directly has a downsides because a
fresh wlr_buffer pointer is passed each output commit instead of
cycling through existing wlr_buffer objects:

- The FDs are re-imported each time in the backend.
- Any import failure is logged every output commit [1].
- The Wayland backend cannot handle import failures without
  roundtripping each output commit [2].

Instead, extract the source buffer from the wlr_client_buffer and
pass that to the backend.

[1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4836
[2]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4841
2024-11-07 14:17:35 +00:00
Kenny Levinsen
63fabecee2 backend/wayland: Account for shm buffer offset
If we are trying directly scan-out a shm buffer, we create a temporary
buffer pool to instantiate a wl_buffer from, created in accordance to
our buffers size. If the buffer has an offset, it will end up out of
bounds of the created pool.

Extend the temporary pool by the buffer offset to compensate. Matching
the original pool size does matter unless we want to optimize away the
temporary pool to reduce mappings in the parent compositor.
2024-11-07 14:45:23 +01:00
xurui
3f314bc183 output-power-management-v1: use enum_is_valid functions
Signed-off-by: xurui <xurui@kylinos.cn>
2024-11-07 10:20:03 +00:00
xurui
38fc4f2976 wlr_output_management_v1: use enum_is_valid functions
Signed-off-by: xurui <xurui@kylinos.cn>
2024-11-07 10:20:03 +00:00
xurui
81fa6c4b96 wlr_linux_dmabuf_v1: use enum_is_valid functions
Signed-off-by: xurui <xurui@kylinos.cn>
2024-11-07 10:20:03 +00:00
xurui
cda6fdffac layer-shell-v1: use enum_is_valid functions
Signed-off-by: xurui <xurui@kylinos.cn>
2024-11-07 10:20:03 +00:00
Simon Ser
2c098a3e45 build: rename meson_options.txt to meson.options
This is the preferred name since Meson 1.1.
2024-11-07 10:53:38 +01:00
Kenny Levinsen
1f13bc72fe render/vulkan: Garbage collect unused buffers
Perform a primitive garbage collection of buffers that have not been
used in the past 10 seconds, an arbitrarily selected number.

As garbage collection also makes span buffer allocation happen much more
often, logging on allocation activity leads to a lot of log noise so get
rid of that while at it.
2024-11-07 09:49:29 +00:00
Kenny Levinsen
0d728f96b7 util/time: Move NSEC_PER_SEC to header
This constant can be useful whenever one has to deal with timespecs, so
let's move it to the header.
2024-11-07 10:22:56 +01:00
Kenny Levinsen
d6b47c3ab0 wlr_keyboard_group: Use get_current_time_msec 2024-11-07 10:22:10 +01:00
Kenny Levinsen
8bb6935374 tablet-v2: Event time should be milliseconds
The event time used for zwp_tablet_pad_group_v2_send_mode_switch was
tv_nsec / 1000, which is microseconds resetting every whole second.

Use get_current_time_msec to get milliseconds including whole seconds.
2024-11-07 10:09:36 +01:00
mmcomando
c5d8f6d187 Consider using vulkan renderer in default configuration
With this change vulkan renderer can be automatically chosen in two more cases:

GLES2 renderer is disabled at compile time
GLES2 renderer failed to be created

Main purpose of this change is to automatically choose vulkan as renderer when GLES2 renderer is not enabled.
2024-11-06 08:52:58 +01:00
Simon Ser
3fdbfb0be8 buffer: add more docs 2024-11-04 19:05:38 +01:00
Kirill Primak
ea93dd5cc3 xdg-system-bell-v1: add implementation 2024-11-03 08:54:10 +03:00
Kirill Primak
3ca4bc8c09 meson: fix file order 2024-11-03 08:52:48 +03:00
llyyr
d835fa813f backend/multi: only consider backends with dmabuf cap for timeline
timeline feature will never be applicable to backends without it, so
don't check it. Before this commit, it would cause timeline to be set
to false if libinput ended up being the last item in the list (which is
the case currently).
2024-10-30 23:10:59 +00:00
Alexander Orzechowski
ebab992b44 wlr_scene: Add notes about wlr_scene_rect taking premultiplied colors 2024-10-29 15:15:41 -04:00
Kenny Levinsen
c1ce983826 backend/drm: Store only a single plane viewport
We store both queued and current buffers to be able to retain both the
framebuffer currently on screen and the one queued to replace it. From a
re-use perspective, we only care about the last committed framebuffer.

The viewport is only stored in order to be re-used together with the
last committed framebuffer, so do away with the queued/current
distinction and store a single viewport updated every time a commit
completes.
2024-10-29 11:18:48 +01:00
Kenny Levinsen
1edd5e224f backend/drm: Remove reset from interface
The reset implementations are no longer used.
2024-10-29 11:13:13 +01:00
Kenny Levinsen
0f255b46fc backend/drm: Remove automatic reset on VT switch
Instead of trying to restore the drm state when the session is activated
again, just disconnect all outputs when the session is deactivated. The
scan that triggers on session activation will rediscover the connectors.
2024-10-28 21:20:30 +01:00
Kenny Levinsen
3df1528a8f backend/drm: Store viewport with framebuffer
Accessing the output state viewport require a buffer, and that might not
have a state with a buffer when preparing the plane properties for an
atomic commit.

Instead, store the properties at the same time as the fb, and use a
similar mechanism to carry the state around.
2024-10-28 19:22:27 +00:00
Kirill Primak
1520be3c5c Make all listeners private 2024-10-28 18:07:02 +00:00
Simon Ser
3bbfae73ae render/vulkan: add support for explicit sync 2024-10-28 17:51:21 +00:00
Simon Ser
9351c78d70 render/vulkan: add render_pass_destroy()
De-duplicate the cleanup logic.
2024-10-28 17:51:21 +00:00
David Turner
cf43a447cb scene: Transform coordinates for direct scanout
We support direct scanout when there is an output and buffer
transform so long as the transforms are the same (so cancel out for the
buffer contents).  But we still need to apply the output transform to
the destination box location and size.
2024-10-28 13:28:23 +00:00
Kenny Levinsen
7717c92ed0 backend/drm: Skip plane props if buffer is not committed
If our session is re-activated during scanout, restore_drm_device will
reset planes and then attempt an enabling modeset commit without a
buffer. The new plane transform logic requires a committed buffer to be
present to calculate the boxes if they were not explicitly provided, and
at least amdgpu rejects commits that try to use 0 as default.

Skip updating plane props instead of segfaulting if no buffer is set.

A better fix would be to not rely on restore_drm_device at all and
instead require compositors to modeset in response to session
activation.

Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3912
2024-10-28 02:14:49 +01:00
llyyr
4c74a8843a presentation-time: bump protocol version to v2
We've actually been doing the wrong thing this whole time, for v1 of the
protocol, we should set the refresh_nsec field to 0 if the output does
not have a constant refresh rate. However we've been setting it to the
fastest rate instead since eac7c2ad2f
which is incidentally exactly what v2 of the protocol proposes.

So allow advertising v2, and fix v1 to set refresh_nsec to 0.
2024-10-28 02:58:48 +05:30
Simon Ser
e8e76dc295 backend/drm: check whether clipped damage is empty
We were checking whether the damage region was empty before
clipping. However a non-empty damage region can become empty after
clipping. Instead, check whether the clipped region is empty.

Fixes: 4339c37f99 ("backend/drm: clip FB damage")
2024-10-26 13:07:10 +02:00
Kirill Primak
6006023a37 Use WLR_PRIVATE for private fields 2024-10-25 07:51:51 +03:00
David Turner
e51ce333bc scene: Apply output offset for direct scanout
When setting the primary buffer location for direct scanout, subtract
the offset of that output to put the buffer location in output-relative
coordinates.

Fixes #3910
2024-10-24 16:03:17 +01:00
Peng Liu
0ba1982488 backend/x11: delete xcb conn check NULL
xcb_connect always returns a non-NULL pointer
to a xcb_connection_t, even on failure.

Signed-off-by: Peng Liu <liupeng01@kylinos.cn>
2024-10-24 09:02:41 +00:00
Isaac Freund
da8f7a07ba
backend/headless: actually perform output test
Currently the headless backend does not actually implement the
wlr_output_test function, causing tests containing output state
unsupported by the headless backend to succeed while committing the same
state will always fail.

This commit fixes that by actually hooking up the already exisiting test
function.

References: https://codeberg.org/river/river/issues/1154
2024-10-24 10:49:08 +02:00
xurui
0d467ef9aa xdg-positioner: use enum_is_valid functions
Signed-off-by: xurui <xurui@kylinos.cn>
2024-10-23 18:52:34 +08:00
xurui
527b77b445 xdg-toplevel: use enum_is_valid functions
Signed-off-by: xurui <xurui@kylinos.cn>
2024-10-23 18:52:26 +08:00
David Turner
c87ab6465d Support direct scanout with src crop and dst boxes
Enable scene-tree direct scanout of a single buffer with various options
for scaling and source crop. This is intended to support direct scanout
for fullscreen video with/without scaling, letterboxing/pillarboxing
(e.g. 4:3 content on a 16:9 display), and source crop (e.g. when
1920x1088 planes are used for 1920x1080 video).

This works by explicitly specifying the source crop and destination box
for the primary buffer in the output state.  DRM atomic and libliftoff
backends will turn this into a crop and scale of the plane (assuming the
hardware supports that).  For the Wayland/X11/DRM-legacy backends I just
reject this so scanout will be disabled.

The previous behaviour is preserved if buffer_src_box and buffer_dst_box
are unset: the buffer is displayed at native size at the top-left of the
output with no crop.

The change to `struct wlr_output_state` makes this a binary breaking
change (but this works transparently for scene-tree compositors like
labwc after a recompile).
2024-10-22 18:28:09 +01:00
YaoBing Xiao
47fb00f66d wlr_linux_dmabuf_v1: log plane index in error messages for dma-buf
Signed-off-by: YaoBing Xiao <xiaoyaobing@uniontech.com>
2024-10-15 18:05:16 +00:00
Kenny Levinsen
ba0cc8eb05 backend/multi: Advance index on backend_commit
wlr_multi_backend sorts the states it is given and tries to perform
sequential backend-wide commits for each sub-backend with the states
that belong to it.

It did not manage the index correctly for the next iteration, so given N
states for a backend it would perform N backend-wide commits.

Clarify the logic by calculating a length rather than an end pointer and
update the index after each iteration.
2024-10-15 17:32:54 +02:00
Kirill Primak
7952658367 scene: crop output buffer damage before adding
This piece of logic was accidentally removed in
009515161b.
2024-10-15 12:59:40 +00:00
Simon Ser
3b3ed21e61 backend/drm: fix timeline feature flag on multi-GPU setups
This piece of code checks for multi-GPU renderer support, so it
needs to run after the renderer is initialized.

Fixes: 514c4b4cce ("backend: add timeline feature flag")
Closes: https://github.com/swaywm/sway/issues/8382
2024-10-12 17:12:29 +02:00
Alexander Orezechowski
402a862413 output: Change wlr_output_add_software_cursors_to_render_pass to take buffer coordinates
Since wlr_damage_ring now only works with buffer local coordinates, this
creates an inpedance mismatch for compositors that want to use this
function. Instead of compositors needing to the the conversion itself,
change thu function to take buffer local coordinates directly.
2024-10-12 07:30:11 +00:00
Simon Ser
1e949402b0 output: drop output timeline flag
This has been superseded by the backend-wide feature flag.
2024-10-11 17:11:35 +00:00
Simon Ser
785e340f01 scene: use backend-wide timeline feature flag 2024-10-11 17:11:35 +00:00
Simon Ser
186bdc8da4 output: use backend-wide timeline feature flag 2024-10-11 17:11:35 +00:00
Simon Ser
514c4b4cce backend: add timeline feature flag
The output feature flag has a flaw: it's not possible to check
whether the backend supports timelines during compositor
initialization when we need to figure out whether we want to enable
the linux-drm-syncobj-v1 protocol.

Introduce a backend-wide feature flag to indicate support for
timelines to address this defect.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3904
2024-10-11 17:11:35 +00:00
Alexander Orezechowski
8d8d5f5e94 scene: Rename some functions 2024-10-11 00:37:55 +03:00
Alexander Orezechowski
009515161b scene: Only accept buffer coordinates for damage 2024-10-11 00:37:55 +03:00
Alexander Orezechowski
f1b8937345 scene: Factor scaling into output transform function 2024-10-11 00:37:51 +03:00
Alexander Orezechowski
b9f0b9c766 scene: Apply damage ring as buffer local coordinates 2024-10-11 00:37:51 +03:00
Alexander Orezechowski
831e7fc7ee damage_ring: Add notes about damage being in buffer local coordinates 2024-10-11 00:37:48 +03:00
Alexander Orzechowski
79e063035c damage_ring: Remove wlr_damage_ring_set_bounds
This wasn't that great:
1. Now that damage ring tracks damage across actual wlr_buffer objects,
   it can use the buffer size to do any sort of cropping that needs to
   happen.
2. The damage ring size really should be the size of the transformed
   size of the output. Compositors currently have to call
   `wlr_damage_ring_set_bounds()` where it might not be clear when to
   call the function. Compositors can just check against the actual
   output bounds that they care about when processing the damage.

Fixes: #3891
2024-10-09 06:31:46 +00:00
Alexander Orzechowski
6202580b7b damage_ring: Stop using ring->{width, height} 2024-10-09 06:31:46 +00:00
Alexander Orzechowski
502eb38d80 damage_ring: Remove return value of wlr_damage_ring_add/wlr_damage_ring_add_box
Compositors should compute whether the damage is part of the output
themselves.
2024-10-09 06:31:46 +00:00
Alexander Orzechowski
fbafd8ed94 wlr_damage_ring: Clamp damage region to buffer size 2024-10-09 06:31:46 +00:00
Alexander Orzechowski
9904f160af scene: Don't rely on return value of wlr_damage_ring_add 2024-10-09 06:31:46 +00:00
Alexander Orzechowski
b8418b7b91 scene: Use wlr_output_transformed_resolution 2024-10-09 06:31:46 +00:00
Kirill Primak
dd8f4913a4 subcompositor: drop unused subsurface state 2024-10-08 19:18:38 +03:00
Simon Ser
6ada67da9b xwayland/xwm: implement somewhat asynchronous request flushing
Instead of calling xcb_flush() directly, wait until the FD is
writable.

Ideally we'd have a non-blocking variant instead of xcb_flush(),
but libxcb doesn't have this. Also libxcb blocks when its internal
buffer is full, but not much we can do here.
2024-10-07 21:51:15 +03:00
Simon Ser
c9fe96102d xwayland/xwm: extract read loop to separate function 2024-10-07 21:47:00 +03:00
Simon Ser
95d25d833f keyboard: add utilities for pointer keys
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3803
2024-10-07 18:41:07 +00:00
Simon Ser
7ce868bcf6 render/vulkan: make VK_KHR_external_semaphore_fd optional
We already block instead of using sync_file when the driver
doesn't support import/export.
2024-10-03 19:24:30 +02:00
Kirill Primak
ab118042ea ext-foreign-toplevel-list: add _from_resource() 2024-10-01 18:59:18 +03:00
Simon Ser
3da6fac1f2 render/vulkan: check size when creating shm texture 2024-09-29 15:22:56 +00:00
Simon Ser
d55c175777 render/vulkan: log size when importing too-large DMA-BUF 2024-09-29 15:22:56 +00:00
Kirill Primak
a8d1e5273a linux-dmabuf-v1: use static_assert 2024-09-24 19:56:36 +00:00
Kirill Primak
56d69320c7 pointer: release pressed buttons on destroy 2024-09-24 19:53:54 +00:00
Kirill Primak
c752270be7 tinywl: drop focused surface check for interactive ops
This serves as a bad example: compositors should validate serials from
events instead. Also, the current implementation segfaults if
focused_surface is NULL.
2024-09-20 10:48:32 +00:00
YaoBing Xiao
7debaced03 x11/backend: Optimize query_version error handling
Signed-off-by: YaoBing Xiao <xiaoyaobing@uniontech.com>
2024-09-12 17:52:26 +08:00
llyyr
bf0cac12a3 input-method-v2: set no keymap format if no keymap is set
Also don't copy the keymap each time it gets sent to a client
2024-09-11 15:09:56 +05:30
YaoBing Xiao
04525e6f82 wlr_screencopy_v1: send fine-grained damage events
Signed-off-by: YaoBing Xiao <xiaoyaobing@uniontech.com>
2024-09-10 20:54:04 +08:00
Kenny Levinsen
96ad414ec9 backend/drm: Remove call to CRTC realloc on scan
After a connector scan, new connectors might have appeared and old ones
gone away. At this point, old CRTC allocations are already gone, while
new allocations are not yet needed. Skip the call.
2024-09-09 13:22:56 +00:00
YaoBing Xiao
9f7ab85718 tearing_control_v1: Free the wlr_tearing_control_v1 on error
Signed-off-by: YaoBing Xiao <xiaoyaobing@uniontech.com>
2024-09-09 16:16:56 +08:00
Simon Ser
1a7981f7c9 compositor: document surface events 2024-09-05 19:49:20 +02:00
Simon Ser
234d31f138 backend/drm: improve doc comment for match_connectors_with_crtcs_()
- Add general description
- Mention the computer theory problem that this is solving
- More wording cleanup
2024-09-02 17:12:32 +00:00
Kenny Levinsen
beb9a9ad0a linux-drm-syncobj-v1: Skip release if there is no timeline
If a surface with an existing buffer has a syncobj surface state created
without committing a new buffer with associated timelines, callers will
see the surface as having a syncobj state and may try to use it, but
calling the signal_release_with_buffer helper at this time will assert
on the lacking release timeline.

As this is a valid situation, remove the assert and replace it with an
early return so that callers do not need to explicitly check for the
presence of valid timelines.

Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3895
2024-08-28 18:44:46 +02:00
YaoBing Xiao
0db4df4c8e gles2/pass: remove duplicate variable declarations 2024-08-27 20:25:18 +08:00
Simon Ser
0d6284eb62 backend/drm: add explicit sync support to libliftoff interface 2024-08-26 18:21:50 +02:00
Simon Ser
d7223eae02 backend/drm: add explicit sync support to multi-GPU blits 2024-08-26 18:09:27 +02:00
Alexander Orzechowski
3187479c07 render/color: Invert ownership model of color_transform types.
Color transform can have multiple types and these different types
want to store different metadata. We previously stored this metadata
directly on wlr_color_transform even for transforms that don't use it.

Instead, let's take the prior art from wlr_scene where each scene node
is built on a base node. Notice how wlr_color_transform_lut3d now has
a `struct wlr_color_transform base`. This is advantageous in multiple
ways:

1. We don't allocate memory for metadata that will never be used.
2. This is more type safe: Compositors can pass around a
struct wlr_color_transform_lut3d if they know they only want to use a
3d_lut.
3. This is more scalable. As we add more transform types, we don't have
to keep growing a monolithic struct.
2024-08-24 14:33:22 -04:00
Simon Ser
fa2abbeefb render/color: return tranform in wlr_color_transform_ref()
This is more consistent with the rest of the wlroots APIs and is
more concise.
2024-08-24 11:07:58 +02:00
YaoBing Xiao
a5aae69b2a backend/drm: remove unnecessary semicolons 2024-08-24 08:36:46 +00:00
Alexander Orzechowski
52afedadea wlr_scene: Assert wlr_scene_rect has nonnegative dimensions 2024-08-23 16:53:32 -04:00
Simon Ser
52dce29e06 render/vulkan: use non-coherent memory for read_pixels()
The spec for VkMemoryPropertyFlagBits says:

> device coherent accesses may be slower than equivalent accesses
> without device coherence [...] it is generally inadvisable to
> use device coherent or device uncached memory except when really
> needed

We don't really need coherent memory so let's not require it and
invalidate the memory range after mapping instead.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3868
2024-08-22 18:19:22 +02:00
Simon Ser
5432108846 backend/drm: drop SKIP in match_connectors_with_crtcs()
It's unused.
2024-08-21 22:25:19 +02:00
Simon Ser
5f3b99bbed backend/drm: rename i param in match_connectors_with_crtcs_()
Use a more descriptive name to make it clear what kind of index
this is.
2024-08-21 22:25:19 +02:00
Simon Ser
1e03719361 backend/drm: drop match_connectors_with_crtcs() return value
It's unused.
2024-08-21 22:25:19 +02:00
Simon Ser
0bf642d246 backend/drm: use more descriptive names for match_obj()
This function is only used for connectors and CRTCs, so instead of
the abstract "obj"/"resource" wording, just use the concrete names.
2024-08-21 22:25:19 +02:00
Simon Ser
d2a5dbe104 backend/drm: use CRTCs in-order
When lighting up a new connector, we'd use the last CRTC instead of the
first one. This causes issues because drivers have the expectation that
userspace will match CRTCs to connectors in-order [1].

The order has regressed a long time ago in 5b13b8a12c ("backend/drm:
consider continue not using resources"). That commit was a fix to
avoid moving a connector between CRTCs [2]. Revert that commit and
use a different approach: even if we've found a solution, always try
not using a CRTC in the hope that we'll find another solution with
less CRTC replacements.

[1]: https://lore.kernel.org/dri-devel/20240612141903.17219-2-ville.syrjala@linux.intel.com/
[2]: https://github.com/swaywm/wlroots/issues/1230

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3098
2024-08-21 15:17:04 +00:00
YaoBing Xiao
43554c1966 types/wlr_output: removing the useless pointer 2024-08-21 14:04:24 +00:00
Kirill Primak
cf93d31736 scene: resize damage ring on geometry update 2024-08-21 13:53:34 +00:00
Alexander Orzechowski
bfcaa4bc44 swapchain, damage_ring: Drop buffer age 2024-08-21 09:45:54 -04:00
Alexander Orzechowski
eebaca8dbf output/render: Drop buffer age from wlr_output_begin_render_pass
For compositors wanting to damage track, `wlr_damage_ring_rotate_buffer()`
should be used and the damage should be set on the passed state.
2024-08-21 09:45:54 -04:00
Kirill Primak
62cc96b3a4 scene: update output geom on commit after dropping pending damage
Otherwise the whole output damage gets ignored.
2024-08-20 19:27:13 +03:00
Simon Ser
098cb9b7a3 xdg-shell: add wlr_xdg_toplevel_configure()
A struct wlr_xdg_toplevel_configure is passed in with the whole
state. This makes it a lot clearer that the size and WM state are
always sent to the client.
2024-08-20 09:15:39 +00:00
Simon Ser
8582b45c9e xdg-shell: document struct wlr_xdg_toplevel_configure 2024-08-20 09:15:39 +00:00
Dudemanguy
3d2f09bace backend/drm: fix a use-after-free
The page_flip can be destroyed, but it is unconditionally accessed later
on when setting present_flags. Fix this by simply setting the
present_flags before the page_flip gets destroyed.
2024-08-19 13:46:41 -04:00
Kirill Primak
b4f077a596 drm-syncobj-v1: remove buffer release listener on signaller destroy 2024-08-19 15:49:01 +00:00
YaoBing Xiao
3048fb3fc6 render/egl: Release devices before return 2024-08-19 23:18:11 +08:00
Kenny Levinsen
5df2b34d2b allocator/gbm: Reset errno before gbm_bo_create
Not all paths in GBM set errno properly on error. Reset it to zero
before calling GBM to avoid accidentally printing a garbage error.
2024-08-19 14:59:44 +02:00
Kenny Levinsen
ccd4703207 allocator/gbm: Log errno if gbm_bo_create fails 2024-08-19 14:41:09 +02:00
Kirill Primak
a0450d219f layer-shell: introduce wlr_layer_surface_v1_get_exclusive_edge() 2024-08-18 01:02:08 +00:00
Kirill Primak
270e6f4ebb layer-shell: add v5 support 2024-08-18 01:02:08 +00:00
Kirill Primak
e88988e364 keyboard: simplify releasing keys on finish 2024-08-15 18:33:00 +00:00
Kirill Primak
310a5eb61c backend/wayland: simplify wl_keyboard.{enter,leave} processing 2024-08-15 18:33:00 +00:00
Isaac Freund
08495d2596 backend/drm: don't set vsync present flag if page flip was async 2024-08-15 15:11:10 +00:00
Kirill Primak
3103ea3af9 backend/wayland: process initial events from globals correctly
Previous logic could lead wlr_wl_backend.drm_render_name being written
to twice, causing a memory leak. This commit fixes the race condition.
2024-08-15 12:52:05 +03:00
Kirill Primak
ee21deb458 linux-drm-syncobj: add missing decls in the header 2024-08-15 09:02:04 +00:00
Leonardo Hernández Hernández
baaec88e2f
linux-drm-syncobj-v1: actually use the requested version 2024-08-14 23:03:14 -06:00
Kirill Primak
4da4269d8f seat/pointer: reset pressed buttons on "grab-compatible" focus change
Fixes: 08e779bd85
2024-08-14 22:43:44 +03:00
Kirill Primak
5c98d1a04a xdg-surface: fix window geometry handling
It was completely wrong: according to the protocol, the effective
geometry is only updated on commit time if there pending state has
new state from xdg_surface.set_window_geometry or
xdg_surface.set_window_geometry has never been sent at all.

This commit adds wlr_xdg_surface.geometry which correctly matches the
effective window geometry and removes now-useless
wlr_xdg_surface_get_geometry().
2024-08-14 18:52:13 +00:00
Kirill Primak
a1298580cc compositor: add surface role map hook 2024-08-14 18:52:13 +00:00
Alexander Orzechowski
515275ee72 wlr_scene: Introduce wlr_scene_set_gamma_control_manager_v1 2024-08-14 13:18:56 -04:00
Alexander Orzechowski
23202e192c wlr_scene: Introduce wlr_scene_output_needs_frame
It seems that some scene compositors want to avoid wlr_scene_output_commit
and use the lower lever wlr_scene_output_build_state. However, build
state does not early return if a frame is not needed so compositors will
implement the check themselves. Let's add a helper function that compositors
can use to implement the check.

Technically pending_commit_damage is a private interface, so this lets
compositors not interface with private interfaces to implement the check.
2024-08-14 12:51:15 -04:00
Alexander Orzechowski
2463a4723e wlr_scene: Ensure we restack all xwayland surfaces to the bottom when scene node is disabled 2024-08-14 11:53:20 -04:00
Alexander Orzechowski
291df10fe5 wlr_scene: Extract function to get xwayland surface from node 2024-08-14 11:53:05 -04:00
Alexander Orzechowski
235c8e922a Revert "wlr_scene: Ensure scene_node_update is updating entire node."
This reverts commit 66d96d244c.
2024-08-14 11:51:44 -04:00
Kirill Primak
70c99460ca pointer-constraints: don't init/finish current/pending states
wlr_surface_synced does it automatically.

Reported-by: llyyr <llyyr.public@gmail.com>
2024-08-14 15:41:31 +00:00
Alexander Orzechowski
4f1104654f wlr_scene: Fix WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT when output is transformed 2024-08-14 11:23:30 -04:00
Alexander Orzechowski
14e1987f50 wlr_scene: Don't special case swapchain buffers
This fixes direct scanout VRR. As direct scanout buffers are not part
of the swapchain, we would mistakenly union instead of subtract the damage
meaning it will just accumulate indefinitely.

The reason for this existing in the first place is for compositors that
might want to sidestep scene and commit their own buffers to the output.
In this case, scene could theoretically acknowledge that and update the
damage. Except, this really didn't work because WLR_OUTPUT_STATE_DAMAGE
would need to be defined which is optional. This patch also properly
acknowledges commits without damage.

In the use case of a weird compositor that might want to sidestep scene,
they can just trash the damage ring themselves.

Fixes: #3871
2024-08-14 11:23:02 -04:00
Alexander Orzechowski
3e1358fec9 wlr_scene: Inline output_state_apply_damage 2024-08-14 11:23:02 -04:00
Alexander Orzechowski
147c5c37e3 wlr_scene: Immediately apply pending output commit damage
There were two problems with the old implementation:
1. wlr_scene_output_commit would bail early if a frame wasn't requested
and there was no commit damage, however commit damage could never accumulate
until rendering happens. The check was subtly wrong as a result.
2. Previously, we would fill the pending commit damage based on the
current state of the damage ring. However, during direct scanout, the
damage would accumulate which would mean we would submit damage from
previous frames even if we didn't need to.
2024-08-14 11:23:01 -04:00
Alexander Orzechowski
78dfa4f06d wlr_scene: Funnel all damage operations through scene_output_damage
We want to add logic to this function later
2024-08-14 11:21:00 -04:00
Kirill Primak
c52e01e85f xdg-popup: don't set a role resource destroy handler
wlr_xdg_surface tracks role resource destruction itself.
2024-08-14 13:32:16 +00:00
Kirill Primak
6c8eabcecd xdg-foreign: clean up
This commit removes extra wlr_xdg_toplevel_set_parent() calls,
simplifies wlr_surface->wlr_xdg_toplevel conversion logic, makes related
structures store wlr_xdg_toplevel objects directly instead of
wlr_surface objects, and improves the code style.
2024-08-14 15:56:36 +03:00
zhoulei
2c64f36e88 xwayland/xwm: listen shell destroy signal
Otherwise we got invaild write in wl_list_remove.

Fixes: e209fe2d0 ("Fix memory leak in xwayland.c")

Signed-off-by: zhoulei <zhoulei@kylinos.cn>
2024-08-13 11:33:04 +08:00
Simon Ser
e6dbe4580e render/gles2: check for DRM_CAP_SYNCOBJ_TIMELINE
Before advertising support for timelines, check for
DRM_CAP_SYNCOBJ_TIMELINE. Without this, the user cannot
create/import drm_syncobj timelines.

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4715#note_2523517
2024-08-12 22:37:34 +00:00
Consolatis
adf9d8b0be ext-foreign-toplevel-list: use correct interface and add missing handler
Without this patch, a client calling handle.destroy() will trigger
an assert in libwayland due to a NULL pointer for the destroy handler.

Also implement a missing .destroy handler for the manager itself
and delay destruction of the manager resource from the .stop handler
to the .destroy handler.
2024-08-12 22:31:31 +02:00
project-repo
e209fe2d05 Fix memory leak in xwayland.c 2024-08-12 14:06:10 +00:00
project-repo
3cae2a2c01 Fix memory leak in drm.c 2024-08-12 14:06:10 +00:00
John Lindgren
6214144735 xwayland: add wlr_xwayland_surface_has_window_type()
The infrastructure to read _NET_WM_WINDOW_TYPE already exists in wlroots
(it's used for example in wlr_xwayland_or_surface_wants_focus()). But
the window type isn't easily accessible to the compositor because the
atoms to compare against are private to xwm.c.

labwc has recently gone to a fair amount of effort (including opening a
whole new xcb connection) just to get the needed window type atoms:
a04b394e59

It seems much cleaner to add the remaining few (3) atoms to wlroots and
implement a shared function which can be used by any wlroots compositor.

v2: naming updates
2024-08-10 16:10:34 +00:00
Alistair Buxton
71cc47b859 Fix Meson version required for C23 support
Attempting to build with Meson 1.3.2 (current version in Ubuntu 24.04 LTS) gives the following error:

    meson.build:1:0: ERROR: Unknown C std ['c23'].

This is because C23 support was not added until Meson 1.4.0.

See:

https://github.com/mesonbuild/meson/blob/1.3.2/mesonbuild/compilers/c.py#L59
https://github.com/mesonbuild/meson/blob/1.4.0/mesonbuild/compilers/c.py#L49
2024-08-10 15:54:23 +00:00
Kirill Primak
08e779bd85 seat/pointer: reset pressed buttons on focus change
This fixes two problems:
- A surface could get unexpected release events for buttons pressed
  while other surface was focused;
- Clearing focus while a button is pressed would lead to the button
  getting "stuck".

Fixes: 8730ca9661
2024-08-10 09:55:17 +03:00
Consolatis
0a388a14f1 xwm: supply wlr_xwayland pointer to user_event_handler callback
Previously it was supplying a pointer to private struct wlr_xwm
which was useless for compositors. The wlr_xwayland pointer in
contrast does have a generic data field and thus can be used by
compositors to attach their own pointer.

Additionally change the return value from int to bool.
2024-08-08 17:00:08 +00:00
Consolatis
20997df416 xwm: add request_close signal
This is especially useful if the compositor wants to support X11
panels to control windows. Without the signal a compositor does
have to hook into the user_event_handler callback but that would
not change the _NET_SUPPORTED root window property.
2024-08-08 15:49:11 +00:00
Simon Ser
df4a1d94e2 scene: fix typo in wlr_scene_surface_create() docs 2024-08-08 09:16:46 +02:00
Simon Ser
1c7e1bcc28 scene: use numbers instead of stars for doc refs
I find these slightly more readable (because the whole comment has
a stars column on the left side).
2024-08-08 09:14:48 +02:00
Alexander Orzechowski
14446216f4 wlr_scene: Improve wlr_scene_surface_create documentation 2024-08-07 16:47:27 -04:00
Alexander Orzechowski
1133bc15ce wlr_scene: Transparently restack xwayland surfaces
Scene will now automatically restack xwayland windows. Scene
compositors should drop calls to wlr_xwayland_surface_restack()
2024-08-06 20:00:01 -04:00
Alexander Orzechowski
66d96d244c wlr_scene: Ensure scene_node_update is updating entire node.
The old logic might not update the entire scene node when a node is
disabled. It would only consider the damage last time (the damage was
based on the visible region of the node).

It's important that we update the entire node region because xwayland
stacking will depend on this.
2024-08-06 20:00:01 -04:00
Alexander Orzechowski
823a64bf7d wlr_scene: Store update box in update data
We'll use it later
2024-08-06 20:00:01 -04:00
Alexander Orzechowski
4b4ca11f6f util/box: Introduce wlr_box_contains_box 2024-08-06 20:00:01 -04:00
Kirill Primak
8730ca9661 seat/pointer: handle duplicate presses/releases correctly 2024-08-06 22:53:08 +00:00
Simon Ser
775817e278 render: add WLR_RENDER_NO_EXPLICIT_SYNC env var
This can be handy to figure out if a bug is due to explicit sync.
2024-08-06 17:37:06 +00:00
Simon Ser
738bbf01ee cursor: add support for linux-drm-syncobj-v1 2024-08-06 17:37:06 +00:00
Simon Ser
5f88635118 scene: add explicit synchronization for rendered buffers 2024-08-06 17:37:06 +00:00
Simon Ser
9e9636f675 scene: add support for linux-drm-syncobj-v1 2024-08-06 17:37:06 +00:00
Simon Ser
850dd7a792 linux-drm-syncobj-v1: add helper to signal on buffer release 2024-08-06 17:37:06 +00:00
Simon Ser
c7035da5e2 scene: add timeline point to wlr_scene_buffer_set_buffer() options 2024-08-06 17:37:06 +00:00
Simon Ser
48f0902a36 scene: add wlr_scene_buffer_set_buffer_with_options()
This is an extensible version of wlr_scene_buffer_set_buffer().
2024-08-06 17:37:06 +00:00
Simon Ser
edb867bc05 render/drm_syncobj: add wlr_drm_syncobj_timeline_export() 2024-08-06 17:37:06 +00:00
Simon Ser
5552de65f8 render/drm_syncobj: add wlr_drm_syncobj_timeline_transfer() 2024-08-06 17:37:06 +00:00
Simon Ser
3067e45c2e backend/drm: add support for explicit sync APIs 2024-08-06 17:37:06 +00:00
Simon Ser
1ad42bea99 output: add explicit sync API 2024-08-06 17:37:06 +00:00
Simon Ser
d2374b3e4e render/gles2: implement explicit sync API 2024-08-06 17:37:06 +00:00
Simon Ser
19ffbfe356 render/egl: add support for explicit sync extensions 2024-08-06 17:37:06 +00:00
Simon Ser
a1635fdb76 render: add explicit sync API 2024-08-06 17:37:06 +00:00
Alexander Orzechowski
4481c6b243 wlr_scene: Force blend mode to PREMULTIPLIED if calculate visibility is disabled
We do it here so WLR_SCENE_HIGHLIGHT_TRANSPARENT_REGION doesn't break
2024-08-05 14:46:30 -04:00
Kirill Primak
6261bd9684 .mailmap: add myself 2024-08-05 19:32:24 +03:00
Philipp Kaeser
d400b4587a wlr_scene: Add 'struct' to comment in wlr_scene_create() to permit auto-linkify. 2024-08-04 11:57:27 +02:00
Philipp Kaeser
42df9414fb wlr_scene: Add documentation to wlr_scene_create about how to destroy the allocated resources. 2024-08-04 09:49:58 +00:00
Kirill Primak
7e13dfdd4d xwayland: remove stray empty line 2024-08-02 17:52:19 +03:00
Consolatis
ceb4fcedca xwm: expose individual axis for _set_maximized()
This allows compositors to support both axis individually.
To keep existing behavior, compositors can supply the same
maximized state for both axis.
2024-08-02 14:46:37 +00:00
John Lindgren
f9199bb6d4 xwayland: set focus to XCB_POINTER_ROOT rather than XCB_NONE
Fixes an issue with keyboard grabs not working when no XWayland windows
are focused.
2024-08-01 13:19:24 +00:00
John Lindgren
5083efe18b xwayland: add wlr_xwayland_surface_offer_focus()
In labwc, we have had trouble with XWayland windows using the Globally
Active input model (see wlr_xwayland_icccm_input_model()). Under
traditional X11, these windows do not expect to be given focus directly
by the window manager; rather, the WM sends them a WM_TAKE_FOCUS message
prompting the client to take focus voluntarily.

Currently, these clients are difficult to support with wlroots, because
wlr_xwayland_surface_activate() assumes the client window will always
accept the keyboard focus after being sent WM_TAKE_FOCUS. Some Globally
Active client windows (e.g. panels/toolbars) don't want to be focused.
It's useless at best to focus them, and might even make them misbehave.
Others do need keyboard focus to be functional -- and there doesn't seem
to be any reliable way to know this in advance.

Adding wlr_xwayland_surface_offer_focus() allows the compositor to send
WM_TAKE_FOCUS to a client window supporting it and then see whether the
client accepts or ignores the offer. If it accepts, the surface will emit
the focus_in signal notifying the compositor that it has received focus.

This is entirely opt-in. A compositor that doesn't want to use the new
function can continue to call wlr_xwayland_surface_activate() directly
just as before.
2024-08-01 13:19:24 +00:00
John Lindgren
eb5312022a xwayland: add focus_in and grab_focus events
Allows the compositor to know when the XWayland focus changes.
2024-08-01 13:19:24 +00:00
John Lindgren
aa1163e640 xwayland: factor out xwm_set_focused_window()
Currently _NET_WM_STATE is updated in xwm_focus_window() but
_NET_ACTIVE_WINDOW is updated in xwm_surface_activate(). In some cases
(for example, client-initiated focus changes) the two properties can get
out of sync.

Factor out a new function which updates both properties in sync.

Adjust the logic in xwm_handle_focus_in() to call either
xwm_focus_window() or xwm_set_focused_window(), or neither, as
appropriate.
2024-08-01 13:19:24 +00:00
Kirill Primak
de574ac098 output-power-management: send zwlr_output_power_v1.failed on output destroy
From the event description:

This event indicates that the output power management mode control is no
longer valid. This can happen for a number of reasons, including:
<...>
- The output disappeared
2024-08-01 15:24:20 +03:00
BiRD
42673a2821 tinywl: Update .gitignore 2024-07-29 11:39:34 -04:00
BiRD
cd2cf1bafb ci: Remove package 'gmake' 2024-07-29 11:39:34 -04:00
BiRD
f16a3c1180 tinywl: Edit Makefile for bmake compatibility
- Replace 'shell' calls with '!=' assignments
- Add default target 'all'
- Don't use var '$<' for non-sources
2024-07-29 11:39:34 -04:00
chenyongxing
015bb8512e render/vulkan: Fix draw rect clip region invalid in blend none mod 2024-07-16 14:24:46 +08:00
Isaac Freund
7550e483ae docs: update comments for wlr_output API changes
The old wlr_output_{commit,test}() functions are still mentioned in
multiple places.
2024-07-15 12:30:06 +00:00
Isaac Freund
2a8a23c467 wlr_output: remove dead function 2024-07-15 12:30:06 +00:00
Simon Ser
179ed7c296 build: use fs.relative_to() instead of hand-rolled logic
Meson has introduced a relative_to() function [1] in its fs module
since version 1.3.

[1]: https://mesonbuild.com/Fs-module.html#relative_to
2024-07-14 21:42:26 +00:00
Bill Li
22adc65586 ci: use package x11-servers/xwayland instead of x11-servers/xwayland-devel 2024-07-15 05:24:31 +08:00
Kirill Primak
e17916d413 Rename wlr_surface_get_extends() to wlr_surface_get_extents()
Extend (verb): cause to cover a wider area; make larger.
2024-07-13 19:56:58 +00:00
Isaac Freund
d3b7e040af wlr_xwayland_surface: fix prefix of two functions
Since we're breaking this API anyways, replace the ambiguous "or" in the
function name with the explicit "override redirect" to avoid confusion.
2024-07-13 19:56:58 +00:00
Isaac Freund
5ecbd23c1d wlr_surface: fix argument order consistency
This swaps the argument order of wlr_surface_accepts_touch() and
wlr_surface_accepts_tablet_v2(), putting the wlr_surface argument first
as should be the case for functions namespaced with wlr_surface_*.
2024-07-13 19:56:58 +00:00
Simon Ser
b10516e1e8 build: bump version to 0.19.0-dev 2024-07-12 23:48:02 +02:00
323 changed files with 13577 additions and 3702 deletions

View file

@ -20,8 +20,13 @@ packages:
- xwayland-dev
- libseat-dev
- hwdata-dev
# for docs
- go
- zip
sources:
- https://gitlab.freedesktop.org/wlroots/wlroots.git
artifacts:
- public.zip
tasks:
- setup: |
cd wlroots
@ -37,3 +42,16 @@ tasks:
- tinywl: |
cd wlroots/tinywl
make
- docs: |
go install 'codeberg.org/emersion/gyosu@latest'
include_dir="$(echo /usr/local/include/wlroots-*)"
~/go/bin/gyosu \
-DWLR_USE_UNSTABLE \
$(pkg-config --cflags-only-I $(basename "$include_dir")) \
-Iwlroots/build/protocol/ \
-fexported-symbols='wlr_*' -fexported-symbols='WLR_*' \
-ffile-prefix-map="$include_dir/"= \
-fsite-name=wlroots \
-o public \
"$include_dir/wlr/"
zip -r ~/public.zip public/

View file

@ -41,9 +41,10 @@ tasks:
cd wlroots/build-gcc/tinywl
sudo modprobe vkms
udevadm settle
card="/dev/dri/$(ls /sys/devices/faux/vkms/drm/ | grep ^card)"
export WLR_BACKENDS=drm
export WLR_RENDERER=pixman
export WLR_DRM_DEVICES=/dev/dri/by-path/platform-vkms-card
export WLR_DRM_DEVICES="$card"
export UBSAN_OPTIONS=halt_on_error=1
sudo chmod ugo+rw /dev/dri/by-path/platform-vkms-card
sudo chmod ugo+rw "$card"
sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ]

View file

@ -20,19 +20,18 @@ packages:
- x11/xcb-util-errors
- x11/xcb-util-renderutil
- x11/xcb-util-wm
- x11-servers/xwayland-devel
- x11-servers/xwayland
- sysutils/libdisplay-info
- sysutils/seatd
- gmake
- hwdata
sources:
- https://gitlab.freedesktop.org/wlroots/wlroots.git
tasks:
- wlroots: |
cd wlroots
meson setup build --fatal-meson-warnings -Dauto_features=enabled
meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dallocators=gbm
ninja -C build
sudo ninja -C build install
- tinywl: |
cd wlroots/tinywl
gmake
make

3
.gitignore vendored
View file

@ -1 +1,2 @@
/subprojects/
/subprojects/*
!/subprojects/*.wrap

View file

@ -1,6 +1,7 @@
include: https://git.sr.ht/~emersion/dalligi/blob/master/templates/multi.yml
alpine:
extends: .dalligi
pages: true
archlinux:
extends: .dalligi
freebsd:

View file

@ -1 +1,2 @@
Isaac Freund <mail@isaacfreund.com> <ifreund@ifreund.xyz>
Kirill Primak <vyivel@eclair.cafe> <vyivel@posteo.net>

View file

@ -213,6 +213,27 @@ reinitialized to be used again.
it, and free the memory. Such functions should always be able to accept a NULL
pointer.
If the object has signals, the destructor function must assert that their
listener lists are empty.
```c
void wlr_thing_init(struct wlr_thing *thing) {
*thing = (struct wlr_thing){
// ...
};
wl_signal_init(&thing->events.destroy);
wl_signal_init(&thing->events.foo);
}
void wlr_thing_finish(struct wlr_thing *thing) {
wl_signal_emit_mutable(&thing->events.destroy, NULL);
assert(wl_list_empty(&thing->events.destroy.listener_list));
assert(wl_list_empty(&thing->events.foo.listener_list));
}
```
### Error Codes
For functions not returning a value, they should return a (stdbool.h) bool to
@ -237,6 +258,13 @@ used and `#undef` them after.
* Document the contents and container of a `struct wl_list` with a
`// content.link` and `// container.list` comment.
### Private fields
Wrap private fields of public structures with `struct { … } WLR_PRIVATE`. This
ensures that compositor authors don't use them by accident. Within wlroots
`WLR_PRIVATE` is expanded to nothing, so private fields are accessed in the same
way as public ones.
### Safety
* Avoid string manipulation functions which don't take the size of the
@ -325,12 +353,14 @@ struct wlr_compositor {
struct wl_global *global;
struct wl_listener display_destroy;
struct {
struct wl_signal new_surface;
struct wl_signal destroy;
} events;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
```

View file

@ -1,5 +1,4 @@
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -12,9 +11,6 @@
#include <wlr/config.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>
#include "backend/backend.h"
#include "backend/multi.h"
#include "render/allocator/allocator.h"
#include "types/wlr_output.h"
#include "util/env.h"
#include "util/time.h"
@ -50,6 +46,10 @@ void wlr_backend_init(struct wlr_backend *backend,
void wlr_backend_finish(struct wlr_backend *backend) {
wl_signal_emit_mutable(&backend->events.destroy, backend);
assert(wl_list_empty(&backend->events.destroy.listener_list));
assert(wl_list_empty(&backend->events.new_input.listener_list));
assert(wl_list_empty(&backend->events.new_output.listener_list));
}
bool wlr_backend_start(struct wlr_backend *backend) {
@ -121,14 +121,6 @@ int wlr_backend_get_drm_fd(struct wlr_backend *backend) {
return backend->impl->get_drm_fd(backend);
}
uint32_t backend_get_buffer_caps(struct wlr_backend *backend) {
if (!backend->impl->get_buffer_caps) {
return 0;
}
return backend->impl->get_buffer_caps(backend);
}
static size_t parse_outputs_env(const char *name) {
const char *outputs_str = getenv(name);
if (outputs_str == NULL) {
@ -493,5 +485,10 @@ bool wlr_backend_commit(struct wlr_backend *backend,
output_apply_commit(state->output, &state->base);
}
for (size_t i = 0; i < states_len; i++) {
const struct wlr_backend_output_state *state = &states[i];
output_send_commit_event(state->output, &state->base);
}
return true;
}

View file

@ -1,13 +1,16 @@
#include <drm_fourcc.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "backend/drm/drm.h"
#include "backend/drm/fb.h"
#include "backend/drm/iface.h"
#include "backend/drm/util.h"
#include "render/color.h"
static char *atomic_commit_flags_str(uint32_t flags) {
const char *const l[] = {
@ -152,18 +155,20 @@ static bool create_gamma_lut_blob(struct wlr_drm_backend *drm,
bool create_fb_damage_clips_blob(struct wlr_drm_backend *drm,
int width, int height, const pixman_region32_t *damage, uint32_t *blob_id) {
if (!pixman_region32_not_empty(damage)) {
*blob_id = 0;
return true;
}
pixman_region32_t clipped;
pixman_region32_init(&clipped);
pixman_region32_intersect_rect(&clipped, damage, 0, 0, width, height);
int rects_len;
const pixman_box32_t *rects = pixman_region32_rectangles(&clipped, &rects_len);
int ret = drmModeCreatePropertyBlob(drm->fd, rects, sizeof(*rects) * rects_len, blob_id);
int ret;
if (rects_len > 0) {
ret = drmModeCreatePropertyBlob(drm->fd, rects, sizeof(*rects) * rects_len, blob_id);
} else {
ret = 0;
*blob_id = 0;
}
pixman_region32_fini(&clipped);
if (ret != 0) {
wlr_log_errno(WLR_ERROR, "Failed to create FB_DAMAGE_CLIPS property blob");
@ -173,6 +178,85 @@ bool create_fb_damage_clips_blob(struct wlr_drm_backend *drm,
return true;
}
static uint8_t convert_cta861_eotf(enum wlr_color_transfer_function tf) {
switch (tf) {
case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
abort(); // unsupported
case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
return 2;
case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
abort(); // unsupported
case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
abort(); // unsupported
case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
abort(); // unsupported
}
abort(); // unreachable
}
static uint16_t convert_cta861_color_coord(double v) {
if (v < 0) {
v = 0;
}
if (v > 1) {
v = 1;
}
return (uint16_t)round(v * 50000);
}
static bool create_hdr_output_metadata_blob(struct wlr_drm_backend *drm,
const struct wlr_output_image_description *img_desc, uint32_t *blob_id) {
if (img_desc == NULL) {
*blob_id = 0;
return true;
}
struct hdr_output_metadata metadata = {
.metadata_type = 0,
.hdmi_metadata_type1 = {
.eotf = convert_cta861_eotf(img_desc->transfer_function),
.metadata_type = 0,
.display_primaries = {
{
.x = convert_cta861_color_coord(img_desc->mastering_display_primaries.red.x),
.y = convert_cta861_color_coord(img_desc->mastering_display_primaries.red.y),
},
{
.x = convert_cta861_color_coord(img_desc->mastering_display_primaries.green.x),
.y = convert_cta861_color_coord(img_desc->mastering_display_primaries.green.y),
},
{
.x = convert_cta861_color_coord(img_desc->mastering_display_primaries.blue.x),
.y = convert_cta861_color_coord(img_desc->mastering_display_primaries.blue.y),
},
},
.white_point = {
.x = convert_cta861_color_coord(img_desc->mastering_display_primaries.white.x),
.y = convert_cta861_color_coord(img_desc->mastering_display_primaries.white.y),
},
.max_display_mastering_luminance = img_desc->mastering_luminance.max,
.min_display_mastering_luminance = img_desc->mastering_luminance.min * 0.0001,
.max_cll = img_desc->max_cll,
.max_fall = img_desc->max_fall,
},
};
if (drmModeCreatePropertyBlob(drm->fd, &metadata, sizeof(metadata), blob_id) != 0) {
wlr_log_errno(WLR_ERROR, "Failed to create HDR_OUTPUT_METADATA property");
return false;
}
return true;
}
static uint64_t convert_primaries_to_colorspace(uint32_t primaries) {
switch (primaries) {
case 0:
return 0; // Default
case WLR_COLOR_NAMED_PRIMARIES_BT2020:
return 9; // BT2020_RGB
}
abort(); // unreachable
}
static uint64_t max_bpc_for_format(uint32_t format) {
switch (format) {
case DRM_FORMAT_XRGB2101010:
@ -247,19 +331,25 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo
}
uint32_t gamma_lut = crtc->gamma_lut;
if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
if (state->base->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) {
size_t dim = 0;
uint16_t *lut = NULL;
if (state->base->color_transform != NULL) {
struct wlr_color_transform_lut_3x1d *tr =
color_transform_lut_3x1d_from_base(state->base->color_transform);
dim = tr->dim;
lut = tr->lut_3x1d;
}
// Fallback to legacy gamma interface when gamma properties are not
// available (can happen on older Intel GPUs that support gamma but not
// degamma).
if (crtc->props.gamma_lut == 0) {
if (!drm_legacy_crtc_set_gamma(drm, crtc,
state->base->gamma_lut_size,
state->base->gamma_lut)) {
if (!drm_legacy_crtc_set_gamma(drm, crtc, dim, lut)) {
return false;
}
} else {
if (!create_gamma_lut_blob(drm, state->base->gamma_lut_size,
state->base->gamma_lut, &gamma_lut)) {
if (!create_gamma_lut_blob(drm, dim, lut, &gamma_lut)) {
return false;
}
}
@ -272,6 +362,15 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo
state->primary_fb->wlr_buf->height, &state->base->damage, &fb_damage_clips);
}
int in_fence_fd = -1;
if (state->wait_timeline != NULL) {
in_fence_fd = wlr_drm_syncobj_timeline_export_sync_file(state->wait_timeline,
state->wait_point);
if (in_fence_fd < 0) {
return false;
}
}
bool prev_vrr_enabled =
output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED;
bool vrr_enabled = prev_vrr_enabled;
@ -282,10 +381,25 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo
vrr_enabled = state->base->adaptive_sync_enabled;
}
uint32_t colorspace = conn->colorspace;
if (state->base->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) {
colorspace = convert_primaries_to_colorspace(
state->base->image_description ? state->base->image_description->primaries : 0);
}
uint32_t hdr_output_metadata = conn->hdr_output_metadata;
if ((state->base->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) &&
!create_hdr_output_metadata_blob(drm, state->base->image_description, &hdr_output_metadata)) {
return false;
}
state->mode_id = mode_id;
state->gamma_lut = gamma_lut;
state->fb_damage_clips = fb_damage_clips;
state->primary_in_fence_fd = in_fence_fd;
state->vrr_enabled = vrr_enabled;
state->colorspace = colorspace;
state->hdr_output_metadata = hdr_output_metadata;
return true;
}
@ -300,11 +414,23 @@ void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) {
crtc->own_mode_id = true;
commit_blob(drm, &crtc->mode_id, state->mode_id);
commit_blob(drm, &crtc->gamma_lut, state->gamma_lut);
commit_blob(drm, &conn->hdr_output_metadata, state->hdr_output_metadata);
conn->output.adaptive_sync_status = state->vrr_enabled ?
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
destroy_blob(drm, state->fb_damage_clips);
if (state->primary_in_fence_fd >= 0) {
close(state->primary_in_fence_fd);
}
if (state->out_fence_fd >= 0) {
// TODO: error handling
wlr_drm_syncobj_timeline_import_sync_file(state->base->signal_timeline,
state->base->signal_point, state->out_fence_fd);
close(state->out_fence_fd);
}
conn->colorspace = state->colorspace;
}
void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) {
@ -314,8 +440,15 @@ void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state)
rollback_blob(drm, &crtc->mode_id, state->mode_id);
rollback_blob(drm, &crtc->gamma_lut, state->gamma_lut);
rollback_blob(drm, &conn->hdr_output_metadata, state->hdr_output_metadata);
destroy_blob(drm, state->fb_damage_clips);
if (state->primary_in_fence_fd >= 0) {
close(state->primary_in_fence_fd);
}
if (state->out_fence_fd >= 0) {
close(state->out_fence_fd);
}
}
static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) {
@ -327,7 +460,8 @@ static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) {
static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm,
struct wlr_drm_plane *plane, struct wlr_drm_fb *fb, uint32_t crtc_id,
int32_t x, int32_t y) {
const struct wlr_box *dst_box,
const struct wlr_fbox *src_box) {
uint32_t id = plane->id;
const struct wlr_drm_plane_props *props = &plane->props;
@ -337,28 +471,50 @@ static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm,
return;
}
uint32_t width = fb->wlr_buf->width;
uint32_t height = fb->wlr_buf->height;
// The src_* properties are in 16.16 fixed point
atomic_add(atom, id, props->src_x, 0);
atomic_add(atom, id, props->src_y, 0);
atomic_add(atom, id, props->src_w, (uint64_t)width << 16);
atomic_add(atom, id, props->src_h, (uint64_t)height << 16);
atomic_add(atom, id, props->crtc_w, width);
atomic_add(atom, id, props->crtc_h, height);
atomic_add(atom, id, props->src_x, src_box->x * (1 << 16));
atomic_add(atom, id, props->src_y, src_box->y * (1 << 16));
atomic_add(atom, id, props->src_w, src_box->width * (1 << 16));
atomic_add(atom, id, props->src_h, src_box->height * (1 << 16));
atomic_add(atom, id, props->fb_id, fb->id);
atomic_add(atom, id, props->crtc_id, crtc_id);
atomic_add(atom, id, props->crtc_x, (uint64_t)x);
atomic_add(atom, id, props->crtc_y, (uint64_t)y);
atomic_add(atom, id, props->crtc_x, dst_box->x);
atomic_add(atom, id, props->crtc_y, dst_box->y);
atomic_add(atom, id, props->crtc_w, dst_box->width);
atomic_add(atom, id, props->crtc_h, dst_box->height);
}
static bool supports_cursor_hotspots(const struct wlr_drm_plane* plane) {
static bool supports_cursor_hotspots(const struct wlr_drm_plane *plane) {
return plane->props.hotspot_x && plane->props.hotspot_y;
}
static void set_plane_in_fence_fd(struct atomic *atom,
struct wlr_drm_plane *plane, int sync_file_fd) {
if (!plane->props.in_fence_fd) {
wlr_log(WLR_ERROR, "Plane %"PRIu32 " is missing the IN_FENCE_FD property",
plane->id);
atom->failed = true;
return;
}
atomic_add(atom, plane->id, plane->props.in_fence_fd, sync_file_fd);
}
static void set_crtc_out_fence_ptr(struct atomic *atom, struct wlr_drm_crtc *crtc,
int *fd_ptr) {
if (!crtc->props.out_fence_ptr) {
wlr_log(WLR_ERROR,
"CRTC %"PRIu32" is missing the OUT_FENCE_PTR property",
crtc->id);
atom->failed = true;
return;
}
atomic_add(atom, crtc->id, crtc->props.out_fence_ptr, (uintptr_t)fd_ptr);
}
static void atomic_connector_add(struct atomic *atom,
const struct wlr_drm_connector_state *state, bool modeset) {
struct wlr_drm_connector_state *state, bool modeset) {
struct wlr_drm_connector *conn = state->connector;
struct wlr_drm_backend *drm = conn->backend;
struct wlr_drm_crtc *crtc = conn->crtc;
@ -376,6 +532,12 @@ static void atomic_connector_add(struct atomic *atom,
if (modeset && active && conn->props.max_bpc != 0 && conn->max_bpc_bounds[1] != 0) {
atomic_add(atom, conn->id, conn->props.max_bpc, pick_max_bpc(conn, state->primary_fb));
}
if (conn->props.colorspace != 0) {
atomic_add(atom, conn->id, conn->props.colorspace, state->colorspace);
}
if (conn->props.hdr_output_metadata != 0) {
atomic_add(atom, conn->id, conn->props.hdr_output_metadata, state->hdr_output_metadata);
}
atomic_add(atom, crtc->id, crtc->props.mode_id, state->mode_id);
atomic_add(atom, crtc->id, crtc->props.active, active);
if (active) {
@ -385,16 +547,33 @@ static void atomic_connector_add(struct atomic *atom,
if (crtc->props.vrr_enabled != 0) {
atomic_add(atom, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled);
}
set_plane_props(atom, drm, crtc->primary, state->primary_fb, crtc->id,
0, 0);
&state->primary_viewport.dst_box, &state->primary_viewport.src_box);
if (crtc->primary->props.fb_damage_clips != 0) {
atomic_add(atom, crtc->primary->id,
crtc->primary->props.fb_damage_clips, state->fb_damage_clips);
}
if (state->primary_in_fence_fd >= 0) {
set_plane_in_fence_fd(atom, crtc->primary, state->primary_in_fence_fd);
}
if (state->base->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) {
set_crtc_out_fence_ptr(atom, crtc, &state->out_fence_fd);
}
if (crtc->cursor) {
if (drm_connector_is_cursor_visible(conn)) {
struct wlr_fbox cursor_src = {
.width = state->cursor_fb->wlr_buf->width,
.height = state->cursor_fb->wlr_buf->height,
};
struct wlr_box cursor_dst = {
.x = conn->cursor_x,
.y = conn->cursor_y,
.width = state->cursor_fb->wlr_buf->width,
.height = state->cursor_fb->wlr_buf->height,
};
set_plane_props(atom, drm, crtc->cursor, state->cursor_fb,
crtc->id, conn->cursor_x, conn->cursor_y);
crtc->id, &cursor_dst, &cursor_src);
if (supports_cursor_hotspots(crtc->cursor)) {
atomic_add(atom, crtc->cursor->id,
crtc->cursor->props.hotspot_x, conn->cursor_hotspot_x);
@ -437,7 +616,7 @@ static bool atomic_device_commit(struct wlr_drm_backend *drm,
if (state->modeset) {
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
}
if (!test_only && state->nonblock) {
if (state->nonblock) {
flags |= DRM_MODE_ATOMIC_NONBLOCK;
}
@ -456,33 +635,6 @@ out:
return ok;
}
bool drm_atomic_reset(struct wlr_drm_backend *drm) {
struct atomic atom;
atomic_begin(&atom);
for (size_t i = 0; i < drm->num_crtcs; i++) {
struct wlr_drm_crtc *crtc = &drm->crtcs[i];
atomic_add(&atom, crtc->id, crtc->props.mode_id, 0);
atomic_add(&atom, crtc->id, crtc->props.active, 0);
}
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->connectors, link) {
atomic_add(&atom, conn->id, conn->props.crtc_id, 0);
}
for (size_t i = 0; i < drm->num_planes; i++) {
plane_disable(&atom, &drm->planes[i]);
}
uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
bool ok = atomic_commit(&atom, drm, NULL, NULL, flags);
atomic_finish(&atom);
return ok;
}
const struct wlr_drm_interface atomic_iface = {
.commit = atomic_device_commit,
.reset = drm_atomic_reset,
};

View file

@ -1,10 +1,8 @@
#include <assert.h>
#include <errno.h>
#include <drm_fourcc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/session.h>
@ -13,6 +11,7 @@
#include <xf86drm.h>
#include "backend/drm/drm.h"
#include "backend/drm/fb.h"
#include "render/drm_format_set.h"
struct wlr_drm_backend *get_drm_backend_from_backend(
struct wlr_backend *wlr_backend) {
@ -53,7 +52,8 @@ static void backend_destroy(struct wlr_backend *backend) {
wl_list_remove(&drm->dev_change.link);
wl_list_remove(&drm->dev_remove.link);
if (drm->parent) {
if (drm->mgpu_renderer.wlr_rend) {
wlr_drm_format_set_finish(&drm->mgpu_formats);
finish_drm_renderer(&drm->mgpu_renderer);
}
@ -75,10 +75,6 @@ static int backend_get_drm_fd(struct wlr_backend *backend) {
return drm->fd;
}
static uint32_t backend_get_buffer_caps(struct wlr_backend *backend) {
return WLR_BUFFER_CAP_DMABUF;
}
static bool backend_test(struct wlr_backend *backend,
const struct wlr_backend_output_state *states, size_t states_len) {
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
@ -95,7 +91,6 @@ static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_drm_fd = backend_get_drm_fd,
.get_buffer_caps = backend_get_buffer_caps,
.test = backend_test,
.commit = backend_commit,
};
@ -117,11 +112,18 @@ static void handle_session_active(struct wl_listener *listener, void *data) {
wlr_log(WLR_INFO, "DRM FD %s", session->active ? "resumed" : "paused");
if (!session->active) {
// Disconnect any active connectors so that the client will modeset and
// rerender when the session is activated again.
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->connectors, link) {
if (conn->status == DRM_MODE_CONNECTED) {
wlr_output_destroy(&conn->output);
}
}
return;
}
scan_drm_connectors(drm, NULL);
restore_drm_device(drm);
}
static void handle_dev_change(struct wl_listener *listener, void *data) {
@ -165,6 +167,44 @@ static void handle_parent_destroy(struct wl_listener *listener, void *data) {
backend_destroy(&drm->backend);
}
static void sanitize_mgpu_modifiers(struct wlr_drm_format_set *set) {
for (size_t idx = 0; idx < set->len; idx++) {
// Implicit modifiers are not well-defined across devices, so strip
// them from all formats in multi-gpu scenarios.
struct wlr_drm_format *fmt = &set->formats[idx];
wlr_drm_format_set_remove(set, fmt->format, DRM_FORMAT_MOD_INVALID);
}
}
static bool init_mgpu_renderer(struct wlr_drm_backend *drm) {
if (!init_drm_renderer(drm, &drm->mgpu_renderer)) {
wlr_log(WLR_INFO, "Failed to initialize mgpu blit renderer"
", falling back to scanning out from primary GPU");
for (uint32_t plane_idx = 0; plane_idx < drm->num_planes; plane_idx++) {
struct wlr_drm_plane *plane = &drm->planes[plane_idx];
sanitize_mgpu_modifiers(&plane->formats);
}
return true;
}
// We'll perform a multi-GPU copy for all submitted buffers, we need
// to be able to texture from them
struct wlr_renderer *renderer = drm->mgpu_renderer.wlr_rend;
const struct wlr_drm_format_set *texture_formats =
wlr_renderer_get_texture_formats(renderer, WLR_BUFFER_CAP_DMABUF);
if (texture_formats == NULL) {
wlr_log(WLR_ERROR, "Failed to query renderer texture formats");
return false;
}
wlr_drm_format_set_copy(&drm->mgpu_formats, texture_formats);
sanitize_mgpu_modifiers(&drm->mgpu_formats);
drm->backend.features.timeline = drm->backend.features.timeline &&
drm->mgpu_renderer.wlr_rend->features.timeline;
return true;
}
struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session,
struct wlr_device *dev, struct wlr_backend *parent) {
assert(session && dev);
@ -192,6 +232,8 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session,
}
wlr_backend_init(&drm->backend, &backend_impl);
drm->backend.buffer_caps = WLR_BUFFER_CAP_DMABUF;
drm->session = session;
wl_list_init(&drm->fbs);
wl_list_init(&drm->connectors);
@ -234,34 +276,8 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session,
goto error_event;
}
if (drm->parent) {
if (!init_drm_renderer(drm, &drm->mgpu_renderer)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer");
goto error_resources;
}
// We'll perform a multi-GPU copy for all submitted buffers, we need
// to be able to texture from them
struct wlr_renderer *renderer = drm->mgpu_renderer.wlr_rend;
const struct wlr_drm_format_set *texture_formats =
wlr_renderer_get_texture_formats(renderer, WLR_BUFFER_CAP_DMABUF);
if (texture_formats == NULL) {
wlr_log(WLR_ERROR, "Failed to query renderer texture formats");
goto error_mgpu_renderer;
}
// Forbid implicit modifiers, because their meaning changes from one
// GPU to another.
for (size_t i = 0; i < texture_formats->len; i++) {
const struct wlr_drm_format *fmt = &texture_formats->formats[i];
for (size_t j = 0; j < fmt->len; j++) {
uint64_t mod = fmt->modifiers[j];
if (mod == DRM_FORMAT_MOD_INVALID) {
continue;
}
wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format, mod);
}
}
if (drm->parent && !init_mgpu_renderer(drm)) {
goto error_mgpu_renderer;
}
drm->session_destroy.notify = handle_session_destroy;
@ -271,7 +287,6 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session,
error_mgpu_renderer:
finish_drm_renderer(&drm->mgpu_renderer);
error_resources:
finish_drm_resources(drm);
error_event:
wl_list_remove(&drm->session_active.link);

View file

@ -1,7 +1,6 @@
#include <assert.h>
#include <drm_fourcc.h>
#include <drm_mode.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdint.h>
@ -14,6 +13,7 @@
#include <wayland-util.h>
#include <wlr/backend/interface.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
@ -24,9 +24,7 @@
#include "backend/drm/fb.h"
#include "backend/drm/iface.h"
#include "backend/drm/util.h"
#include "render/pixel_format.h"
#include "render/drm_format_set.h"
#include "render/wlr_renderer.h"
#include "render/color.h"
#include "types/wlr_output.h"
#include "util/env.h"
#include "config.h"
@ -40,9 +38,12 @@ static const uint32_t COMMIT_OUTPUT_STATE =
WLR_OUTPUT_STATE_BUFFER |
WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_ENABLED |
WLR_OUTPUT_STATE_GAMMA_LUT |
WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED |
WLR_OUTPUT_STATE_LAYERS;
WLR_OUTPUT_STATE_LAYERS |
WLR_OUTPUT_STATE_WAIT_TIMELINE |
WLR_OUTPUT_STATE_SIGNAL_TIMELINE |
WLR_OUTPUT_STATE_COLOR_TRANSFORM |
WLR_OUTPUT_STATE_IMAGE_DESCRIPTION;
static const uint32_t SUPPORTED_OUTPUT_STATE =
WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE;
@ -121,6 +122,7 @@ bool check_drm_features(struct wlr_drm_backend *drm) {
drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1;
} else {
drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1;
drm->backend.features.timeline = drmGetCap(drm->fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap) == 0 && cap == 1;
}
if (env_parse_bool("WLR_DRM_NO_MODIFIERS")) {
@ -170,7 +172,7 @@ static bool init_plane(struct wlr_drm_backend *drm,
}
p->type = type;
p->id = drm_plane->plane_id;
p->id = id;
p->props = props;
p->initial_crtc_id = drm_plane->crtc_id;
@ -396,6 +398,7 @@ void finish_drm_resources(struct wlr_drm_backend *drm) {
struct wlr_drm_plane *plane = &drm->planes[i];
drm_plane_finish_surface(plane);
wlr_drm_format_set_finish(&plane->formats);
free(plane->cursor_sizes);
}
free(drm->planes);
@ -553,6 +556,7 @@ static void drm_connector_apply_commit(const struct wlr_drm_connector_state *sta
struct wlr_drm_crtc *crtc = conn->crtc;
drm_fb_copy(&crtc->primary->queued_fb, state->primary_fb);
crtc->primary->viewport = state->primary_viewport;
if (crtc->cursor != NULL) {
drm_fb_copy(&crtc->cursor->queued_fb, state->cursor_fb);
}
@ -576,6 +580,16 @@ static void drm_connector_apply_commit(const struct wlr_drm_connector_state *sta
conn->cursor_enabled = false;
conn->crtc = NULL;
// Legacy uAPI doesn't support requesting page-flip events when
// turning off a CRTC
if (page_flip != NULL && conn->backend->iface == &legacy_iface) {
drm_page_flip_pop(page_flip, crtc->id);
conn->pending_page_flip = NULL;
if (page_flip->connectors_len == 0) {
drm_page_flip_destroy(page_flip);
}
}
}
}
@ -607,6 +621,7 @@ static bool drm_commit(struct wlr_drm_backend *drm,
if (page_flip == NULL) {
return false;
}
page_flip->async = (flags & DRM_MODE_PAGE_FLIP_ASYNC);
}
bool ok = drm->iface->commit(drm, state, page_flip, flags, test_only);
@ -630,6 +645,8 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state,
.connector = conn,
.base = base,
.active = output_pending_enabled(&conn->output, base),
.primary_in_fence_fd = -1,
.out_fence_fd = -1,
};
struct wlr_output_mode *mode = conn->output.current_mode;
@ -639,7 +656,7 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state,
if (base->committed & WLR_OUTPUT_STATE_MODE) {
switch (base->mode_type) {
case WLR_OUTPUT_STATE_MODE_FIXED:;
case WLR_OUTPUT_STATE_MODE_FIXED:
mode = base->mode;
break;
case WLR_OUTPUT_STATE_MODE_CUSTOM:
@ -666,8 +683,10 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state,
struct wlr_drm_plane *primary = conn->crtc->primary;
if (primary->queued_fb != NULL) {
state->primary_fb = drm_fb_lock(primary->queued_fb);
state->primary_viewport = primary->viewport;
} else if (primary->current_fb != NULL) {
state->primary_fb = drm_fb_lock(primary->current_fb);
state->primary_viewport = primary->viewport;
}
if (conn->cursor_enabled) {
@ -687,6 +706,7 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state,
static void drm_connector_state_finish(struct wlr_drm_connector_state *state) {
drm_fb_clear(&state->primary_fb);
drm_fb_clear(&state->cursor_fb);
wlr_drm_syncobj_timeline_unref(state->wait_timeline);
}
static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn,
@ -701,8 +721,16 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn
struct wlr_drm_plane *plane = crtc->primary;
struct wlr_buffer *source_buf = state->base->buffer;
struct wlr_drm_syncobj_timeline *wait_timeline = NULL;
uint64_t wait_point = 0;
if (state->base->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) {
wait_timeline = state->base->wait_timeline;
wait_point = state->base->wait_point;
}
assert(state->wait_timeline == NULL);
struct wlr_buffer *local_buf;
if (drm->parent) {
if (drm->mgpu_renderer.wlr_rend) {
struct wlr_drm_format format = {0};
if (!drm_plane_pick_render_format(plane, &format, &drm->mgpu_renderer)) {
wlr_log(WLR_ERROR, "Failed to pick primary plane format");
@ -717,12 +745,23 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn
return false;
}
local_buf = drm_surface_blit(&plane->mgpu_surf, source_buf);
local_buf = drm_surface_blit(&plane->mgpu_surf, source_buf,
wait_timeline, wait_point);
if (local_buf == NULL) {
return false;
}
if (plane->mgpu_surf.timeline != NULL) {
state->wait_timeline = wlr_drm_syncobj_timeline_ref(plane->mgpu_surf.timeline);
state->wait_point = plane->mgpu_surf.point;
}
} else {
local_buf = wlr_buffer_lock(source_buf);
if (wait_timeline != NULL) {
state->wait_timeline = wlr_drm_syncobj_timeline_ref(wait_timeline);
state->wait_point = wait_point;
}
}
bool ok = drm_fb_import(&state->primary_fb, drm, local_buf,
@ -734,6 +773,9 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn
return false;
}
output_state_get_buffer_src_box(state->base, &state->primary_viewport.src_box);
output_state_get_buffer_dst_box(state->base, &state->primary_viewport.dst_box);
return true;
}
@ -742,7 +784,7 @@ static bool drm_connector_set_pending_layer_fbs(struct wlr_drm_connector *conn,
struct wlr_drm_backend *drm = conn->backend;
struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc || drm->parent) {
if (!crtc || drm->mgpu_renderer.wlr_rend) {
return false;
}
@ -784,13 +826,12 @@ static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bo
return false;
}
if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && state->enabled) {
if (output->current_mode == NULL &&
!(state->committed & WLR_OUTPUT_STATE_MODE)) {
wlr_drm_conn_log(conn, WLR_DEBUG,
"Can't enable an output without a mode");
return false;
}
if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && state->enabled &&
output->width == 0 && output->height == 0 &&
!(state->committed & WLR_OUTPUT_STATE_MODE)) {
wlr_drm_conn_log(conn, WLR_DEBUG,
"Can't enable an output without a mode");
return false;
}
if ((state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
@ -801,7 +842,36 @@ static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bo
return false;
}
if (test_only && conn->backend->parent) {
if ((state->committed & WLR_OUTPUT_STATE_BUFFER) && conn->backend->mgpu_renderer.wlr_rend) {
struct wlr_dmabuf_attributes dmabuf;
if (!wlr_buffer_get_dmabuf(state->buffer, &dmabuf)) {
wlr_drm_conn_log(conn, WLR_DEBUG, "Buffer is not a DMA-BUF");
return false;
}
if (!wlr_drm_format_set_has(&conn->backend->mgpu_formats, dmabuf.format, dmabuf.modifier)) {
wlr_drm_conn_log(conn, WLR_DEBUG,
"Buffer format 0x%"PRIX32" with modifier 0x%"PRIX64" cannot be "
"imported into multi-GPU renderer",
dmabuf.format, dmabuf.modifier);
return false;
}
}
if ((state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) && state->color_transform != NULL &&
state->color_transform->type != COLOR_TRANSFORM_LUT_3X1D) {
wlr_drm_conn_log(conn, WLR_DEBUG,
"Only 3x1D LUT color transforms are supported");
return false;
}
if ((state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) &&
conn->backend->iface != &atomic_iface) {
wlr_log(WLR_DEBUG, "Image descriptions are only supported by the atomic interface");
return false;
}
if (test_only && conn->backend->mgpu_renderer.wlr_rend) {
// If we're running as a secondary GPU, we can't perform an atomic
// commit without blitting a buffer.
return true;
@ -871,7 +941,7 @@ static bool drm_connector_commit_state(struct wlr_drm_connector *conn,
goto out;
}
if (test_only && conn->backend->parent) {
if (test_only && conn->backend->mgpu_renderer.wlr_rend) {
// If we're running as a secondary GPU, we can't perform an atomic
// commit without blitting a buffer.
ok = true;
@ -1068,7 +1138,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
}
struct wlr_buffer *local_buf;
if (drm->parent) {
if (drm->mgpu_renderer.wlr_rend) {
struct wlr_drm_format format = {0};
if (!drm_plane_pick_render_format(plane, &format, &drm->mgpu_renderer)) {
wlr_log(WLR_ERROR, "Failed to pick cursor plane format");
@ -1082,7 +1152,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
return false;
}
local_buf = drm_surface_blit(&plane->mgpu_surf, buffer);
local_buf = drm_surface_blit(&plane->mgpu_surf, buffer, NULL, 0);
if (local_buf == NULL) {
return false;
}
@ -1102,7 +1172,6 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
conn->cursor_height = buffer->height;
}
wlr_output_update_needs_frame(output);
return true;
}
@ -1132,7 +1201,6 @@ static bool drm_connector_move_cursor(struct wlr_output *output,
conn->cursor_x = box.x;
conn->cursor_y = box.y;
wlr_output_update_needs_frame(output);
return true;
}
@ -1155,6 +1223,8 @@ static void dealloc_crtc(struct wlr_drm_connector *conn);
static void drm_connector_destroy_output(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
wlr_output_finish(output);
dealloc_crtc(conn);
conn->status = DRM_MODE_DISCONNECTED;
@ -1182,7 +1252,7 @@ static const struct wlr_drm_format_set *drm_connector_get_cursor_formats(
if (!plane) {
return NULL;
}
if (conn->backend->parent) {
if (conn->backend->mgpu_renderer.wlr_rend) {
return &conn->backend->mgpu_formats;
}
return &plane->formats;
@ -1211,7 +1281,7 @@ static const struct wlr_drm_format_set *drm_connector_get_primary_formats(
if (!drm_connector_alloc_crtc(conn)) {
return NULL;
}
if (conn->backend->parent) {
if (conn->backend->mgpu_renderer.wlr_rend) {
return &conn->backend->mgpu_formats;
}
return &conn->crtc->primary->formats;
@ -1348,7 +1418,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm,
++i;
}
match_obj(num_connectors, connector_constraints,
match_connectors_with_crtcs(num_connectors, connector_constraints,
drm->num_crtcs, previous_match, new_match);
// Converts our crtc=>connector result into a connector=>crtc one.
@ -1476,14 +1546,14 @@ static struct wlr_drm_connector *create_drm_connector(struct wlr_drm_backend *dr
return NULL;
}
const char *conn_name =
const char *conn_type_name =
drmModeGetConnectorTypeName(drm_conn->connector_type);
if (conn_name == NULL) {
conn_name = "Unknown";
if (conn_type_name == NULL) {
conn_type_name = "Unknown";
}
snprintf(wlr_conn->name, sizeof(wlr_conn->name),
"%s-%"PRIu32, conn_name, drm_conn->connector_type_id);
"%s-%"PRIu32, conn_type_name, drm_conn->connector_type_id);
wlr_conn->possible_crtcs =
drmModeConnectorGetPossibleCrtcs(drm->fd, drm_conn);
@ -1554,6 +1624,7 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn,
wlr_log(WLR_INFO, "Detected modes:");
bool found_current_mode = false;
for (int i = 0; i < drm_conn->count_modes; ++i) {
if (drm_conn->modes[i].flags & DRM_MODE_FLAG_INTERLACE) {
continue;
@ -1572,14 +1643,7 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn,
if (current_modeinfo != NULL && memcmp(&mode->drm_mode,
current_modeinfo, sizeof(*current_modeinfo)) == 0) {
wlr_output_state_set_mode(&state, &mode->wlr_mode);
uint64_t mode_id = 0;
get_drm_prop(drm->fd, wlr_conn->crtc->id,
wlr_conn->crtc->props.mode_id, &mode_id);
wlr_conn->crtc->own_mode_id = false;
wlr_conn->crtc->mode_id = mode_id;
wlr_conn->refresh = calculate_refresh_rate(current_modeinfo);
found_current_mode = true;
}
wlr_log(WLR_INFO, " %"PRId32"x%"PRId32" @ %.3f Hz %s",
@ -1590,6 +1654,23 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn,
wl_list_insert(modes.prev, &mode->wlr_mode.link);
}
if (current_modeinfo != NULL) {
int32_t refresh = calculate_refresh_rate(current_modeinfo);
if (!found_current_mode) {
wlr_output_state_set_custom_mode(&state,
current_modeinfo->hdisplay, current_modeinfo->vdisplay, refresh);
}
uint64_t mode_id = 0;
get_drm_prop(drm->fd, wlr_conn->crtc->id,
wlr_conn->crtc->props.mode_id, &mode_id);
wlr_conn->crtc->own_mode_id = false;
wlr_conn->crtc->mode_id = mode_id;
wlr_conn->refresh = refresh;
}
free(current_modeinfo);
wlr_output_init(output, &drm->backend, &output_impl, drm->session->event_loop, &state);
@ -1636,7 +1717,11 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn,
size_t edid_len = 0;
uint8_t *edid = get_drm_prop_blob(drm->fd,
wlr_conn->id, wlr_conn->props.edid, &edid_len);
parse_edid(wlr_conn, edid_len, edid);
if (edid_len > 0) {
parse_edid(wlr_conn, edid_len, edid);
} else {
wlr_log(WLR_DEBUG, "Connector has no EDID");
}
free(edid);
char *subconnector = NULL;
@ -1702,6 +1787,10 @@ void scan_drm_connectors(struct wlr_drm_backend *drm,
}
}
if (wlr_conn && wlr_conn->lease) {
continue;
}
// If the hotplug event contains a connector ID, ignore any other
// connector.
if (event != NULL && event->connector_id != 0 &&
@ -1779,8 +1868,6 @@ void scan_drm_connectors(struct wlr_drm_backend *drm,
destroy_drm_connector(conn);
}
realloc_crtcs(drm, NULL);
for (size_t i = 0; i < new_outputs_len; ++i) {
struct wlr_drm_connector *conn = new_outputs[i];
@ -1820,108 +1907,6 @@ void scan_drm_leases(struct wlr_drm_backend *drm) {
drmFree(list);
}
static void build_current_connector_state(struct wlr_output_state *state,
struct wlr_drm_connector *conn) {
bool enabled = conn->status != DRM_MODE_DISCONNECTED && conn->output.enabled;
wlr_output_state_init(state);
wlr_output_state_set_enabled(state, enabled);
if (!enabled) {
return;
}
if (conn->output.current_mode != NULL) {
wlr_output_state_set_mode(state, conn->output.current_mode);
} else {
wlr_output_state_set_custom_mode(state,
conn->output.width, conn->output.height, conn->output.refresh);
}
}
/**
* Check whether we need to perform a full reset after a VT switch.
*
* If any connector or plane has a different CRTC, we need to perform a full
* reset to restore our mapping. We couldn't avoid a full reset even if we
* used a single KMS atomic commit to apply our state: the kernel rejects
* commits which migrate a plane from one CRTC to another without going through
* an intermediate state where the plane is disabled.
*/
static bool skip_reset_for_restore(struct wlr_drm_backend *drm) {
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->connectors, link) {
drmModeConnector *drm_conn = drmModeGetConnectorCurrent(drm->fd, conn->id);
if (drm_conn == NULL) {
return false;
}
struct wlr_drm_crtc *crtc = connector_get_current_crtc(conn, drm_conn);
drmModeFreeConnector(drm_conn);
if (crtc != NULL && conn->crtc != crtc) {
return false;
}
}
for (size_t i = 0; i < drm->num_planes; i++) {
struct wlr_drm_plane *plane = &drm->planes[i];
drmModePlane *drm_plane = drmModeGetPlane(drm->fd, plane->id);
if (drm_plane == NULL) {
return false;
}
uint32_t crtc_id = drm_plane->crtc_id;
drmModeFreePlane(drm_plane);
struct wlr_drm_crtc *crtc = NULL;
for (size_t i = 0; i < drm->num_crtcs; i++) {
if (drm->crtcs[i].id == crtc_id) {
crtc = &drm->crtcs[i];
break;
}
}
if (crtc == NULL) {
continue;
}
bool ok = false;
switch (plane->type) {
case DRM_PLANE_TYPE_PRIMARY:
ok = crtc->primary == plane;
break;
case DRM_PLANE_TYPE_CURSOR:
ok = crtc->cursor == plane;
break;
}
if (!ok) {
return false;
}
}
return true;
}
void restore_drm_device(struct wlr_drm_backend *drm) {
// The previous DRM master leaves KMS in an undefined state. We need
// to restore our own state, but be careful to avoid invalid
// configurations. The connector/CRTC mapping may have changed, so
// first disable all CRTCs, then light up the ones we were using
// before the VT switch.
// TODO: better use the atomic API to improve restoration after a VT switch
if (!skip_reset_for_restore(drm) && !drm->iface->reset(drm)) {
wlr_log(WLR_ERROR, "Failed to reset state after VT switch");
}
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->connectors, link) {
struct wlr_output_state state;
build_current_connector_state(&state, conn);
if (!drm_connector_commit_state(conn, &state, false)) {
wlr_drm_conn_log(conn, WLR_ERROR, "Failed to restore state after VT switch");
}
wlr_output_state_finish(&state);
}
}
bool commit_drm_device(struct wlr_drm_backend *drm,
const struct wlr_backend_output_state *output_states, size_t output_states_len,
bool test_only) {
@ -1970,7 +1955,7 @@ bool commit_drm_device(struct wlr_drm_backend *drm,
modeset |= output_state->base.allow_reconfiguration;
}
if (test_only && drm->parent) {
if (test_only && drm->mgpu_renderer.wlr_rend) {
// If we're running as a secondary GPU, we can't perform an atomic
// commit without blitting a buffer.
ok = true;
@ -2008,6 +1993,12 @@ static void handle_page_flip(int fd, unsigned seq,
if (conn != NULL) {
conn->pending_page_flip = NULL;
}
uint32_t present_flags = WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION;
if (!page_flip->async) {
present_flags |= WLR_OUTPUT_PRESENT_VSYNC;
}
if (page_flip->connectors_len == 0) {
drm_page_flip_destroy(page_flip);
}
@ -2038,26 +2029,23 @@ static void handle_page_flip(int fd, unsigned seq,
drm_fb_move(&layer->current_fb, &layer->queued_fb);
}
uint32_t present_flags = WLR_OUTPUT_PRESENT_VSYNC |
WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION;
/* Don't report ZERO_COPY in multi-gpu situations, because we had to copy
* data between the GPUs, even if we were using the direct scanout
* interface.
*/
if (!drm->parent) {
if (!drm->mgpu_renderer.wlr_rend) {
present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY;
}
struct timespec present_time = {
.tv_sec = tv_sec,
.tv_nsec = tv_usec * 1000,
};
struct wlr_output_event_present present_event = {
/* The DRM backend guarantees that the presentation event will be for
* the last submitted frame. */
.commit_seq = conn->output.commit_seq,
.presented = drm->session->active,
.when = &present_time,
.when = {
.tv_sec = tv_sec,
.tv_nsec = tv_usec * 1000,
},
.seq = seq,
.refresh = mhz_to_nsec(conn->refresh),
.flags = present_flags,
@ -2191,6 +2179,7 @@ struct wlr_drm_lease *wlr_drm_create_lease(struct wlr_output **outputs,
get_drm_connector_from_output(outputs[i]);
conn->lease = lease;
conn->crtc->lease = lease;
disconnect_drm_connector(conn);
}
return lease;
@ -2213,6 +2202,8 @@ void drm_lease_destroy(struct wlr_drm_lease *lease) {
wl_signal_emit_mutable(&lease->events.destroy, NULL);
assert(wl_list_empty(&lease->events.destroy.listener_list));
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->connectors, link) {
if (conn->lease == lease) {
@ -2227,4 +2218,5 @@ void drm_lease_destroy(struct wlr_drm_lease *lease) {
}
free(lease);
scan_drm_connectors(drm, NULL);
}

View file

@ -7,6 +7,8 @@
#include "backend/drm/fb.h"
#include "backend/drm/iface.h"
#include "backend/drm/util.h"
#include "render/color.h"
#include "types/wlr_output.h"
static bool legacy_fb_props_match(struct wlr_drm_fb *fb1,
struct wlr_drm_fb *fb2) {
@ -39,20 +41,41 @@ static bool legacy_crtc_test(const struct wlr_drm_connector_state *state,
struct wlr_drm_connector *conn = state->connector;
struct wlr_drm_crtc *crtc = conn->crtc;
if ((state->base->committed & WLR_OUTPUT_STATE_BUFFER) && !modeset) {
struct wlr_drm_fb *pending_fb = state->primary_fb;
struct wlr_drm_fb *prev_fb = crtc->primary->queued_fb;
if (!prev_fb) {
prev_fb = crtc->primary->current_fb;
if (state->base->committed & WLR_OUTPUT_STATE_BUFFER) {
// If the size doesn't match, reject buffer (scaling is not supported)
int pending_width, pending_height;
output_pending_resolution(&state->connector->output, state->base,
&pending_width, &pending_height);
if (state->base->buffer->width != pending_width ||
state->base->buffer->height != pending_height) {
wlr_log(WLR_DEBUG, "Primary buffer size mismatch");
return false;
}
// Source crop is also not supported
struct wlr_fbox src_box;
output_state_get_buffer_src_box(state->base, &src_box);
if (src_box.x != 0.0 || src_box.y != 0.0 ||
src_box.width != (double)state->base->buffer->width ||
src_box.height != (double)state->base->buffer->height) {
wlr_log(WLR_DEBUG, "Source crop not supported in DRM-legacy output");
return false;
}
/* Legacy is only guaranteed to be able to display a FB if it's been
* allocated the same way as the previous one. */
if (prev_fb != NULL && !legacy_fb_props_match(prev_fb, pending_fb)) {
wlr_drm_conn_log(conn, WLR_DEBUG,
"Cannot change scan-out buffer parameters with legacy KMS API");
return false;
if (!modeset) {
struct wlr_drm_fb *pending_fb = state->primary_fb;
struct wlr_drm_fb *prev_fb = crtc->primary->queued_fb;
if (!prev_fb) {
prev_fb = crtc->primary->current_fb;
}
/* Legacy is only guaranteed to be able to display a FB if it's been
* allocated the same way as the previous one. */
if (prev_fb != NULL && !legacy_fb_props_match(prev_fb, pending_fb)) {
wlr_drm_conn_log(conn, WLR_DEBUG,
"Cannot change scan-out buffer parameters with legacy KMS API");
return false;
}
}
}
@ -102,9 +125,17 @@ static bool legacy_crtc_commit(const struct wlr_drm_connector_state *state,
}
}
if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
if (!drm_legacy_crtc_set_gamma(drm, crtc,
state->base->gamma_lut_size, state->base->gamma_lut)) {
if (state->base->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) {
size_t dim = 0;
uint16_t *lut = NULL;
if (state->base->color_transform != NULL) {
struct wlr_color_transform_lut_3x1d *tr =
color_transform_lut_3x1d_from_base(state->base->color_transform);
dim = tr->dim;
lut = tr->lut_3x1d;
}
if (!drm_legacy_crtc_set_gamma(drm, crtc, dim, lut)) {
return false;
}
}
@ -128,7 +159,7 @@ static bool legacy_crtc_commit(const struct wlr_drm_connector_state *state,
state->base->adaptive_sync_enabled ? "enabled" : "disabled");
}
if (cursor != NULL && drm_connector_is_cursor_visible(conn)) {
if (cursor != NULL && state->active && drm_connector_is_cursor_visible(conn)) {
struct wlr_drm_fb *cursor_fb = state->cursor_fb;
if (cursor_fb == NULL) {
wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to acquire cursor FB");
@ -170,7 +201,9 @@ static bool legacy_crtc_commit(const struct wlr_drm_connector_state *state,
}
}
if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
// Legacy uAPI doesn't support requesting page-flip events when
// turning off a CRTC
if (state->active && (flags & DRM_MODE_PAGE_FLIP_EVENT)) {
if (drmModePageFlip(drm->fd, crtc->id, fb_id, flags, page_flip)) {
wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed");
return false;
@ -248,20 +281,6 @@ bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
return true;
}
static bool legacy_reset(struct wlr_drm_backend *drm) {
bool ok = true;
for (size_t i = 0; i < drm->num_crtcs; i++) {
struct wlr_drm_crtc *crtc = &drm->crtcs[i];
if (drmModeSetCrtc(drm->fd, crtc->id, 0, 0, 0, NULL, 0, NULL) != 0) {
wlr_log_errno(WLR_ERROR, "Failed to disable CRTC %"PRIu32,
crtc->id);
ok = false;
}
}
return ok;
}
const struct wlr_drm_interface legacy_iface = {
.commit = legacy_commit,
.reset = legacy_reset,
};

View file

@ -4,12 +4,14 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include "backend/drm/drm.h"
#include "backend/drm/fb.h"
#include "backend/drm/iface.h"
#include "config.h"
#include "types/wlr_output.h"
static void log_handler(enum liftoff_log_priority priority, const char *fmt, va_list args) {
enum wlr_log_importance importance = WLR_SILENT;
@ -149,25 +151,23 @@ static bool add_prop(drmModeAtomicReq *req, uint32_t obj,
}
static bool set_plane_props(struct wlr_drm_plane *plane,
struct liftoff_layer *layer, struct wlr_drm_fb *fb, int32_t x, int32_t y, uint64_t zpos) {
struct liftoff_layer *layer, struct wlr_drm_fb *fb, uint64_t zpos,
const struct wlr_box *dst_box, const struct wlr_fbox *src_box) {
if (fb == NULL) {
wlr_log(WLR_ERROR, "Failed to acquire FB for plane %"PRIu32, plane->id);
return false;
}
uint32_t width = fb->wlr_buf->width;
uint32_t height = fb->wlr_buf->height;
// The SRC_* properties are in 16.16 fixed point
// The src_* properties are in 16.16 fixed point
return liftoff_layer_set_property(layer, "zpos", zpos) == 0 &&
liftoff_layer_set_property(layer, "SRC_X", 0) == 0 &&
liftoff_layer_set_property(layer, "SRC_Y", 0) == 0 &&
liftoff_layer_set_property(layer, "SRC_W", (uint64_t)width << 16) == 0 &&
liftoff_layer_set_property(layer, "SRC_H", (uint64_t)height << 16) == 0 &&
liftoff_layer_set_property(layer, "CRTC_X", (uint64_t)x) == 0 &&
liftoff_layer_set_property(layer, "CRTC_Y", (uint64_t)y) == 0 &&
liftoff_layer_set_property(layer, "CRTC_W", width) == 0 &&
liftoff_layer_set_property(layer, "CRTC_H", height) == 0 &&
liftoff_layer_set_property(layer, "SRC_X", src_box->x * (1 << 16)) == 0 &&
liftoff_layer_set_property(layer, "SRC_Y", src_box->y * (1 << 16)) == 0 &&
liftoff_layer_set_property(layer, "SRC_W", src_box->width * (1 << 16)) == 0 &&
liftoff_layer_set_property(layer, "SRC_H", src_box->height * (1 << 16)) == 0 &&
liftoff_layer_set_property(layer, "CRTC_X", dst_box->x) == 0 &&
liftoff_layer_set_property(layer, "CRTC_Y", dst_box->y) == 0 &&
liftoff_layer_set_property(layer, "CRTC_W", dst_box->width) == 0 &&
liftoff_layer_set_property(layer, "CRTC_H", dst_box->height) == 0 &&
liftoff_layer_set_property(layer, "FB_ID", fb->id) == 0;
}
@ -331,14 +331,32 @@ static bool add_connector(drmModeAtomicReq *req,
if (crtc->props.vrr_enabled != 0) {
ok = ok && add_prop(req, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled);
}
ok = ok &&
set_plane_props(crtc->primary, crtc->primary->liftoff_layer, state->primary_fb, 0, 0, 0) &&
set_plane_props(crtc->primary, crtc->liftoff_composition_layer, state->primary_fb, 0, 0, 0);
ok = ok && set_plane_props(crtc->primary,
crtc->primary->liftoff_layer, state->primary_fb, 0,
&state->primary_viewport.dst_box,
&state->primary_viewport.src_box);
ok = ok && set_plane_props(crtc->primary,
crtc->liftoff_composition_layer, state->primary_fb, 0,
&state->primary_viewport.dst_box,
&state->primary_viewport.src_box);
liftoff_layer_set_property(crtc->primary->liftoff_layer,
"FB_DAMAGE_CLIPS", state->fb_damage_clips);
liftoff_layer_set_property(crtc->liftoff_composition_layer,
"FB_DAMAGE_CLIPS", state->fb_damage_clips);
if (state->primary_in_fence_fd >= 0) {
liftoff_layer_set_property(crtc->primary->liftoff_layer,
"IN_FENCE_FD", state->primary_in_fence_fd);
liftoff_layer_set_property(crtc->liftoff_composition_layer,
"IN_FENCE_FD", state->primary_in_fence_fd);
}
if (state->base->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) {
ok = ok && add_prop(req, crtc->id, crtc->props.out_fence_ptr,
(uintptr_t)&state->out_fence_fd);
}
if (state->base->committed & WLR_OUTPUT_STATE_LAYERS) {
for (size_t i = 0; i < state->base->layers_len; i++) {
const struct wlr_output_layer_state *layer_state = &state->base->layers[i];
@ -349,9 +367,19 @@ static bool add_connector(drmModeAtomicReq *req,
if (crtc->cursor) {
if (drm_connector_is_cursor_visible(conn)) {
struct wlr_fbox cursor_src = {
.width = state->cursor_fb->wlr_buf->width,
.height = state->cursor_fb->wlr_buf->height,
};
struct wlr_box cursor_dst = {
.x = conn->cursor_x,
.y = conn->cursor_y,
.width = state->cursor_fb->wlr_buf->width,
.height = state->cursor_fb->wlr_buf->height,
};
ok = ok && set_plane_props(crtc->cursor, crtc->cursor->liftoff_layer,
state->cursor_fb, conn->cursor_x, conn->cursor_y,
wl_list_length(&crtc->layers) + 1);
state->cursor_fb, wl_list_length(&crtc->layers) + 1,
&cursor_dst, &cursor_src);
} else {
ok = ok && disable_plane(crtc->cursor);
}
@ -397,7 +425,7 @@ static bool commit(struct wlr_drm_backend *drm,
if (state->modeset) {
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
}
if (!test_only && state->nonblock) {
if (state->nonblock) {
flags |= DRM_MODE_ATOMIC_NONBLOCK;
}
@ -479,5 +507,4 @@ const struct wlr_drm_interface liftoff_iface = {
.init = init,
.finish = finish,
.commit = commit,
.reset = drm_atomic_reset,
};

View file

@ -7,6 +7,7 @@ hwdata = dependency(
libdisplay_info = dependency(
'libdisplay-info',
version: '>=0.2.0',
required: 'drm' in backends,
fallback: 'libdisplay-info',
not_found_message: 'Required for the DRM backend.',

View file

@ -3,6 +3,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
@ -21,8 +22,10 @@ struct prop_info {
static const struct prop_info connector_info[] = {
#define INDEX(name) (offsetof(struct wlr_drm_connector_props, name) / sizeof(uint32_t))
{ "CRTC_ID", INDEX(crtc_id) },
{ "Colorspace", INDEX(colorspace) },
{ "DPMS", INDEX(dpms) },
{ "EDID", INDEX(edid) },
{ "HDR_OUTPUT_METADATA", INDEX(hdr_output_metadata) },
{ "PATH", INDEX(path) },
{ "content type", INDEX(content_type) },
{ "link-status", INDEX(link_status) },
@ -40,6 +43,7 @@ static const struct prop_info crtc_info[] = {
{ "GAMMA_LUT", INDEX(gamma_lut) },
{ "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) },
{ "MODE_ID", INDEX(mode_id) },
{ "OUT_FENCE_PTR", INDEX(out_fence_ptr) },
{ "VRR_ENABLED", INDEX(vrr_enabled) },
#undef INDEX
};
@ -55,6 +59,7 @@ static const struct prop_info plane_info[] = {
{ "FB_ID", INDEX(fb_id) },
{ "HOTSPOT_X", INDEX(hotspot_x) },
{ "HOTSPOT_Y", INDEX(hotspot_y) },
{ "IN_FENCE_FD", INDEX(in_fence_fd) },
{ "IN_FORMATS", INDEX(in_formats) },
{ "SIZE_HINTS", INDEX(size_hints) },
{ "SRC_H", INDEX(src_h) },
@ -77,14 +82,14 @@ static bool scan_properties(int fd, uint32_t id, uint32_t type, uint32_t *result
const struct prop_info *info, size_t info_len) {
drmModeObjectProperties *props = drmModeObjectGetProperties(fd, id, type);
if (!props) {
wlr_log_errno(WLR_ERROR, "Failed to get DRM object properties");
wlr_log_errno(WLR_ERROR, "Failed to get DRM object %" PRIu32 " properties", id);
return false;
}
for (uint32_t i = 0; i < props->count_props; ++i) {
drmModePropertyRes *prop = drmModeGetProperty(fd, props->props[i]);
if (!prop) {
wlr_log_errno(WLR_ERROR, "Failed to get DRM object property");
wlr_log_errno(WLR_ERROR, "Failed to get property %" PRIu32 " of DRM object %" PRIu32, props->props[i], id);
continue;
}

View file

@ -1,31 +1,35 @@
#include <assert.h>
#include <drm_fourcc.h>
#include <wlr/render/allocator.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/render/swapchain.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>
#include "backend/drm/drm.h"
#include "backend/drm/fb.h"
#include "backend/drm/renderer.h"
#include "backend/backend.h"
#include "render/drm_format_set.h"
#include "render/allocator/allocator.h"
#include "render/pixel_format.h"
#include "render/wlr_renderer.h"
bool init_drm_renderer(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer) {
wlr_log(WLR_DEBUG, "Creating multi-GPU renderer");
renderer->wlr_rend = renderer_autocreate_with_drm_fd(drm->fd);
if (!renderer->wlr_rend) {
wlr_log(WLR_ERROR, "Failed to create renderer");
return false;
}
if (wlr_renderer_get_texture_formats(renderer->wlr_rend, WLR_BUFFER_CAP_DMABUF) == NULL) {
wlr_log(WLR_ERROR, "Renderer did not support importing DMA-BUFs");
wlr_renderer_destroy(renderer->wlr_rend);
renderer->wlr_rend = NULL;
return false;
}
uint32_t backend_caps = backend_get_buffer_caps(&drm->backend);
renderer->allocator = allocator_autocreate_with_drm_fd(backend_caps,
renderer->wlr_rend, drm->fd);
renderer->allocator = wlr_allocator_autocreate(&drm->backend, renderer->wlr_rend);
if (renderer->allocator == NULL) {
wlr_log(WLR_ERROR, "Failed to create allocator");
wlr_renderer_destroy(renderer->wlr_rend);
renderer->wlr_rend = NULL;
return false;
}
@ -46,6 +50,7 @@ void finish_drm_surface(struct wlr_drm_surface *surf) {
return;
}
wlr_drm_syncobj_timeline_unref(surf->timeline);
wlr_swapchain_destroy(surf->swapchain);
*surf = (struct wlr_drm_surface){0};
@ -68,13 +73,24 @@ bool init_drm_surface(struct wlr_drm_surface *surf,
return false;
}
int drm_fd = wlr_renderer_get_drm_fd(renderer->wlr_rend);
if (renderer->wlr_rend->features.timeline && drm_fd >= 0) {
surf->timeline = wlr_drm_syncobj_timeline_create(drm_fd);
if (surf->timeline == NULL) {
finish_drm_surface(surf);
wlr_log(WLR_ERROR, "Failed to create DRM syncobj timeline");
return false;
}
}
surf->renderer = renderer;
return true;
}
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
struct wlr_buffer *buffer) {
struct wlr_buffer *buffer,
struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) {
struct wlr_renderer *renderer = surf->renderer->wlr_rend;
if (surf->swapchain->width != buffer->width ||
@ -89,13 +105,18 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
return NULL;
}
struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain, NULL);
struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain);
if (!dst) {
wlr_log(WLR_ERROR, "Failed to acquire multi-GPU swapchain buffer");
goto error_tex;
}
struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, NULL);
surf->point++;
const struct wlr_buffer_pass_options pass_options = {
.signal_timeline = surf->timeline,
.signal_point = surf->point,
};
struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, &pass_options);
if (pass == NULL) {
wlr_log(WLR_ERROR, "Failed to begin render pass with multi-GPU destination buffer");
goto error_dst;
@ -104,6 +125,8 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){
.texture = tex,
.blend_mode = WLR_RENDER_BLEND_MODE_NONE,
.wait_timeline = wait_timeline,
.wait_point = wait_point,
});
if (!wlr_render_pass_submit(pass)) {
wlr_log(WLR_ERROR, "Failed to submit multi-GPU render pass");

View file

@ -83,6 +83,17 @@ void parse_edid(struct wlr_drm_connector *conn, size_t len, const uint8_t *data)
output->model = di_info_get_model(info);
output->serial = di_info_get_serial(info);
const struct di_supported_signal_colorimetry *colorimetry = di_info_get_supported_signal_colorimetry(info);
bool has_bt2020 = colorimetry->bt2020_cycc || colorimetry->bt2020_ycc || colorimetry->bt2020_rgb;
if (conn->props.colorspace != 0 && has_bt2020) {
output->supported_primaries |= WLR_COLOR_NAMED_PRIMARIES_BT2020;
}
const struct di_hdr_static_metadata *hdr_static_metadata = di_info_get_hdr_static_metadata(info);
if (conn->props.hdr_output_metadata != 0 && hdr_static_metadata->type1 && hdr_static_metadata->pq) {
output->supported_transfer_functions |= WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ;
}
di_info_destroy(info);
}
@ -112,9 +123,9 @@ static bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
* passing 12 arguments to a function.
*/
struct match_state {
const size_t num_objs;
const uint32_t *restrict objs;
const size_t num_res;
const size_t num_conns;
const uint32_t *restrict conns;
const size_t num_crtcs;
size_t score;
size_t replaced;
uint32_t *restrict res;
@ -123,27 +134,31 @@ struct match_state {
bool exit_early;
};
/*
* skips: The number of SKIP elements encountered so far.
* score: The number of resources we've matched so far.
/**
* Step to process a CRTC.
*
* This is a naive implementation of maximum bipartite matching.
*
* score: The number of connectors we've matched so far.
* replaced: The number of changes from the original solution.
* i: The index of the current element.
* crtc_index: The index of the current CRTC.
*
* This tries to match a solution as close to st->orig as it can.
*
* Returns whether we've set a new best element with this solution.
*/
static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_t replaced, size_t i) {
static bool match_connectors_with_crtcs_(struct match_state *st,
size_t score, size_t replaced, size_t crtc_index) {
// Finished
if (i >= st->num_res) {
if (crtc_index >= st->num_crtcs) {
if (score > st->score ||
(score == st->score && replaced < st->replaced)) {
st->score = score;
st->replaced = replaced;
memcpy(st->best, st->res, sizeof(st->best[0]) * st->num_res);
memcpy(st->best, st->res, sizeof(st->best[0]) * st->num_crtcs);
st->exit_early = (st->score == st->num_res - skips
|| st->score == st->num_objs)
st->exit_early = (st->score == st->num_crtcs
|| st->score == st->num_conns)
&& st->replaced == 0;
return true;
@ -152,27 +167,16 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_
}
}
if (st->orig[i] == SKIP) {
st->res[i] = SKIP;
return match_obj_(st, skips + 1, score, replaced, i + 1);
}
bool has_best = false;
/*
* Attempt to use the current solution first, to try and avoid
* recalculating everything
*/
if (st->orig[i] != UNMATCHED && !is_taken(i, st->res, st->orig[i])) {
st->res[i] = st->orig[i];
size_t obj_score = st->objs[st->res[i]] != 0 ? 1 : 0;
if (match_obj_(st, skips, score + obj_score, replaced, i + 1)) {
has_best = true;
}
}
if (st->orig[i] == UNMATCHED) {
st->res[i] = UNMATCHED;
if (match_obj_(st, skips, score, replaced, i + 1)) {
if (st->orig[crtc_index] != UNMATCHED && !is_taken(crtc_index, st->res, st->orig[crtc_index])) {
st->res[crtc_index] = st->orig[crtc_index];
size_t crtc_score = st->conns[st->res[crtc_index]] != 0 ? 1 : 0;
if (match_connectors_with_crtcs_(st, score + crtc_score, replaced, crtc_index + 1)) {
has_best = true;
}
}
@ -180,29 +184,29 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_
return true;
}
if (st->orig[i] != UNMATCHED) {
if (st->orig[crtc_index] != UNMATCHED) {
++replaced;
}
for (size_t candidate = 0; candidate < st->num_objs; ++candidate) {
for (size_t candidate = 0; candidate < st->num_conns; ++candidate) {
// We tried this earlier
if (candidate == st->orig[i]) {
if (candidate == st->orig[crtc_index]) {
continue;
}
// Not compatible
if (!(st->objs[candidate] & (1 << i))) {
if (!(st->conns[candidate] & (1 << crtc_index))) {
continue;
}
// Already taken
if (is_taken(i, st->res, candidate)) {
if (is_taken(crtc_index, st->res, candidate)) {
continue;
}
st->res[i] = candidate;
size_t obj_score = st->objs[candidate] != 0 ? 1 : 0;
if (match_obj_(st, skips, score + obj_score, replaced, i + 1)) {
st->res[crtc_index] = candidate;
size_t crtc_score = st->conns[candidate] != 0 ? 1 : 0;
if (match_connectors_with_crtcs_(st, score + crtc_score, replaced, crtc_index + 1)) {
has_best = true;
}
@ -211,37 +215,37 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_
}
}
if (has_best) {
return true;
// Maybe this CRTC can't be matched
st->res[crtc_index] = UNMATCHED;
if (match_connectors_with_crtcs_(st, score, replaced, crtc_index + 1)) {
has_best = true;
}
// Maybe this resource can't be matched
st->res[i] = UNMATCHED;
return match_obj_(st, skips, score, replaced, i + 1);
return has_best;
}
size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs],
size_t num_res, const uint32_t res[static restrict num_res],
uint32_t out[static restrict num_res]) {
uint32_t solution[num_res];
for (size_t i = 0; i < num_res; ++i) {
void match_connectors_with_crtcs(size_t num_conns,
const uint32_t conns[static restrict num_conns],
size_t num_crtcs, const uint32_t prev_crtcs[static restrict num_crtcs],
uint32_t new_crtcs[static restrict num_crtcs]) {
uint32_t solution[num_crtcs];
for (size_t i = 0; i < num_crtcs; ++i) {
solution[i] = UNMATCHED;
}
struct match_state st = {
.num_objs = num_objs,
.num_res = num_res,
.num_conns = num_conns,
.num_crtcs = num_crtcs,
.score = 0,
.replaced = SIZE_MAX,
.objs = objs,
.conns = conns,
.res = solution,
.best = out,
.orig = res,
.best = new_crtcs,
.orig = prev_crtcs,
.exit_early = false,
};
match_obj_(&st, 0, 0, 0, 0);
return st.score;
match_connectors_with_crtcs_(&st, 0, 0, 0);
}
void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay,

View file

@ -45,16 +45,9 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
free(backend);
}
static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) {
return WLR_BUFFER_CAP_DATA_PTR
| WLR_BUFFER_CAP_DMABUF
| WLR_BUFFER_CAP_SHM;
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_buffer_caps = get_buffer_caps,
};
static void handle_event_loop_destroy(struct wl_listener *listener, void *data) {
@ -74,12 +67,17 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_event_loop *loop) {
wlr_backend_init(&backend->backend, &backend_impl);
backend->backend.buffer_caps =
WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM;
backend->event_loop = loop;
wl_list_init(&backend->outputs);
backend->event_loop_destroy.notify = handle_event_loop_destroy;
wl_event_loop_add_destroy_listener(loop, &backend->event_loop_destroy);
backend->backend.features.timeline = true;
return &backend->backend;
}

View file

@ -79,9 +79,20 @@ static bool output_commit(struct wlr_output *wlr_output,
return true;
}
static bool output_set_cursor(struct wlr_output *wlr_output,
struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) {
return true;
}
static bool output_move_cursor(struct wlr_output *wlr_output, int x, int y) {
return true;
}
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
struct wlr_headless_output *output = headless_output_from_output(wlr_output);
wlr_output_finish(wlr_output);
wl_list_remove(&output->link);
wl_event_source_remove(output->frame_timer);
free(output);
@ -89,7 +100,10 @@ static void output_destroy(struct wlr_output *wlr_output) {
static const struct wlr_output_impl output_impl = {
.destroy = output_destroy,
.test = output_test,
.commit = output_commit,
.set_cursor = output_set_cursor,
.move_cursor = output_move_cursor,
};
bool wlr_output_is_headless(struct wlr_output *wlr_output) {

View file

@ -61,25 +61,15 @@ void handle_pointer_button(struct libinput_event *event,
.time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)),
.button = libinput_event_pointer_get_button(pevent),
};
// Ignore events which aren't a seat-wide state change. For instance, if
// the same button is pressed twice on the same seat, ignore the second
// press.
uint32_t seat_count = libinput_event_pointer_get_seat_button_count(pevent);
switch (libinput_event_pointer_get_button_state(pevent)) {
case LIBINPUT_BUTTON_STATE_PRESSED:
wlr_event.state = WL_POINTER_BUTTON_STATE_PRESSED;
if (seat_count != 1) {
return;
}
break;
case LIBINPUT_BUTTON_STATE_RELEASED:
wlr_event.state = WL_POINTER_BUTTON_STATE_RELEASED;
if (seat_count != 0) {
return;
}
break;
}
wl_signal_emit_mutable(&pointer->events.button, &wlr_event);
wlr_pointer_notify_button(pointer, &wlr_event);
wl_signal_emit_mutable(&pointer->events.frame, pointer);
}

View file

@ -104,6 +104,7 @@ void init_device_tablet_pad(struct wlr_libinput_input_device *dev) {
struct udev_device *udev = libinput_device_get_udev_device(handle);
char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *));
*dst = strdup(udev_device_get_syspath(udev));
udev_device_unref(udev);
int groups = libinput_device_tablet_pad_get_num_mode_groups(handle);
for (int i = 0; i < groups; ++i) {

View file

@ -6,6 +6,7 @@
#include <wlr/interfaces/wlr_tablet_tool.h>
#include <wlr/util/log.h>
#include "backend/libinput.h"
#include "config.h"
struct tablet_tool {
struct wlr_tablet_tool wlr_tool;
@ -36,6 +37,7 @@ void init_device_tablet(struct wlr_libinput_input_device *dev) {
struct udev_device *udev = libinput_device_get_udev_device(dev->handle);
char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *));
*dst = strdup(udev_device_get_syspath(udev));
udev_device_unref(udev);
wl_list_init(&dev->tablet_tools);
}

View file

@ -6,7 +6,6 @@
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/log.h>
#include "backend/backend.h"
#include "backend/multi.h"
struct subbackend_state {
@ -52,6 +51,9 @@ static void multi_backend_destroy(struct wlr_backend *wlr_backend) {
wlr_backend_finish(wlr_backend);
assert(wl_list_empty(&backend->events.backend_add.listener_list));
assert(wl_list_empty(&backend->events.backend_remove.listener_list));
// Some backends may depend on other backends, ie. destroying a backend may
// also destroy other backends
while (!wl_list_empty(&backend->backends)) {
@ -76,28 +78,6 @@ static int multi_backend_get_drm_fd(struct wlr_backend *backend) {
return -1;
}
static uint32_t multi_backend_get_buffer_caps(struct wlr_backend *backend) {
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
if (wl_list_empty(&multi->backends)) {
return 0;
}
uint32_t caps = WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF
| WLR_BUFFER_CAP_SHM;
struct subbackend_state *sub;
wl_list_for_each(sub, &multi->backends, link) {
uint32_t backend_caps = backend_get_buffer_caps(sub->backend);
if (backend_caps != 0) {
// only count backend capable of presenting a buffer
caps = caps & backend_caps;
}
}
return caps;
}
static int compare_output_state_backend(const void *data_a, const void *data_b) {
const struct wlr_backend_output_state *a = data_a;
const struct wlr_backend_output_state *b = data_b;
@ -126,22 +106,24 @@ static bool commit(struct wlr_backend *backend,
qsort(by_backend, states_len, sizeof(by_backend[0]), compare_output_state_backend);
bool ok = true;
for (size_t i = 0; i < states_len; i++) {
for (size_t i = 0; i < states_len;) {
struct wlr_backend *sub = by_backend[i].output->backend;
size_t j = i;
while (j < states_len && by_backend[j].output->backend == sub) {
j++;
size_t len = 1;
while (i + len < states_len &&
by_backend[i + len].output->backend == sub) {
len++;
}
if (test_only) {
ok = wlr_backend_test(sub, &by_backend[i], j - i);
ok = wlr_backend_test(sub, &by_backend[i], len);
} else {
ok = wlr_backend_commit(sub, &by_backend[i], j - i);
ok = wlr_backend_commit(sub, &by_backend[i], len);
}
if (!ok) {
break;
}
i += len;
}
free(by_backend);
@ -162,7 +144,6 @@ static const struct wlr_backend_impl backend_impl = {
.start = multi_backend_start,
.destroy = multi_backend_destroy,
.get_drm_fd = multi_backend_get_drm_fd,
.get_buffer_caps = multi_backend_get_buffer_caps,
.test = multi_backend_test,
.commit = multi_backend_commit,
};
@ -225,6 +206,33 @@ static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_ba
return NULL;
}
static void multi_backend_refresh_features(struct wlr_multi_backend *multi) {
multi->backend.buffer_caps = 0;
multi->backend.features.timeline = true;
bool has_buffer_cap = false;
uint32_t buffer_caps_intersection =
WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM;
struct subbackend_state *sub = NULL;
wl_list_for_each(sub, &multi->backends, link) {
// Only take into account backends capable of presenting a buffer
if (sub->backend->buffer_caps != 0) {
has_buffer_cap = true;
buffer_caps_intersection &= sub->backend->buffer_caps;
}
// timeline is only applicable to backends that support DMABUFs
if (sub->backend->buffer_caps & WLR_BUFFER_CAP_DMABUF) {
multi->backend.features.timeline = multi->backend.features.timeline &&
sub->backend->features.timeline;
}
}
if (has_buffer_cap) {
multi->backend.buffer_caps = buffer_caps_intersection;
}
}
bool wlr_multi_backend_add(struct wlr_backend *_multi,
struct wlr_backend *backend) {
assert(_multi && backend);
@ -256,6 +264,7 @@ bool wlr_multi_backend_add(struct wlr_backend *_multi,
wl_signal_add(&backend->events.new_output, &sub->new_output);
sub->new_output.notify = new_output_reemit;
multi_backend_refresh_features(multi);
wl_signal_emit_mutable(&multi->events.backend_add, backend);
return true;
}
@ -270,6 +279,7 @@ void wlr_multi_backend_remove(struct wlr_backend *_multi,
if (sub) {
wl_signal_emit_mutable(&multi->events.backend_remove, backend);
subbackend_state_destroy(sub);
multi_backend_refresh_features(multi);
}
}

View file

@ -191,32 +191,40 @@ static int handle_udev_event(int fd, uint32_t mask, void *data) {
goto out;
}
dev_t devnum = udev_device_get_devnum(udev_dev);
if (strcmp(action, "add") == 0) {
struct wlr_device *dev;
wl_list_for_each(dev, &session->devices, link) {
if (dev->dev == devnum) {
wlr_log(WLR_DEBUG, "Skipping duplicate device %s", sysname);
goto out;
}
}
wlr_log(WLR_DEBUG, "DRM device %s added", sysname);
struct wlr_session_add_event event = {
.path = devnode,
};
wl_signal_emit_mutable(&session->events.add_drm_card, &event);
} else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) {
dev_t devnum = udev_device_get_devnum(udev_dev);
} else if (strcmp(action, "change") == 0) {
struct wlr_device *dev;
wl_list_for_each(dev, &session->devices, link) {
if (dev->dev != devnum) {
continue;
}
if (strcmp(action, "change") == 0) {
if (dev->dev == devnum) {
wlr_log(WLR_DEBUG, "DRM device %s changed", sysname);
struct wlr_device_change_event event = {0};
read_udev_change_event(&event, udev_dev);
wl_signal_emit_mutable(&dev->events.change, &event);
} else if (strcmp(action, "remove") == 0) {
break;
}
}
} else if (strcmp(action, "remove") == 0) {
struct wlr_device *dev;
wl_list_for_each(dev, &session->devices, link) {
if (dev->dev == devnum) {
wlr_log(WLR_DEBUG, "DRM device %s removed", sysname);
wl_signal_emit_mutable(&dev->events.remove, NULL);
} else {
assert(0);
break;
}
break;
}
}
@ -295,6 +303,11 @@ void wlr_session_destroy(struct wlr_session *session) {
}
wl_signal_emit_mutable(&session->events.destroy, session);
assert(wl_list_empty(&session->events.active.listener_list));
assert(wl_list_empty(&session->events.add_drm_card.listener_list));
assert(wl_list_empty(&session->events.destroy.listener_list));
wl_list_remove(&session->event_loop_destroy.link);
wl_event_source_remove(session->udev_event);
@ -352,6 +365,13 @@ void wlr_session_close_file(struct wlr_session *session,
if (libseat_close_device(session->seat_handle, dev->device_id) == -1) {
wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id);
}
assert(wl_list_empty(&dev->events.change.listener_list));
// TODO: assert that the "remove" listener list is empty as well. Listeners
// will typically call wlr_session_close_file() in response, and
// wl_signal_emit_mutable() installs two phantom listeners, so we'd count
// these two.
close(dev->fd);
wl_list_remove(&dev->link);
free(dev);
@ -499,8 +519,6 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
break;
}
bool is_boot_vga = false;
const char *path = udev_list_entry_get_name(entry);
struct udev_device *dev = udev_device_new_from_syspath(session->udev, path);
if (!dev) {
@ -516,14 +534,20 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
continue;
}
// This is owned by 'dev', so we don't need to free it
struct udev_device *pci =
udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
bool is_primary = false;
const char *boot_display = udev_device_get_sysattr_value(dev, "boot_display");
if (boot_display && strcmp(boot_display, "1") == 0) {
is_primary = true;
} else {
// This is owned by 'dev', so we don't need to free it
struct udev_device *pci =
udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
if (pci) {
const char *id = udev_device_get_sysattr_value(pci, "boot_vga");
if (id && strcmp(id, "1") == 0) {
is_boot_vga = true;
if (pci) {
const char *id = udev_device_get_sysattr_value(pci, "boot_vga");
if (id && strcmp(id, "1") == 0) {
is_primary = true;
}
}
}
@ -537,7 +561,7 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
udev_device_unref(dev);
ret[i] = wlr_dev;
if (is_boot_vga) {
if (is_primary) {
struct wlr_device *tmp = ret[0];
ret[0] = ret[i];
ret[i] = tmp;

View file

@ -21,6 +21,7 @@
#include "drm-client-protocol.h"
#include "linux-dmabuf-v1-client-protocol.h"
#include "linux-drm-syncobj-v1-client-protocol.h"
#include "pointer-gestures-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
@ -54,14 +55,6 @@ struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *wlr_backe
static int dispatch_events(int fd, uint32_t mask, void *data) {
struct wlr_wl_backend *wl = data;
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
if (mask & WL_EVENT_ERROR) {
wlr_log(WLR_ERROR, "Failed to read from remote Wayland display");
}
wlr_backend_destroy(&wl->backend);
return 0;
}
int count = 0;
if (mask & WL_EVENT_READABLE) {
count = wl_display_dispatch(wl->remote_display);
@ -74,6 +67,18 @@ static int dispatch_events(int fd, uint32_t mask, void *data) {
wl_display_flush(wl->remote_display);
}
// Make sure we've consumed all data before disconnecting due to hangup,
// so that we process any wl_display.error events
if (!(mask & WL_EVENT_READABLE) && (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR))) {
if (mask & WL_EVENT_ERROR) {
wlr_log(WLR_ERROR, "Failed to read from remote Wayland display");
} else {
wlr_log(WLR_DEBUG, "Disconnected from remote Wayland display");
}
wlr_backend_destroy(&wl->backend);
return 0;
}
if (count < 0) {
wlr_log(WLR_ERROR, "Failed to dispatch remote Wayland display");
wlr_backend_destroy(&wl->backend);
@ -178,7 +183,9 @@ static void linux_dmabuf_feedback_v1_handle_main_device(void *data,
"falling back to primary node", name);
}
feedback_data->backend->drm_render_name = strdup(name);
struct wlr_wl_backend *wl = feedback_data->backend;
assert(wl->drm_render_name == NULL);
wl->drm_render_name = strdup(name);
drmFreeDevice(&device);
}
@ -305,6 +312,7 @@ static char *get_render_name(const char *name) {
static void legacy_drm_handle_device(void *data, struct wl_drm *drm,
const char *name) {
struct wlr_wl_backend *wl = data;
assert(wl->drm_render_name == NULL);
wl->drm_render_name = get_render_name(name);
}
@ -408,6 +416,9 @@ static void registry_global(void *data, struct wl_registry *registry,
} else if (strcmp(iface, wp_viewporter_interface.name) == 0) {
wl->viewporter = wl_registry_bind(registry, name,
&wp_viewporter_interface, 1);
} else if (strcmp(iface, wp_linux_drm_syncobj_manager_v1_interface.name) == 0) {
wl->drm_syncobj_manager_v1 = wl_registry_bind(registry, name,
&wp_linux_drm_syncobj_manager_v1_interface, 1);
}
}
@ -481,6 +492,11 @@ static void backend_destroy(struct wlr_backend *backend) {
destroy_wl_buffer(buffer);
}
struct wlr_wl_drm_syncobj_timeline *timeline, *tmp_timeline;
wl_list_for_each_safe(timeline, tmp_timeline, &wl->drm_syncobj_timelines, link) {
destroy_wl_drm_syncobj_timeline(timeline);
}
wlr_backend_finish(backend);
wl_list_remove(&wl->event_loop_destroy.link);
@ -515,6 +531,9 @@ static void backend_destroy(struct wlr_backend *backend) {
if (wl->zwp_linux_dmabuf_v1) {
zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1);
}
if (wl->drm_syncobj_manager_v1) {
wp_linux_drm_syncobj_manager_v1_destroy(wl->drm_syncobj_manager_v1);
}
if (wl->legacy_drm != NULL) {
wl_drm_destroy(wl->legacy_drm);
}
@ -540,6 +559,7 @@ static void backend_destroy(struct wlr_backend *backend) {
wl_compositor_destroy(wl->compositor);
wl_registry_destroy(wl->registry);
wl_display_flush(wl->remote_display);
wl_event_queue_destroy(wl->busy_loop_queue);
if (wl->own_remote_display) {
wl_display_disconnect(wl->remote_display);
}
@ -551,17 +571,10 @@ static int backend_get_drm_fd(struct wlr_backend *backend) {
return wl->drm_fd;
}
static uint32_t get_buffer_caps(struct wlr_backend *backend) {
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
return (wl->zwp_linux_dmabuf_v1 ? WLR_BUFFER_CAP_DMABUF : 0)
| (wl->shm ? WLR_BUFFER_CAP_SHM : 0);
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_drm_fd = backend_get_drm_fd,
.get_buffer_caps = get_buffer_caps,
};
bool wlr_backend_is_wl(struct wlr_backend *b) {
@ -589,6 +602,7 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop,
wl_list_init(&wl->outputs);
wl_list_init(&wl->seats);
wl_list_init(&wl->buffers);
wl_list_init(&wl->drm_syncobj_timelines);
if (remote_display != NULL) {
wl->remote_display = remote_display;
@ -601,10 +615,16 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop,
wl->own_remote_display = true;
}
wl->busy_loop_queue = wl_display_create_queue(wl->remote_display);
if (wl->busy_loop_queue == NULL) {
wlr_log_errno(WLR_ERROR, "Could not create a Wayland event queue");
goto error_display;
}
wl->registry = wl_display_get_registry(wl->remote_display);
if (!wl->registry) {
wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry");
goto error_display;
goto error_queue;
}
wl_registry_add_listener(wl->registry, &registry_listener, wl);
@ -621,6 +641,10 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop,
goto error_registry;
}
wl->backend.features.timeline = wl->drm_syncobj_manager_v1 != NULL;
wl_display_roundtrip(wl->remote_display); // process initial event bursts
struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL;
struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { .backend = wl };
if (wl->zwp_linux_dmabuf_v1 != NULL &&
@ -638,18 +662,27 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop,
if (wl->legacy_drm != NULL) {
wl_drm_destroy(wl->legacy_drm);
wl->legacy_drm = NULL;
free(wl->drm_render_name);
wl->drm_render_name = NULL;
}
}
wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats
wl_display_roundtrip(wl->remote_display); // get linux-dmabuf feedback events
if (feedback_data.format_table != NULL) {
munmap(feedback_data.format_table, feedback_data.format_table_size);
}
if (feedback_data.format_table != NULL) {
munmap(feedback_data.format_table, feedback_data.format_table_size);
}
if (linux_dmabuf_feedback_v1 != NULL) {
zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1);
}
if (wl->zwp_linux_dmabuf_v1) {
wl->backend.buffer_caps |= WLR_BUFFER_CAP_DMABUF;
}
if (wl->shm) {
wl->backend.buffer_caps |= WLR_BUFFER_CAP_SHM;
}
int fd = wl_display_get_fd(wl->remote_display);
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
dispatch_events, wl);
@ -693,6 +726,8 @@ error_registry:
xdg_wm_base_destroy(wl->xdg_wm_base);
}
wl_registry_destroy(wl->registry);
error_queue:
wl_event_queue_destroy(wl->busy_loop_queue);
error_display:
if (wl->own_remote_display) {
wl_display_disconnect(wl->remote_display);

View file

@ -1,6 +1,5 @@
wayland_client = dependency('wayland-client',
fallback: 'wayland',
default_options: wayland_project_options,
kwargs: wayland_kwargs,
)
wlr_deps += wayland_client
@ -15,6 +14,7 @@ wlr_files += files(
client_protos = [
'drm',
'linux-dmabuf-v1',
'linux-drm-syncobj-v1',
'pointer-gestures-unstable-v1',
'presentation-time',
'relative-pointer-unstable-v1',

View file

@ -11,6 +11,7 @@
#include <wayland-client-protocol.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_output_layer.h>
#include <wlr/util/log.h>
@ -20,6 +21,7 @@
#include "types/wlr_output.h"
#include "linux-dmabuf-v1-client-protocol.h"
#include "linux-drm-syncobj-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
@ -31,7 +33,9 @@ static const uint32_t SUPPORTED_OUTPUT_STATE =
WLR_OUTPUT_STATE_BUFFER |
WLR_OUTPUT_STATE_ENABLED |
WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED |
WLR_OUTPUT_STATE_WAIT_TIMELINE |
WLR_OUTPUT_STATE_SIGNAL_TIMELINE;
static size_t last_output_num = 0;
@ -94,14 +98,13 @@ static void presentation_feedback_handle_presented(void *data,
uint32_t seq_hi, uint32_t seq_lo, uint32_t flags) {
struct wlr_wl_presentation_feedback *feedback = data;
struct timespec t = {
.tv_sec = ((uint64_t)tv_sec_hi << 32) | tv_sec_lo,
.tv_nsec = tv_nsec,
};
struct wlr_output_event_present event = {
.commit_seq = feedback->commit_seq,
.presented = true,
.when = &t,
.when = {
.tv_sec = ((uint64_t)tv_sec_hi << 32) | tv_sec_lo,
.tv_nsec = tv_nsec,
},
.seq = ((uint64_t)seq_hi << 32) | seq_lo,
.refresh = refresh_ns,
.flags = flags,
@ -131,6 +134,11 @@ static const struct wp_presentation_feedback_listener
.discarded = presentation_feedback_handle_discarded,
};
static void buffer_remove_drm_syncobj_waiter(struct wlr_wl_buffer *buffer) {
wlr_drm_syncobj_timeline_waiter_finish(&buffer->drm_syncobj_waiter);
buffer->has_drm_syncobj_waiter = false;
}
void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
if (buffer == NULL) {
return;
@ -138,22 +146,42 @@ void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
wl_list_remove(&buffer->buffer_destroy.link);
wl_list_remove(&buffer->link);
wl_buffer_destroy(buffer->wl_buffer);
if (buffer->has_drm_syncobj_waiter) {
buffer_remove_drm_syncobj_waiter(buffer);
}
if (!buffer->released) {
wlr_buffer_unlock(buffer->buffer);
}
wlr_drm_syncobj_timeline_unref(buffer->fallback_signal_timeline);
free(buffer);
}
static void buffer_release(struct wlr_wl_buffer *buffer) {
if (buffer->released) {
return;
}
buffer->released = true;
wlr_buffer_unlock(buffer->buffer); // might free buffer
}
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
struct wlr_wl_buffer *buffer = data;
buffer->released = true;
wlr_buffer_unlock(buffer->buffer); // might free buffer
if (buffer->has_drm_syncobj_waiter) {
return;
}
buffer_release(buffer);
}
static const struct wl_buffer_listener buffer_listener = {
.release = buffer_handle_release,
};
static void buffer_handle_drm_syncobj_ready(struct wlr_drm_syncobj_timeline_waiter *waiter) {
struct wlr_wl_buffer *buffer = wl_container_of(waiter, buffer, drm_syncobj_waiter);
buffer_remove_drm_syncobj_waiter(buffer);
buffer_release(buffer);
}
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
void *data) {
struct wlr_wl_buffer *buffer =
@ -176,6 +204,30 @@ static bool test_buffer(struct wlr_wl_backend *wl,
}
}
struct dmabuf_listener_data {
struct wl_buffer *wl_buffer;
bool done;
};
static void dmabuf_handle_created(void *data_, struct zwp_linux_buffer_params_v1 *params,
struct wl_buffer *buffer) {
struct dmabuf_listener_data *data = data_;
data->wl_buffer = buffer;
data->done = true;
wlr_log(WLR_DEBUG, "DMA-BUF imported into parent Wayland compositor");
}
static void dmabuf_handle_failed(void *data_, struct zwp_linux_buffer_params_v1 *params) {
struct dmabuf_listener_data *data = data_;
data->done = true;
wlr_log(WLR_ERROR, "Failed to import DMA-BUF into parent Wayland compositor");
}
static const struct zwp_linux_buffer_params_v1_listener dmabuf_listener = {
.created = dmabuf_handle_created,
.failed = dmabuf_handle_failed,
};
static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl,
struct wlr_dmabuf_attributes *dmabuf) {
uint32_t modifier_hi = dmabuf->modifier >> 32;
@ -187,18 +239,35 @@ static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl,
dmabuf->offset[i], dmabuf->stride[i], modifier_hi, modifier_lo);
}
struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed(
params, dmabuf->width, dmabuf->height, dmabuf->format, 0);
struct dmabuf_listener_data data = {0};
zwp_linux_buffer_params_v1_add_listener(params, &dmabuf_listener, &data);
zwp_linux_buffer_params_v1_create(params, dmabuf->width, dmabuf->height, dmabuf->format, 0);
struct wl_event_queue *display_queue =
wl_proxy_get_queue((struct wl_proxy *)wl->remote_display);
wl_proxy_set_queue((struct wl_proxy *)params, wl->busy_loop_queue);
while (!data.done) {
if (wl_display_dispatch_queue(wl->remote_display, wl->busy_loop_queue) < 0) {
wlr_log(WLR_ERROR, "wl_display_dispatch_queue() failed");
break;
}
}
struct wl_buffer *buffer = data.wl_buffer;
if (buffer != NULL) {
wl_proxy_set_queue((struct wl_proxy *)buffer, display_queue);
}
zwp_linux_buffer_params_v1_destroy(params);
// TODO: handle create() errors
return wl_buffer;
return buffer;
}
static struct wl_buffer *import_shm(struct wlr_wl_backend *wl,
struct wlr_shm_attributes *shm) {
enum wl_shm_format wl_shm_format = convert_drm_format_to_wl_shm(shm->format);
uint32_t size = shm->stride * shm->height;
struct wl_shm_pool *pool = wl_shm_create_pool(wl->shm, shm->fd, size);
struct wl_shm_pool *pool = wl_shm_create_pool(wl->shm, shm->fd, shm->offset + size);
if (pool == NULL) {
return NULL;
}
@ -262,6 +331,58 @@ static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl,
return create_wl_buffer(wl, wlr_buffer);
}
void destroy_wl_drm_syncobj_timeline(struct wlr_wl_drm_syncobj_timeline *timeline) {
wp_linux_drm_syncobj_timeline_v1_destroy(timeline->wl);
wlr_addon_finish(&timeline->addon);
wl_list_remove(&timeline->link);
free(timeline);
}
static void drm_syncobj_timeline_addon_destroy(struct wlr_addon *addon) {
struct wlr_wl_drm_syncobj_timeline *timeline = wl_container_of(addon, timeline, addon);
destroy_wl_drm_syncobj_timeline(timeline);
}
static const struct wlr_addon_interface drm_syncobj_timeline_addon_impl = {
.name = "wlr_wl_drm_syncobj_timeline",
.destroy = drm_syncobj_timeline_addon_destroy,
};
static struct wlr_wl_drm_syncobj_timeline *get_or_create_drm_syncobj_timeline(
struct wlr_wl_backend *wl, struct wlr_drm_syncobj_timeline *wlr_timeline) {
struct wlr_addon *addon =
wlr_addon_find(&wlr_timeline->addons, wl, &drm_syncobj_timeline_addon_impl);
if (addon != NULL) {
struct wlr_wl_drm_syncobj_timeline *timeline = wl_container_of(addon, timeline, addon);
return timeline;
}
struct wlr_wl_drm_syncobj_timeline *timeline = calloc(1, sizeof(*timeline));
if (timeline == NULL) {
return NULL;
}
timeline->base = wlr_timeline;
int fd = wlr_drm_syncobj_timeline_export(wlr_timeline);
if (fd < 0) {
free(timeline);
return NULL;
}
timeline->wl = wp_linux_drm_syncobj_manager_v1_import_timeline(wl->drm_syncobj_manager_v1, fd);
close(fd);
if (timeline->wl == NULL) {
free(timeline);
return NULL;
}
wlr_addon_init(&timeline->addon, &wlr_timeline->addons, wl, &drm_syncobj_timeline_addon_impl);
wl_list_insert(&wl->drm_syncobj_timelines, &timeline->link);
return timeline;
}
static bool update_title(struct wlr_wl_output *output, const char *title) {
struct wlr_output *wlr_output = &output->wlr_output;
@ -301,6 +422,28 @@ static bool output_test(struct wlr_output *wlr_output,
struct wlr_wl_output *output =
get_wl_output_from_output(wlr_output);
if (state->committed & WLR_OUTPUT_STATE_BUFFER) {
// If the size doesn't match, reject buffer (scaling is not currently
// supported but could be implemented with viewporter)
int pending_width, pending_height;
output_pending_resolution(wlr_output, state,
&pending_width, &pending_height);
if (state->buffer->width != pending_width ||
state->buffer->height != pending_height) {
wlr_log(WLR_DEBUG, "Primary buffer size mismatch");
return false;
}
// Source crop is also not currently supported
struct wlr_fbox src_box;
output_state_get_buffer_src_box(state, &src_box);
if (src_box.x != 0.0 || src_box.y != 0.0 ||
src_box.width != (double)state->buffer->width ||
src_box.height != (double)state->buffer->height) {
wlr_log(WLR_DEBUG, "Source crop not supported in wayland output");
return false;
}
}
uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE;
if (unsupported != 0) {
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
@ -334,6 +477,21 @@ static bool output_test(struct wlr_output *wlr_output,
return false;
}
if ((state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) &&
!(state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE)) {
wlr_log(WLR_DEBUG, "Signal timeline requires a wait timeline");
return false;
}
if ((state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) ||
(state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE)) {
struct wlr_dmabuf_attributes dmabuf;
if (!wlr_buffer_get_dmabuf(state->buffer, &dmabuf)) {
wlr_log(WLR_DEBUG, "Wait/signal timelines require DMA-BUFs");
return false;
}
}
if (state->committed & WLR_OUTPUT_STATE_LAYERS) {
// If we can't use a sub-surface for a layer, then we can't use a
// sub-surface for any layer underneath
@ -565,15 +723,15 @@ static const struct wl_callback_listener unmap_callback_listener = {
.done = unmap_callback_handle_done,
};
static bool output_commit(struct wlr_output *wlr_output,
const struct wlr_output_state *state) {
struct wlr_wl_output *output =
get_wl_output_from_output(wlr_output);
static bool output_commit(struct wlr_output *wlr_output, const struct wlr_output_state *state) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
if (!output_test(wlr_output, state)) {
return false;
}
struct wlr_wl_backend *wl = output->backend;
bool pending_enabled = output_pending_enabled(wlr_output, state);
if (wlr_output->enabled && !pending_enabled) {
@ -598,15 +756,28 @@ static bool output_commit(struct wlr_output *wlr_output,
wl_surface_commit(output->surface);
output->initialized = true;
wl_display_flush(output->backend->remote_display);
struct wl_event_queue *display_queue =
wl_proxy_get_queue((struct wl_proxy *)wl->remote_display);
wl_proxy_set_queue((struct wl_proxy *)output->xdg_surface, wl->busy_loop_queue);
wl_proxy_set_queue((struct wl_proxy *)output->xdg_toplevel, wl->busy_loop_queue);
wl_display_flush(wl->remote_display);
while (!output->configured) {
if (wl_display_dispatch(output->backend->remote_display) == -1) {
wlr_log(WLR_ERROR, "wl_display_dispatch() failed");
return false;
if (wl_display_dispatch_queue(wl->remote_display, wl->busy_loop_queue) == -1) {
wlr_log(WLR_ERROR, "wl_display_dispatch_queue() failed");
break;
}
}
wl_proxy_set_queue((struct wl_proxy *)output->xdg_surface, display_queue);
wl_proxy_set_queue((struct wl_proxy *)output->xdg_toplevel, display_queue);
if (!output->configured) {
return false;
}
}
struct wlr_wl_buffer *buffer = NULL;
if (state->committed & WLR_OUTPUT_STATE_BUFFER) {
const pixman_region32_t *damage = NULL;
if (state->committed & WLR_OUTPUT_STATE_DAMAGE) {
@ -614,8 +785,7 @@ static bool output_commit(struct wlr_output *wlr_output,
}
struct wlr_buffer *wlr_buffer = state->buffer;
struct wlr_wl_buffer *buffer =
get_or_create_wl_buffer(output->backend, wlr_buffer);
buffer = get_or_create_wl_buffer(wl, wlr_buffer);
if (buffer == NULL) {
return false;
}
@ -624,6 +794,63 @@ static bool output_commit(struct wlr_output *wlr_output,
damage_surface(output->surface, damage);
}
if (state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) {
struct wlr_wl_drm_syncobj_timeline *wait_timeline =
get_or_create_drm_syncobj_timeline(wl, state->wait_timeline);
struct wlr_wl_drm_syncobj_timeline *signal_timeline;
uint64_t signal_point;
if (state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) {
signal_timeline = get_or_create_drm_syncobj_timeline(wl, state->signal_timeline);
signal_point = state->signal_point;
} else {
if (buffer->fallback_signal_timeline == NULL) {
buffer->fallback_signal_timeline =
wlr_drm_syncobj_timeline_create(wl->drm_fd);
if (buffer->fallback_signal_timeline == NULL) {
return false;
}
}
signal_timeline =
get_or_create_drm_syncobj_timeline(wl, buffer->fallback_signal_timeline);
signal_point = ++buffer->fallback_signal_point;
}
if (wait_timeline == NULL || signal_timeline == NULL) {
return false;
}
if (output->drm_syncobj_surface_v1 == NULL) {
output->drm_syncobj_surface_v1 = wp_linux_drm_syncobj_manager_v1_get_surface(
wl->drm_syncobj_manager_v1, output->surface);
if (output->drm_syncobj_surface_v1 == NULL) {
return false;
}
}
uint32_t wait_point_hi = state->wait_point >> 32;
uint32_t wait_point_lo = state->wait_point & UINT32_MAX;
uint32_t signal_point_hi = signal_point >> 32;
uint32_t signal_point_lo = signal_point & UINT32_MAX;
wp_linux_drm_syncobj_surface_v1_set_acquire_point(output->drm_syncobj_surface_v1,
wait_timeline->wl, wait_point_hi, wait_point_lo);
wp_linux_drm_syncobj_surface_v1_set_release_point(output->drm_syncobj_surface_v1,
signal_timeline->wl, signal_point_hi, signal_point_lo);
if (!wlr_drm_syncobj_timeline_waiter_init(&buffer->drm_syncobj_waiter,
signal_timeline->base, signal_point, 0, wl->event_loop,
buffer_handle_drm_syncobj_ready)) {
return false;
}
buffer->has_drm_syncobj_waiter = true;
} else {
if (output->drm_syncobj_surface_v1 != NULL) {
wp_linux_drm_syncobj_surface_v1_destroy(output->drm_syncobj_surface_v1);
output->drm_syncobj_surface_v1 = NULL;
}
}
if ((state->committed & WLR_OUTPUT_STATE_LAYERS) &&
!commit_layers(output, state->layers, state->layers_len)) {
return false;
@ -637,9 +864,8 @@ static bool output_commit(struct wlr_output *wlr_output,
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
struct wp_presentation_feedback *wp_feedback = NULL;
if (output->backend->presentation != NULL) {
wp_feedback = wp_presentation_feedback(output->backend->presentation,
output->surface);
if (wl->presentation != NULL) {
wp_feedback = wp_presentation_feedback(wl->presentation, output->surface);
}
if (output->has_configure_serial) {
@ -672,7 +898,7 @@ static bool output_commit(struct wlr_output *wlr_output,
}
}
wl_display_flush(output->backend->remote_display);
wl_display_flush(wl->remote_display);
return true;
}
@ -728,6 +954,8 @@ static void output_destroy(struct wlr_output *wlr_output) {
return;
}
wlr_output_finish(wlr_output);
wl_list_remove(&output->link);
if (output->cursor.surface) {
@ -748,6 +976,9 @@ static void output_destroy(struct wlr_output *wlr_output) {
wl_callback_destroy(output->unmap_callback);
}
if (output->drm_syncobj_surface_v1) {
wp_linux_drm_syncobj_surface_v1_destroy(output->drm_syncobj_surface_v1);
}
if (output->zxdg_toplevel_decoration_v1) {
zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1);
}
@ -824,6 +1055,12 @@ static void xdg_surface_handle_configure(void *data,
output->has_configure_serial = true;
output->configure_serial = serial;
if (!output->wlr_output.enabled) {
// We're waiting for a configure event after an initial commit to enable
// the output. Do not notify the compositor about the requested state.
return;
}
struct wlr_output_state state;
wlr_output_state_init(&state);
wlr_output_state_set_custom_mode(&state, req_width, req_height, 0);

View file

@ -112,7 +112,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
.state = state,
.time_msec = time,
};
wl_signal_emit_mutable(&pointer->wlr_pointer.events.button, &event);
wlr_pointer_notify_button(&pointer->wlr_pointer, &event);
}
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,

View file

@ -27,12 +27,14 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
struct wlr_keyboard *keyboard = data;
int64_t time_msec = get_current_time_msec();
uint32_t *keycode_ptr;
wl_array_for_each(keycode_ptr, keys) {
struct wlr_keyboard_key_event event = {
.keycode = *keycode_ptr,
.state = WL_KEYBOARD_KEY_STATE_PRESSED,
.time_msec = get_current_time_msec(),
.time_msec = time_msec,
.update_state = false,
};
wlr_keyboard_notify_key(keyboard, &event);
@ -43,18 +45,12 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface) {
struct wlr_keyboard *keyboard = data;
size_t num_keycodes = keyboard->num_keycodes;
uint32_t pressed[num_keycodes + 1];
memcpy(pressed, keyboard->keycodes,
num_keycodes * sizeof(uint32_t));
for (size_t i = 0; i < num_keycodes; ++i) {
uint32_t keycode = pressed[i];
int64_t time_msec = get_current_time_msec();
while (keyboard->num_keycodes > 0) {
struct wlr_keyboard_key_event event = {
.keycode = keycode,
.keycode = keyboard->keycodes[keyboard->num_keycodes - 1],
.state = WL_KEYBOARD_KEY_STATE_RELEASED,
.time_msec = get_current_time_msec(),
.time_msec = time_msec,
.update_state = false,
};
wlr_keyboard_notify_key(keyboard, &event);

View file

@ -212,17 +212,10 @@ static int backend_get_drm_fd(struct wlr_backend *backend) {
return x11->drm_fd;
}
static uint32_t get_buffer_caps(struct wlr_backend *backend) {
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
return (x11->have_dri3 ? WLR_BUFFER_CAP_DMABUF : 0)
| (x11->have_shm ? WLR_BUFFER_CAP_SHM : 0);
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_drm_fd = backend_get_drm_fd,
.get_buffer_caps = get_buffer_caps,
};
bool wlr_backend_is_x11(struct wlr_backend *backend) {
@ -409,7 +402,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop,
wl_list_init(&x11->outputs);
x11->xcb = xcb_connect(x11_display, NULL);
if (!x11->xcb || xcb_connection_has_error(x11->xcb)) {
if (xcb_connection_has_error(x11->xcb)) {
wlr_log(WLR_ERROR, "Failed to open xcb connection");
goto error_x11;
}
@ -512,7 +505,10 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop,
xcb_present_query_version(x11->xcb, 1, 2);
xcb_present_query_version_reply_t *present_reply =
xcb_present_query_version_reply(x11->xcb, present_cookie, NULL);
if (!present_reply || present_reply->major_version < 1) {
if (!present_reply) {
wlr_log(WLR_ERROR, "Failed to query Present version");
goto error_display;
} else if (present_reply->major_version < 1) {
wlr_log(WLR_ERROR, "X11 does not support required Present version "
"(has %"PRIu32".%"PRIu32", want 1.0)",
present_reply->major_version, present_reply->minor_version);
@ -533,7 +529,10 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop,
xcb_xfixes_query_version(x11->xcb, 4, 0);
xcb_xfixes_query_version_reply_t *fixes_reply =
xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL);
if (!fixes_reply || fixes_reply->major_version < 4) {
if (!fixes_reply) {
wlr_log(WLR_ERROR, "Failed to query Xfixes version");
goto error_display;
} else if (fixes_reply->major_version < 4) {
wlr_log(WLR_ERROR, "X11 does not support required Xfixes version "
"(has %"PRIu32".%"PRIu32", want 4.0)",
fixes_reply->major_version, fixes_reply->minor_version);
@ -555,7 +554,10 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop,
xcb_input_xi_query_version(x11->xcb, 2, 0);
xcb_input_xi_query_version_reply_t *xi_reply =
xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL);
if (!xi_reply || xi_reply->major_version < 2) {
if (!xi_reply) {
wlr_log(WLR_ERROR, "Failed to query Xinput version");
goto error_display;
} else if (xi_reply->major_version < 2) {
wlr_log(WLR_ERROR, "X11 does not support required Xinput version "
"(has %"PRIu32".%"PRIu32", want 2.0)",
xi_reply->major_version, xi_reply->minor_version);
@ -564,6 +566,13 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop,
}
free(xi_reply);
if (x11->have_dri3) {
x11->backend.buffer_caps |= WLR_BUFFER_CAP_DMABUF;
}
if (x11->have_shm) {
x11->backend.buffer_caps |= WLR_BUFFER_CAP_SHM;
}
int fd = xcb_get_file_descriptor(x11->xcb);
uint32_t events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP;
x11->event_source = wl_event_loop_add_fd(loop, fd, events, x11_event, x11);

View file

@ -36,7 +36,7 @@ static void send_button_event(struct wlr_x11_output *output, uint32_t key,
.button = key,
.state = st,
};
wl_signal_emit_mutable(&output->pointer.events.button, &ev);
wlr_pointer_notify_button(&output->pointer, &ev);
wl_signal_emit_mutable(&output->pointer.events.frame, &output->pointer);
}

View file

@ -94,6 +94,8 @@ static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11;
wlr_output_finish(wlr_output);
pixman_region32_fini(&output->exposed);
wlr_pointer_finish(&output->pointer);
@ -129,6 +131,27 @@ static bool output_test(struct wlr_output *wlr_output,
return false;
}
if (state->committed & WLR_OUTPUT_STATE_BUFFER) {
// If the size doesn't match, reject buffer (scaling is not supported)
int pending_width, pending_height;
output_pending_resolution(wlr_output, state,
&pending_width, &pending_height);
if (state->buffer->width != pending_width ||
state->buffer->height != pending_height) {
wlr_log(WLR_DEBUG, "Primary buffer size mismatch");
return false;
}
// Source crop is not supported
struct wlr_fbox src_box;
output_state_get_buffer_src_box(state, &src_box);
if (src_box.x != 0.0 || src_box.y != 0.0 ||
src_box.width != (double)state->buffer->width ||
src_box.height != (double)state->buffer->height) {
wlr_log(WLR_DEBUG, "Source crop not supported in X11 output");
return false;
}
}
// All we can do to influence adaptive sync on the X11 backend is set the
// _VARIABLE_REFRESH window property like mesa automatically does. We don't
// have any control beyond that, so we set the state to enabled on creating
@ -747,9 +770,6 @@ void handle_x11_present_event(struct wlr_x11_backend *x11,
output->last_msc = complete_notify->msc;
struct timespec t;
timespec_from_nsec(&t, complete_notify->ust * 1000);
uint32_t flags = 0;
if (complete_notify->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) {
flags |= WLR_OUTPUT_PRESENT_ZERO_COPY;
@ -760,10 +780,10 @@ void handle_x11_present_event(struct wlr_x11_backend *x11,
.output = &output->wlr_output,
.commit_seq = complete_notify->serial,
.presented = presented,
.when = &t,
.seq = complete_notify->msc,
.flags = flags,
};
timespec_from_nsec(&present_event.when, complete_notify->ust * 1000);
wlr_output_send_present(&output->wlr_output, &present_event);
wlr_output_send_frame(&output->wlr_output);

View file

@ -12,6 +12,10 @@ wlroots reads these environment variables
renderers: gles2, pixman, vulkan)
* *WLR_RENDER_DRM_DEVICE*: specifies the DRM node to use for
hardware-accelerated renderers.
* *WLR_RENDER_NO_EXPLICIT_SYNC*: set to 1 to disable explicit synchronization
support in renderers.
* *WLR_RENDERER_FORCE_SOFTWARE*: set to 1 to force software rendering for GLES2
and Vulkan
* *WLR_EGL_NO_MODIFIERS*: set to 1 to disable format modifiers in EGL, this can
be used to understand and work around driver bugs.

View file

@ -24,6 +24,7 @@ struct cairo_buffer {
static void cairo_buffer_destroy(struct wlr_buffer *wlr_buffer) {
struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
wlr_buffer_finish(wlr_buffer);
cairo_surface_destroy(buffer->surface);
free(buffer);
}

View file

@ -1,258 +0,0 @@
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_fullscreen_shell_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <wlr/util/transform.h>
/**
* A minimal fullscreen-shell server. It only supports rendering.
*/
struct fullscreen_server {
struct wl_display *wl_display;
struct wlr_backend *backend;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_fullscreen_shell_v1 *fullscreen_shell;
struct wl_listener present_surface;
struct wlr_output_layout *output_layout;
struct wl_list outputs;
struct wl_listener new_output;
};
struct fullscreen_output {
struct wl_list link;
struct fullscreen_server *server;
struct wlr_output *wlr_output;
struct wlr_surface *surface;
struct wl_listener surface_destroy;
struct wl_listener frame;
};
struct render_data {
struct wlr_output *output;
struct wlr_render_pass *render_pass;
struct timespec *when;
};
static void render_surface(struct wlr_surface *surface,
int sx, int sy, void *data) {
struct render_data *rdata = data;
struct wlr_output *output = rdata->output;
struct wlr_texture *texture = wlr_surface_get_texture(surface);
if (texture == NULL) {
return;
}
struct wlr_box box = {
.x = sx * output->scale,
.y = sy * output->scale,
.width = surface->current.width * output->scale,
.height = surface->current.height * output->scale,
};
enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
transform = wlr_output_transform_compose(transform, output->transform);
wlr_render_pass_add_texture(rdata->render_pass, &(struct wlr_render_texture_options){
.texture = texture,
.dst_box = box,
.transform = transform,
});
wlr_surface_send_frame_done(surface, rdata->when);
}
static void output_handle_frame(struct wl_listener *listener, void *data) {
struct fullscreen_output *output =
wl_container_of(listener, output, frame);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int width, height;
wlr_output_effective_resolution(output->wlr_output, &width, &height);
struct wlr_output_state state;
wlr_output_state_init(&state);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &state, NULL,
NULL);
if (pass == NULL) {
return;
}
wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){
.color = { 0.3, 0.3, 0.3, 1.0 },
.box = { .width = width, .height = height },
});
if (output->surface != NULL) {
struct render_data rdata = {
.output = output->wlr_output,
.render_pass = pass,
.when = &now,
};
wlr_surface_for_each_surface(output->surface, render_surface, &rdata);
}
wlr_render_pass_submit(pass);
wlr_output_commit_state(output->wlr_output, &state);
wlr_output_state_finish(&state);
}
static void output_set_surface(struct fullscreen_output *output,
struct wlr_surface *surface);
static void output_handle_surface_destroy(struct wl_listener *listener,
void *data) {
struct fullscreen_output *output =
wl_container_of(listener, output, surface_destroy);
output_set_surface(output, NULL);
}
static void output_set_surface(struct fullscreen_output *output,
struct wlr_surface *surface) {
if (output->surface == surface) {
return;
}
if (output->surface != NULL) {
wl_list_remove(&output->surface_destroy.link);
output->surface = NULL;
}
if (surface != NULL) {
output->surface_destroy.notify = output_handle_surface_destroy;
wl_signal_add(&surface->events.destroy, &output->surface_destroy);
output->surface = surface;
}
wlr_log(WLR_DEBUG, "Presenting surface %p on output %s",
surface, output->wlr_output->name);
}
static void server_handle_new_output(struct wl_listener *listener, void *data) {
struct fullscreen_server *server =
wl_container_of(listener, server, new_output);
struct wlr_output *wlr_output = data;
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
struct fullscreen_output *output = calloc(1, sizeof(*output));
output->wlr_output = wlr_output;
output->server = server;
output->frame.notify = output_handle_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
wl_list_insert(&server->outputs, &output->link);
wlr_output_layout_add_auto(server->output_layout, wlr_output);
wlr_output_create_global(wlr_output, server->wl_display);
struct wlr_output_state state;
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, true);
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
if (mode != NULL) {
wlr_output_state_set_mode(&state, mode);
}
wlr_output_commit_state(wlr_output, &state);
wlr_output_state_finish(&state);
}
static void server_handle_present_surface(struct wl_listener *listener,
void *data) {
struct fullscreen_server *server =
wl_container_of(listener, server, present_surface);
struct wlr_fullscreen_shell_v1_present_surface_event *event = data;
struct fullscreen_output *output;
wl_list_for_each(output, &server->outputs, link) {
if (event->output == NULL || event->output == output->wlr_output) {
output_set_surface(output, event->surface);
}
}
}
int main(int argc, char *argv[]) {
wlr_log_init(WLR_DEBUG, NULL);
char *startup_cmd = NULL;
int c;
while ((c = getopt(argc, argv, "s:")) != -1) {
switch (c) {
case 's':
startup_cmd = optarg;
break;
default:
printf("usage: %s [-s startup-command]\n", argv[0]);
return EXIT_FAILURE;
}
}
if (optind < argc) {
printf("usage: %s [-s startup-command]\n", argv[0]);
return EXIT_FAILURE;
}
struct fullscreen_server server = {0};
server.wl_display = wl_display_create();
server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.wl_display), NULL);
server.renderer = wlr_renderer_autocreate(server.backend);
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
server.allocator = wlr_allocator_autocreate(server.backend,
server.renderer);
wlr_compositor_create(server.wl_display, 5, server.renderer);
server.output_layout = wlr_output_layout_create(server.wl_display);
wl_list_init(&server.outputs);
server.new_output.notify = server_handle_new_output;
wl_signal_add(&server.backend->events.new_output, &server.new_output);
server.fullscreen_shell = wlr_fullscreen_shell_v1_create(server.wl_display);
server.present_surface.notify = server_handle_present_surface;
wl_signal_add(&server.fullscreen_shell->events.present_surface,
&server.present_surface);
const char *socket = wl_display_add_socket_auto(server.wl_display);
if (!socket) {
wl_display_destroy(server.wl_display);
return EXIT_FAILURE;
}
if (!wlr_backend_start(server.backend)) {
wl_display_destroy(server.wl_display);
return EXIT_FAILURE;
}
setenv("WAYLAND_DISPLAY", socket, true);
if (startup_cmd != NULL) {
if (fork() == 0) {
execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL);
}
}
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s",
socket);
wl_display_run(server.wl_display);
wl_display_destroy_clients(server.wl_display);
wl_display_destroy(server.wl_display);
return 0;
}

View file

@ -25,10 +25,6 @@ compositors = {
'output-layout': {
'src': ['output-layout.c', 'cat.c'],
},
'fullscreen-shell': {
'src': 'fullscreen-shell.c',
'proto': ['fullscreen-shell-unstable-v1'],
},
'scene-graph': {
'src': 'scene-graph.c',
'proto': ['xdg-shell'],

View file

@ -87,16 +87,14 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
layers_arr.size / sizeof(struct wlr_output_layer_state));
if (!wlr_output_test_state(output->wlr_output, &output_state)) {
wlr_log(WLR_ERROR, "wlr_output_test() failed");
wlr_log(WLR_ERROR, "wlr_output_test_state() failed");
return;
}
int width, height;
wlr_output_effective_resolution(output->wlr_output, &width, &height);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &output_state,
NULL, NULL);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &output_state, NULL);
wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){
.box = { .width = width, .height = height },
.color = { 0.3, 0.3, 0.3, 1 },

View file

@ -114,7 +114,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
struct wlr_output_state output_state;
wlr_output_state_init(&output_state);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL);
wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){
.box = { .width = wlr_output->width, .height = wlr_output->height },

View file

@ -101,7 +101,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
struct wlr_output_state output_state;
wlr_output_state_init(&output_state);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL);
wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){
.box = { .width = wlr_output->width, .height = wlr_output->height },
.color = {

View file

@ -59,7 +59,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
struct wlr_output_state output_state;
wlr_output_state_init(&output_state);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL);
wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){
.box = { .width = wlr_output->width, .height = wlr_output->height },

View file

@ -63,7 +63,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
struct wlr_output_state state;
wlr_output_state_init(&state);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &state, NULL, NULL);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &state, NULL);
wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){
.box = { .width = wlr_output->width, .height = wlr_output->height },
.color = {

View file

@ -89,7 +89,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
struct wlr_output_state output_state;
wlr_output_state_init(&output_state);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL);
wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){
.box = { .width = wlr_output->width, .height = wlr_output->height },

View file

@ -76,7 +76,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
struct wlr_output_state output_state;
wlr_output_state_init(&output_state);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL);
struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL);
wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){
.box = { .width = width, .height = height },
.color = { 0.25, 0.25, 0.25, 1 },

View file

@ -1,13 +0,0 @@
#ifndef BACKEND_WLR_BACKEND_H
#define BACKEND_WLR_BACKEND_H
#include <wlr/backend.h>
/**
* Get the supported buffer capabilities.
*
* This functions returns a bitfield of supported wlr_buffer_cap.
*/
uint32_t backend_get_buffer_caps(struct wlr_backend *backend);
#endif

View file

@ -15,6 +15,11 @@
#include "backend/drm/properties.h"
#include "backend/drm/renderer.h"
struct wlr_drm_viewport {
struct wlr_fbox src_box;
struct wlr_box dst_box;
};
struct wlr_drm_plane {
uint32_t type;
uint32_t id;
@ -26,6 +31,8 @@ struct wlr_drm_plane {
struct wlr_drm_fb *queued_fb;
/* Buffer currently displayed on screen */
struct wlr_drm_fb *current_fb;
/* Viewport belonging to the last committed fb */
struct wlr_drm_viewport viewport;
struct wlr_drm_format_set formats;
@ -139,13 +146,20 @@ struct wlr_drm_connector_state {
bool active;
drmModeModeInfo mode;
struct wlr_drm_fb *primary_fb;
struct wlr_drm_viewport primary_viewport;
struct wlr_drm_fb *cursor_fb;
struct wlr_drm_syncobj_timeline *wait_timeline;
uint64_t wait_point;
// used by atomic
uint32_t mode_id;
uint32_t gamma_lut;
uint32_t fb_damage_clips;
int primary_in_fence_fd, out_fence_fd;
bool vrr_enabled;
uint32_t colorspace;
uint32_t hdr_output_metadata;
};
/**
@ -164,6 +178,8 @@ struct wlr_drm_page_flip {
struct wl_list link; // wlr_drm_connector.page_flips
struct wlr_drm_page_flip_connector *connectors;
size_t connectors_len;
// True if DRM_MODE_PAGE_FLIP_ASYNC was set
bool async;
};
struct wlr_drm_page_flip_connector {
@ -198,6 +214,10 @@ struct wlr_drm_connector {
// Last committed page-flip
struct wlr_drm_page_flip *pending_page_flip;
// Atomic modesetting only
uint32_t colorspace;
uint32_t hdr_output_metadata;
int32_t refresh;
};
@ -211,7 +231,6 @@ void scan_drm_connectors(struct wlr_drm_backend *state,
void scan_drm_leases(struct wlr_drm_backend *drm);
bool commit_drm_device(struct wlr_drm_backend *drm,
const struct wlr_backend_output_state *states, size_t states_len, bool test_only);
void restore_drm_device(struct wlr_drm_backend *drm);
int handle_drm_event(int fd, uint32_t mask, void *data);
void destroy_drm_connector(struct wlr_drm_connector *conn);
bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn);

View file

@ -2,6 +2,10 @@
#define BACKEND_DRM_FB_H
#include <stdbool.h>
#include <stdint.h>
#include <wlr/util/addon.h>
struct wlr_drm_format_set;
struct wlr_drm_fb {
struct wlr_buffer *wlr_buf;

View file

@ -22,8 +22,6 @@ struct wlr_drm_interface {
bool (*commit)(struct wlr_drm_backend *drm,
const struct wlr_drm_device_state *state,
struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only);
// Turn off everything
bool (*reset)(struct wlr_drm_backend *drm);
};
extern const struct wlr_drm_interface atomic_iface;

View file

@ -26,6 +26,8 @@ struct wlr_drm_connector_props {
// atomic-modesetting only
uint32_t crtc_id;
uint32_t colorspace;
uint32_t hdr_output_metadata;
};
struct wlr_drm_crtc_props {
@ -38,6 +40,7 @@ struct wlr_drm_crtc_props {
uint32_t active;
uint32_t mode_id;
uint32_t out_fence_ptr;
};
struct wlr_drm_plane_props {
@ -61,6 +64,7 @@ struct wlr_drm_plane_props {
uint32_t fb_damage_clips;
uint32_t hotspot_x;
uint32_t hotspot_y;
uint32_t in_fence_fd;
};
bool get_drm_connector_props(int fd, uint32_t id,

View file

@ -20,6 +20,9 @@ struct wlr_drm_renderer {
struct wlr_drm_surface {
struct wlr_drm_renderer *renderer;
struct wlr_swapchain *swapchain;
struct wlr_drm_syncobj_timeline *timeline;
uint64_t point;
};
bool init_drm_renderer(struct wlr_drm_backend *drm,
@ -32,7 +35,8 @@ bool init_drm_surface(struct wlr_drm_surface *surf,
void finish_drm_surface(struct wlr_drm_surface *surf);
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
struct wlr_buffer *buffer);
struct wlr_buffer *buffer,
struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point);
bool drm_plane_pick_render_format(struct wlr_drm_plane *plane,
struct wlr_drm_format *fmt, struct wlr_drm_renderer *renderer);

View file

@ -18,26 +18,24 @@ const char *drm_connector_status_str(drmModeConnection status);
void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay,
float vrefresh);
// Part of match_obj
enum {
UNMATCHED = (uint32_t)-1,
SKIP = (uint32_t)-2,
};
// Part of match_connectors_with_crtcs
#define UNMATCHED ((uint32_t)-1)
/*
* Tries to match some DRM objects with some other DRM resource.
* e.g. Match CRTCs with Encoders, CRTCs with Planes.
/**
* Tries to match DRM connectors with DRM CRTCs.
*
* objs contains a bit array which resources it can be matched with.
* e.g. Bit 0 set means can be matched with res[0]
* conns contains an array of bitmasks describing compatible CRTCs. For
* instance bit 0 set in an connector element means that it's compatible with
* CRTC 0.
*
* res contains an index of which objs it is matched with or UNMATCHED.
* prev_crtcs contains connector indices each CRTC was previously matched with,
* or UNMATCHED.
*
* This solution is left in out.
* Returns the total number of matched solutions.
* new_crtcs is populated with the new connector indices.
*/
size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs],
size_t num_res, const uint32_t res[static restrict num_res],
uint32_t out[static restrict num_res]);
void match_connectors_with_crtcs(size_t num_conns,
const uint32_t conns[static restrict num_conns],
size_t num_crtcs, const uint32_t prev_crtcs[static restrict num_crtcs],
uint32_t new_crtcs[static restrict num_crtcs]);
#endif

View file

@ -12,8 +12,6 @@
#include <wlr/types/wlr_tablet_tool.h>
#include <wlr/types/wlr_touch.h>
#include "config.h"
struct wlr_libinput_backend {
struct wlr_backend backend;

View file

@ -14,6 +14,7 @@
#include <wlr/types/wlr_tablet_tool.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/render/drm_format_set.h>
#include <wlr/render/drm_syncobj.h>
struct wlr_wl_backend {
struct wlr_backend backend;
@ -21,6 +22,7 @@ struct wlr_wl_backend {
/* local state */
bool started;
struct wl_event_loop *event_loop;
struct wl_event_queue *busy_loop_queue;
struct wl_list outputs;
int drm_fd;
struct wl_list buffers; // wlr_wl_buffer.link
@ -40,6 +42,8 @@ struct wlr_wl_backend {
struct wp_presentation *presentation;
struct wl_shm *shm;
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1;
struct wp_linux_drm_syncobj_manager_v1 *drm_syncobj_manager_v1;
struct wl_list drm_syncobj_timelines; // wlr_wl_drm_syncobj_timeline.link
struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1;
struct wl_list seats; // wlr_wl_seat.link
struct zwp_tablet_manager_v2 *tablet_manager;
@ -58,6 +62,19 @@ struct wlr_wl_buffer {
bool released;
struct wl_list link; // wlr_wl_backend.buffers
struct wl_listener buffer_destroy;
bool has_drm_syncobj_waiter;
struct wlr_drm_syncobj_timeline_waiter drm_syncobj_waiter;
struct wlr_drm_syncobj_timeline *fallback_signal_timeline;
uint64_t fallback_signal_point;
};
struct wlr_wl_drm_syncobj_timeline {
struct wlr_drm_syncobj_timeline *base;
struct wlr_addon addon;
struct wl_list link; // wlr_wl_backend.drm_syncobj_timelines
struct wp_linux_drm_syncobj_timeline_v1 *wl;
};
struct wlr_wl_presentation_feedback {
@ -88,6 +105,7 @@ struct wlr_wl_output {
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1;
struct wp_linux_drm_syncobj_surface_v1 *drm_syncobj_surface_v1;
struct wl_list presentation_feedbacks;
char *title;
@ -190,6 +208,7 @@ bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl,
uint32_t global_name);
void destroy_wl_seat(struct wlr_wl_seat *seat);
void destroy_wl_buffer(struct wlr_wl_buffer *buffer);
void destroy_wl_drm_syncobj_timeline(struct wlr_wl_drm_syncobj_timeline *timeline);
extern const struct wlr_pointer_impl wl_pointer_impl;
extern const struct wlr_tablet_pad_impl wl_tablet_pad_impl;

View file

@ -1,9 +0,0 @@
#ifndef RENDER_ALLOCATOR_ALLOCATOR_H
#define RENDER_ALLOCATOR_ALLOCATOR_H
#include <wlr/render/allocator.h>
struct wlr_allocator *allocator_autocreate_with_drm_fd(
uint32_t backend_caps, struct wlr_renderer *renderer, int drm_fd);
#endif

View file

@ -1,9 +1,9 @@
#ifndef RENDER_ALLOCATOR_DRM_DUMB_H
#define RENDER_ALLOCATOR_DRM_DUMB_H
#include <wlr/render/allocator.h>
#include <wlr/render/dmabuf.h>
#include <wlr/types/wlr_buffer.h>
#include "render/allocator/allocator.h"
struct wlr_drm_dumb_buffer {
struct wlr_buffer base;

View file

@ -2,9 +2,9 @@
#define RENDER_ALLOCATOR_GBM_H
#include <gbm.h>
#include <wlr/render/allocator.h>
#include <wlr/render/dmabuf.h>
#include <wlr/types/wlr_buffer.h>
#include "render/allocator/allocator.h"
struct wlr_gbm_buffer {
struct wlr_buffer base;

View file

@ -1,8 +1,8 @@
#ifndef RENDER_ALLOCATOR_SHM_H
#define RENDER_ALLOCATOR_SHM_H
#include <wlr/render/allocator.h>
#include <wlr/types/wlr_buffer.h>
#include "render/allocator/allocator.h"
struct wlr_shm_buffer {
struct wlr_buffer base;

View file

@ -0,0 +1,23 @@
#ifndef RENDER_ALLOCATOR_UDMABUF_H
#define RENDER_ALLOCATOR_UDMABUF_H
#include <wlr/types/wlr_buffer.h>
#include <wlr/render/allocator.h>
struct wlr_udmabuf_buffer {
struct wlr_buffer base;
size_t size;
struct wlr_shm_attributes shm;
struct wlr_dmabuf_attributes dmabuf;
};
struct wlr_udmabuf_allocator {
struct wlr_allocator base;
int fd;
};
struct wlr_allocator *wlr_udmabuf_allocator_create(void);
#endif

View file

@ -2,30 +2,14 @@
#define RENDER_COLOR_H
#include <stdint.h>
#include <wlr/render/color.h>
#include <wlr/util/addon.h>
/**
* The formula is approximated via a 3D look-up table. A 3D LUT is a
* three-dimensional array where each element is an RGB triplet. The flat lut_3d
* array has a length of dim_len³.
*
* Color channel values in the range [0.0, 1.0] are mapped linearly to
* 3D LUT indices such that 0.0 maps exactly to the first element and 1.0 maps
* exactly to the last element in each dimension.
*
* The offset of the RGB triplet given red, green and blue indices r_index,
* g_index and b_index is:
*
* offset = 3 * (r_index + dim_len * g_index + dim_len * dim_len * b_index)
*/
struct wlr_color_transform_lut3d {
float *lut_3d;
size_t dim_len;
};
enum wlr_color_transform_type {
COLOR_TRANSFORM_SRGB,
COLOR_TRANSFORM_LUT_3D,
COLOR_TRANSFORM_INVERSE_EOTF,
COLOR_TRANSFORM_LCMS2,
COLOR_TRANSFORM_LUT_3X1D,
COLOR_TRANSFORM_PIPELINE,
};
struct wlr_color_transform {
@ -33,7 +17,84 @@ struct wlr_color_transform {
struct wlr_addon_set addons; // per-renderer helper state
enum wlr_color_transform_type type;
struct wlr_color_transform_lut3d lut3d;
};
struct wlr_color_transform_inverse_eotf {
struct wlr_color_transform base;
enum wlr_color_transfer_function tf;
};
/**
* The formula is approximated via three 1D look-up tables. The flat lut_3x1d
* array has a length of 3 * dim.
*
* The offset of a color value for a given channel and color index is:
*
* offset = channel_index * dim + color_index
*/
struct wlr_color_transform_lut_3x1d {
struct wlr_color_transform base;
uint16_t *lut_3x1d;
size_t dim;
};
struct wlr_color_transform_pipeline {
struct wlr_color_transform base;
struct wlr_color_transform **transforms;
size_t len;
};
void wlr_color_transform_init(struct wlr_color_transform *tr,
enum wlr_color_transform_type type);
/**
* Get a struct wlr_color_transform_lcms2 from a generic struct wlr_color_transform.
* Asserts that the base type is COLOR_TRANSFORM_LCMS2.
*/
struct wlr_color_transform_lcms2 *color_transform_lcms2_from_base(
struct wlr_color_transform *tr);
void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr);
/**
* Evaluate a LCMS2 color transform for a given RGB triplet.
*/
void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr,
float out[static 3], const float in[static 3]);
/**
* Gets a wlr_color_transform_inverse_eotf from a generic wlr_color_transform.
* Asserts that the base type is COLOR_TRANSFORM_INVERSE_EOTF
*/
struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_base(
struct wlr_color_transform *tr);
/**
* Get a struct wlr_color_transform_lut_3x1d from a generic
* struct wlr_color_transform. Asserts that the base type is
* COLOR_TRANSFORM_LUT_3X1D.
*/
struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
struct wlr_color_transform *tr);
/**
* Obtain primaries values from a well-known primaries name.
*/
void wlr_color_primaries_from_named(struct wlr_color_primaries *out,
enum wlr_color_named_primaries named);
/**
* Compute the matrix to convert RGB color values to CIE 1931 XYZ.
*/
void wlr_color_primaries_to_xyz(const struct wlr_color_primaries *primaries, float matrix[static 9]);
/**
* Get default luminances for a transfer function.
*/
void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_function tf,
struct wlr_color_luminances *lum);
#endif

View file

@ -38,6 +38,10 @@ struct wlr_egl {
PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT;
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT;
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT;
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR;
PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID;
PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR;
} procs;
bool has_modifiers;
@ -105,4 +109,12 @@ bool wlr_egl_make_current(struct wlr_egl *egl, struct wlr_egl_context *save_cont
bool wlr_egl_unset_current(struct wlr_egl *egl);
EGLSyncKHR wlr_egl_create_sync(struct wlr_egl *egl, int fence_fd);
void wlr_egl_destroy_sync(struct wlr_egl *egl, EGLSyncKHR sync);
int wlr_egl_dup_fence_fd(struct wlr_egl *egl, EGLSyncKHR sync);
bool wlr_egl_wait_sync(struct wlr_egl *egl, EGLSyncKHR sync);
#endif

View file

@ -5,7 +5,6 @@
#include <GLES2/gl2ext.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
@ -138,6 +137,8 @@ struct wlr_gles2_render_pass {
float projection_matrix[9];
struct wlr_egl_context prev_ctx;
struct wlr_gles2_render_timer *timer;
struct wlr_drm_syncobj_timeline *signal_timeline;
uint64_t signal_point;
};
bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer,
@ -169,6 +170,7 @@ void push_gles2_debug_(struct wlr_gles2_renderer *renderer,
void pop_gles2_debug(struct wlr_gles2_renderer *renderer);
struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer,
struct wlr_egl_context *prev_ctx, struct wlr_gles2_render_timer *timer);
struct wlr_egl_context *prev_ctx, struct wlr_gles2_render_timer *timer,
struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point);
#endif

View file

@ -7,6 +7,7 @@
#include <vulkan/vulkan.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/render/wlr_texture.h>
#include <wlr/render/color.h>
#include <wlr/render/drm_format_set.h>
#include <wlr/render/interface.h>
#include <wlr/util/addon.h>
@ -40,6 +41,7 @@ struct wlr_vk_device {
int drm_fd;
bool sync_file_import_export;
bool implicit_sync_interop;
bool sampler_ycbcr_conversion;
@ -150,6 +152,9 @@ struct wlr_vk_pipeline_layout {
enum wlr_vk_texture_transform {
WLR_VK_TEXTURE_TRANSFORM_IDENTITY = 0,
WLR_VK_TEXTURE_TRANSFORM_SRGB = 1,
WLR_VK_TEXTURE_TRANSFORM_ST2084_PQ = 2,
WLR_VK_TEXTURE_TRANSFORM_GAMMA22 = 3,
WLR_VK_TEXTURE_TRANSFORM_BT1886 = 4,
};
enum wlr_vk_shader_source {
@ -160,8 +165,12 @@ enum wlr_vk_shader_source {
// Constants used to pick the color transform for the blend-to-output
// fragment shader. Must match those in shaders/output.frag
enum wlr_vk_output_transform {
WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 0,
WLR_VK_OUTPUT_TRANSFORM_LUT3D = 1,
WLR_VK_OUTPUT_TRANSFORM_IDENTITY = 0,
WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 1,
WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ = 2,
WLR_VK_OUTPUT_TRANSFORM_LUT3D = 3,
WLR_VK_OUTPUT_TRANSFORM_INVERSE_GAMMA22 = 4,
WLR_VK_OUTPUT_TRANSFORM_INVERSE_BT1886 = 5,
};
struct wlr_vk_pipeline_key {
@ -190,13 +199,24 @@ struct wlr_vk_render_format_setup {
bool use_blending_buffer;
VkRenderPass render_pass;
VkPipeline output_pipe_identity;
VkPipeline output_pipe_srgb;
VkPipeline output_pipe_pq;
VkPipeline output_pipe_lut3d;
VkPipeline output_pipe_gamma22;
VkPipeline output_pipe_bt1886;
struct wlr_vk_renderer *renderer;
struct wl_list pipelines; // struct wlr_vk_pipeline.link
};
// Final output framebuffer and image view
struct wlr_vk_render_buffer_out {
VkImageView image_view;
VkFramebuffer framebuffer;
bool transitioned;
};
// Renderer-internal represenation of an wlr_buffer imported for rendering.
struct wlr_vk_render_buffer {
struct wlr_buffer *wlr_buffer;
@ -208,36 +228,40 @@ struct wlr_vk_render_buffer {
uint32_t mem_count;
VkImage image;
// Framebuffer and image view for rendering directly onto the buffer image,
// without any color transform.
struct {
struct wlr_vk_render_buffer_out out;
struct wlr_vk_render_format_setup *render_setup;
} linear;
// Framebuffer and image view for rendering directly onto the buffer image.
// This requires that the image support an _SRGB VkFormat, and does
// not work with color transforms.
struct {
struct wlr_vk_render_buffer_out out;
struct wlr_vk_render_format_setup *render_setup;
VkImageView image_view;
VkFramebuffer framebuffer;
bool transitioned;
} srgb;
// Framebuffer, image view, and blending image to render indirectly
// onto the buffer image. This works for general image types and permits
// color transforms.
struct {
struct wlr_vk_render_buffer_out out;
struct wlr_vk_render_format_setup *render_setup;
VkImageView image_view;
VkFramebuffer framebuffer;
bool transitioned;
VkImage blend_image;
VkImageView blend_image_view;
VkDeviceMemory blend_memory;
VkDescriptorSet blend_descriptor_set;
struct wlr_vk_descriptor_pool *blend_attachment_pool;
bool blend_transitioned;
} plain;
} two_pass;
};
bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer,
bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffer,
const struct wlr_dmabuf_attributes *dmabuf, bool srgb);
bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer,
const struct wlr_dmabuf_attributes *dmabuf);
struct wlr_vk_command_buffer {
@ -253,6 +277,8 @@ struct wlr_vk_command_buffer {
// For DMA-BUF implicit sync interop, may be NULL
VkSemaphore binary_semaphore;
struct wl_array wait_semaphores; // VkSemaphore
};
#define VULKAN_COMMAND_BUFFERS_CAP 64
@ -331,7 +357,15 @@ struct wlr_vk_vert_pcr_data {
float uv_size[2];
};
struct wlr_vk_frag_texture_pcr_data {
float matrix[4][4]; // only a 3x3 subset is used
float alpha;
float luminance_multiplier;
};
struct wlr_vk_frag_output_pcr_data {
float matrix[4][4]; // only a 3x3 subset is used
float luminance_multiplier;
float lut_3d_offset;
float lut_3d_scale;
};
@ -339,6 +373,7 @@ struct wlr_vk_frag_output_pcr_data {
struct wlr_vk_texture_view {
struct wl_list link; // struct wlr_vk_texture.views
const struct wlr_vk_pipeline_layout *layout;
bool srgb;
VkDescriptorSet ds;
VkImageView image_view;
@ -353,7 +388,7 @@ struct wlr_vk_pipeline_layout *get_or_create_pipeline_layout(
const struct wlr_vk_pipeline_layout_key *key);
struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(
struct wlr_vk_texture *texture,
const struct wlr_vk_pipeline_layout *layout);
const struct wlr_vk_pipeline_layout *layout, bool srgb);
// Creates a vulkan renderer for the given device.
struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev);
@ -367,17 +402,34 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer);
// finished execution.
bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer);
struct wlr_vk_render_pass_texture {
struct wlr_vk_texture *texture;
struct wlr_drm_syncobj_timeline *wait_timeline;
uint64_t wait_point;
};
struct wlr_vk_render_pass {
struct wlr_render_pass base;
struct wlr_vk_renderer *renderer;
struct wlr_vk_render_buffer *render_buffer;
struct wlr_vk_render_buffer_out *render_buffer_out;
struct wlr_vk_render_format_setup *render_setup;
struct wlr_vk_command_buffer *command_buffer;
struct rect_union updated_region;
VkPipeline bound_pipeline;
float projection[9];
bool failed;
bool srgb_pathway; // if false, rendering via intermediate blending buffer
bool two_pass; // rendering via intermediate blending buffer
struct wlr_color_transform *color_transform;
bool has_primaries;
struct wlr_color_primaries primaries;
struct wlr_drm_syncobj_timeline *signal_timeline;
uint64_t signal_point;
struct wl_array textures; // struct wlr_vk_render_pass_texture
};
struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer,
@ -419,8 +471,10 @@ bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb,
struct wlr_vk_renderer *renderer);
bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer,
struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb);
bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture);
struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb,
struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point);
bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture,
int sync_file_fds[static WLR_DMABUF_MAX_PLANES]);
bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer,
VkFormat src_format, VkImage src_image,
@ -436,13 +490,12 @@ struct wlr_vk_texture {
VkDeviceMemory memories[WLR_DMABUF_MAX_PLANES];
VkImage image;
const struct wlr_vk_format *format;
enum wlr_vk_texture_transform transform;
struct wlr_vk_command_buffer *last_used_cb; // to track when it can be destroyed
bool dmabuf_imported;
bool owned; // if dmabuf_imported: whether we have ownership of the image
bool transitioned; // if dma_imported: whether we transitioned it away from preinit
bool has_alpha; // whether the image is has alpha channel
bool using_mutable_srgb; // is this accessed through _SRGB format view
bool using_mutable_srgb; // can be accessed through _SRGB format view
struct wl_list foreign_link; // wlr_vk_renderer.foreign_textures
struct wl_list destroy_link; // wlr_vk_command_buffer.destroy_textures
struct wl_list link; // wlr_vk_renderer.textures
@ -450,10 +503,8 @@ struct wlr_vk_texture {
// If imported from a wlr_buffer
struct wlr_buffer *buffer;
struct wlr_addon buffer_addon;
// For DMA-BUF implicit sync interop
VkSemaphore foreign_semaphores[WLR_DMABUF_MAX_PLANES];
struct wl_list views; // struct wlr_vk_texture_ds.link
struct wl_list views; // struct wlr_vk_texture_view.link
};
struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture);
@ -485,6 +536,7 @@ struct wlr_vk_shared_buffer {
VkDeviceSize buf_size;
void *cpu_mapping;
struct wl_array allocs; // struct wlr_vk_allocation
int64_t last_used_ms;
};
// Suballocated range on a buffer.
@ -500,6 +552,7 @@ struct wlr_vk_color_transform {
struct wl_list link; // wlr_vk_renderer, list of all color transforms
struct {
size_t dim;
VkImage image;
VkImageView image_view;
VkDeviceMemory memory;

View file

@ -9,7 +9,7 @@
struct wlr_renderer *renderer_autocreate_with_drm_fd(int drm_fd);
/**
* Get the supported render formats. Buffers allocated with a format from this
* list may be attached via wlr_renderer_begin_with_buffer.
* list may be used with wlr_renderer_begin_buffer_pass().
*/
const struct wlr_drm_format_set *wlr_renderer_get_render_formats(
struct wlr_renderer *renderer);

View file

@ -50,15 +50,6 @@ struct wlr_dmabuf_buffer *dmabuf_buffer_create(
*/
bool dmabuf_buffer_drop(struct wlr_dmabuf_buffer *buffer);
/**
* Check whether a buffer is fully opaque.
*
* When true is returned, the buffer is guaranteed to be fully opaque, but the
* reverse is not true: false may be returned in cases where the buffer is fully
* opaque.
*/
bool buffer_is_opaque(struct wlr_buffer *buffer);
/**
* Creates a struct wlr_client_buffer from a given struct wlr_buffer by creating
* a texture from it, and copying its struct wl_resource.

View file

@ -1,15 +0,0 @@
#ifndef TYPES_WLR_MATRIX_H
#define TYPES_WLR_MATRIX_H
#include <wlr/types/wlr_matrix.h>
/**
* Writes a 2D orthographic projection matrix to mat of (width, height) with a
* specified wl_output_transform.
*
* Equivalent to glOrtho(0, width, 0, height, 1, -1) with the transform applied.
*/
void matrix_projection(float mat[static 9], int width, int height,
enum wl_output_transform transform);
#endif

View file

@ -8,6 +8,8 @@ void output_pending_resolution(struct wlr_output *output,
const struct wlr_output_state *state, int *width, int *height);
bool output_pending_enabled(struct wlr_output *output,
const struct wlr_output_state *state);
const struct wlr_output_image_description *output_pending_image_description(
struct wlr_output *output, const struct wlr_output_state *state);
bool output_pick_format(struct wlr_output *output,
const struct wlr_drm_format_set *display_formats,
@ -18,11 +20,18 @@ bool output_ensure_buffer(struct wlr_output *output,
bool output_cursor_set_texture(struct wlr_output_cursor *cursor,
struct wlr_texture *texture, bool own_texture, const struct wlr_fbox *src_box,
int dst_width, int dst_height, enum wl_output_transform transform,
int32_t hotspot_x, int32_t hotspot_y);
int32_t hotspot_x, int32_t hotspot_y, struct wlr_drm_syncobj_timeline *wait_timeline,
uint64_t wait_point);
void output_defer_present(struct wlr_output *output, struct wlr_output_event_present event);
bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state);
void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state);
void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state);
void output_state_get_buffer_src_box(const struct wlr_output_state *state,
struct wlr_fbox *out);
void output_state_get_buffer_dst_box(const struct wlr_output_state *state,
struct wlr_box *out);
#endif

View file

@ -1,7 +1,7 @@
#ifndef TYPES_WLR_REGION_H
#define TYPES_WLR_REGION_H
#include <wlr/types/wlr_region.h>
#include <stdint.h>
struct wl_client;

View file

@ -5,6 +5,8 @@
struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node);
void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height);
void scene_surface_set_clip(struct wlr_scene_surface *surface, struct wlr_box *clip);
#endif

View file

@ -1,10 +1,5 @@
/*
* This is a deprecated interface of wlroots. It will be removed in a future
* version.
*/
#ifndef WLR_TYPES_WLR_MATRIX_H
#define WLR_TYPES_WLR_MATRIX_H
#ifndef UTIL_MATRIX_H
#define UTIL_MATRIX_H
#include <wayland-server-protocol.h>
@ -17,17 +12,12 @@ void wlr_matrix_identity(float mat[static 9]);
void wlr_matrix_multiply(float mat[static 9], const float a[static 9],
const float b[static 9]);
void wlr_matrix_transpose(float mat[static 9], const float a[static 9]);
/** Writes a 2D translation matrix to mat of magnitude (x, y) */
void wlr_matrix_translate(float mat[static 9], float x, float y);
/** Writes a 2D scale matrix to mat of magnitude (x, y) */
void wlr_matrix_scale(float mat[static 9], float x, float y);
/** Writes a 2D rotation matrix to mat at an angle of rad radians */
void wlr_matrix_rotate(float mat[static 9], float rad);
/** Writes a transformation matrix which applies the specified
* wl_output_transform to mat */
void wlr_matrix_transform(float mat[static 9],
@ -38,7 +28,22 @@ void wlr_matrix_transform(float mat[static 9],
* rotation. The result is written to mat, which can be applied to each
* coordinate of the box to get a new coordinate from [-1,1]. */
void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box,
enum wl_output_transform transform, float rotation,
const float projection[static 9]);
enum wl_output_transform transform, const float projection[static 9]);
/**
* Writes a 2D orthographic projection matrix to mat of (width, height) with a
* specified wl_output_transform.
*
* Equivalent to glOrtho(0, width, 0, height, 1, -1) with the transform applied.
*/
void matrix_projection(float mat[static 9], int width, int height,
enum wl_output_transform transform);
/**
* Compute the inverse of a matrix.
*
* The matrix needs to be inversible.
*/
void matrix_invert(float out[static 9], float m[static 9]);
#endif

14
include/util/mem.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef UTIL_MEM_H
#define UTIL_MEM_H
#include <stdbool.h>
#include <stddef.h>
/**
* Allocate a new block of memory and copy *src to it, then store the address
* of the new allocation in *out. Returns true if it worked, or false if
* allocation failed.
*/
bool memdup(void *out, const void *src, size_t size);
#endif // UTIL_MEM_H

View file

@ -4,6 +4,8 @@
#include <stdint.h>
#include <time.h>
static const long NSEC_PER_SEC = 1000000000;
/**
* Get the current time, in milliseconds.
*/

View file

@ -25,10 +25,21 @@ struct wlr_backend_output_state {
/**
* A backend provides a set of input and output devices.
*
* Buffer capabilities and features can change over the lifetime of a backend,
* for instance when a child backend is added to a multi-backend.
*/
struct wlr_backend {
const struct wlr_backend_impl *impl;
// Bitfield of supported buffer capabilities (see enum wlr_buffer_cap)
uint32_t buffer_caps;
struct {
// Whether wait/signal timelines are supported in output commits
bool timeline;
} features;
struct {
/** Raised when destroyed */
struct wl_signal destroy;
@ -54,13 +65,12 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_event_loop *loop,
struct wlr_session **session_ptr);
/**
* Start the backend. This may signal new_input or new_output immediately, but
* may also wait until the display's event loop begins. Returns false on
* failure.
* may also wait until the event loop is started. Returns false on failure.
*/
bool wlr_backend_start(struct wlr_backend *backend);
/**
* Destroy the backend and clean up all of its resources. Normally called
* automatically when the struct wl_display is destroyed.
* automatically when the event loop is destroyed.
*/
void wlr_backend_destroy(struct wlr_backend *backend);
/**

View file

@ -18,7 +18,6 @@ struct wlr_backend_impl {
bool (*start)(struct wlr_backend *backend);
void (*destroy)(struct wlr_backend *backend);
int (*get_drm_fd)(struct wlr_backend *backend);
uint32_t (*get_buffer_caps)(struct wlr_backend *backend);
bool (*test)(struct wlr_backend *backend,
const struct wlr_backend_output_state *states, size_t states_len);
bool (*commit)(struct wlr_backend *backend,

View file

@ -43,11 +43,6 @@ struct wlr_session {
*/
bool active;
/*
* 0 if virtual terminals are not supported
* i.e. seat != "seat0"
*/
unsigned vtnr;
char seat[256];
struct udev *udev;
@ -60,13 +55,16 @@ struct wlr_session {
struct wl_list devices; // wlr_device.link
struct wl_event_loop *event_loop;
struct wl_listener event_loop_destroy;
struct {
struct wl_signal active;
struct wl_signal add_drm_card; // struct wlr_session_add_event
struct wl_signal destroy;
} events;
struct {
struct wl_listener event_loop_destroy;
} WLR_PRIVATE;
};
struct wlr_session_add_event {

View file

@ -1,19 +1,76 @@
#ifndef WLR_CONFIG_H
#define WLR_CONFIG_H
/**
* Whether the DRM backend is compile-time enabled. Equivalent to the
* pkg-config "have_drm_backend" variable.
*
* Required for <wlr/backend/drm.h>.
*/
#mesondefine WLR_HAS_DRM_BACKEND
/**
* Whether the libinput backend is compile-time enabled. Equivalent to the
* pkg-config "have_libinput_backend" vartiable.
*
* Required for <wlr/backend/libinput.h>.
*/
#mesondefine WLR_HAS_LIBINPUT_BACKEND
/**
* Whether the X11 backend is compile-time enabled. Equivalent to the
* pkg-config "have_x11_backend" variable.
*
* Required for <wlr/backend/x11.h>.
*/
#mesondefine WLR_HAS_X11_BACKEND
/**
* Whether the GLES2 renderer is compile-time enabled. Equivalent to the
* pkg-config "have_gles2_renderer" variable.
*
* Required for <wlr/render/gles2.h>.
*/
#mesondefine WLR_HAS_GLES2_RENDERER
/**
* Whether the Vulkan renderer is compile-time enabled. Equivalent to the
* pkg-config "have_vulkan_renderer" variable.
*
* Required for <wlr/render/vulkan.h>.
*/
#mesondefine WLR_HAS_VULKAN_RENDERER
/**
* Whether the GBM allocator is compile-time enabled. Equivalent to the
* pkg-config "have_gbm_allocator" variable.
*/
#mesondefine WLR_HAS_GBM_ALLOCATOR
/**
* Whether the udmabuf allocator is compile-time enabled. Equivalent to the
* pkg-config "have_udmabuf_allocator" variable.
*/
#mesondefine WLR_HAS_UDMABUF_ALLOCATOR
/**
* Whether Xwayland support is compile-time enabled. Equivalent to the
* pkg-config "have_xwayland" variable.
*
* Required for <wlr/xwayland/>.
*/
#mesondefine WLR_HAS_XWAYLAND
/**
* Whether session support is compile-time enabled. Equivalent to the
* pkg-config "have_session" variable.
*
* Required for <wlr/backend/session.h>.
*/
#mesondefine WLR_HAS_SESSION
/**
* Whether traditional color management support is compile-time enabled.
* Equivalent to the pkg-config "have_color_management" variable.
*
* Required for ICC profile support in <wlr/render/color.h>.
*/
#mesondefine WLR_HAS_COLOR_MANAGEMENT
#endif

View file

@ -36,6 +36,11 @@ struct wlr_buffer_resource_interface {
void wlr_buffer_init(struct wlr_buffer *buffer,
const struct wlr_buffer_impl *impl, int width, int height);
/**
* Emit the destroy event and clean up common buffer state.
*/
void wlr_buffer_finish(struct wlr_buffer *buffer);
/**
* Allows the registration of a struct wl_resource implementation.
*

View file

@ -0,0 +1,45 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_INTERFACES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H
#define WLR_INTERFACES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
struct wlr_ext_image_copy_capture_frame_v1;
struct wlr_swapchain;
struct wlr_renderer;
struct wlr_seat;
struct wlr_ext_image_capture_source_v1_interface {
// TODO: drop with_cursors flag
void (*start)(struct wlr_ext_image_capture_source_v1 *source, bool with_cursors);
void (*stop)(struct wlr_ext_image_capture_source_v1 *source);
void (*schedule_frame)(struct wlr_ext_image_capture_source_v1 *source);
void (*copy_frame)(struct wlr_ext_image_capture_source_v1 *source,
struct wlr_ext_image_copy_capture_frame_v1 *dst_frame,
struct wlr_ext_image_capture_source_v1_frame_event *frame_event);
struct wlr_ext_image_capture_source_v1_cursor *(*get_pointer_cursor)(
struct wlr_ext_image_capture_source_v1 *source, struct wlr_seat *seat);
};
void wlr_ext_image_capture_source_v1_init(struct wlr_ext_image_capture_source_v1 *source,
const struct wlr_ext_image_capture_source_v1_interface *impl);
void wlr_ext_image_capture_source_v1_finish(struct wlr_ext_image_capture_source_v1 *source);
bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_capture_source_v1 *source,
struct wl_client *client, uint32_t new_id);
bool wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(
struct wlr_ext_image_capture_source_v1 *source,
struct wlr_swapchain *swapchain, struct wlr_renderer *renderer);
void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_source_v1_cursor *source_cursor,
const struct wlr_ext_image_capture_source_v1_interface *impl);
void wlr_ext_image_capture_source_v1_cursor_finish(struct wlr_ext_image_capture_source_v1_cursor *source_cursor);
#endif

View file

@ -108,6 +108,10 @@ struct wlr_output_impl {
void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
const struct wlr_output_impl *impl, struct wl_event_loop *event_loop,
const struct wlr_output_state *state);
/**
* Emit the destroy event and clean up common output state.
*/
void wlr_output_finish(struct wlr_output *output);
/**
* Notify compositors that they need to submit a new frame in order to apply
* output changes.

View file

@ -19,4 +19,7 @@ void wlr_pointer_init(struct wlr_pointer *pointer,
const struct wlr_pointer_impl *impl, const char *name);
void wlr_pointer_finish(struct wlr_pointer *pointer);
void wlr_pointer_notify_button(struct wlr_pointer *pointer,
struct wlr_pointer_button_event *event);
#endif

View file

@ -10,8 +10,100 @@
#define WLR_RENDER_COLOR_H
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
/**
* Well-known color primaries.
*/
enum wlr_color_named_primaries {
WLR_COLOR_NAMED_PRIMARIES_SRGB = 1 << 0,
WLR_COLOR_NAMED_PRIMARIES_BT2020 = 1 << 1,
};
/**
* Well-known color transfer functions.
*/
enum wlr_color_transfer_function {
WLR_COLOR_TRANSFER_FUNCTION_SRGB = 1 << 0,
WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ = 1 << 1,
WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR = 1 << 2,
WLR_COLOR_TRANSFER_FUNCTION_GAMMA22 = 1 << 3,
WLR_COLOR_TRANSFER_FUNCTION_BT1886 = 1 << 4,
};
/**
* Specifies alpha blending modes. Note that premultiplied_electrical
* is the default, so there is no "none" or "unset" value.
*/
enum wlr_alpha_mode {
WLR_COLOR_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL,
WLR_COLOR_ALPHA_MODE_PREMULTIPLIED_OPTICAL,
WLR_COLOR_ALPHA_MODE_STRAIGHT,
};
/**
* Well-known color encodings, each representing a set of matrix coefficients
* used to convert that particular YCbCr encoding to RGB. NONE means the
* value is unset or unknown.
*/
enum wlr_color_encoding {
WLR_COLOR_ENCODING_NONE,
WLR_COLOR_ENCODING_IDENTITY,
WLR_COLOR_ENCODING_BT709,
WLR_COLOR_ENCODING_FCC,
WLR_COLOR_ENCODING_BT601,
WLR_COLOR_ENCODING_SMPTE240,
WLR_COLOR_ENCODING_BT2020,
WLR_COLOR_ENCODING_BT2020_CL,
WLR_COLOR_ENCODING_ICTCP,
};
/**
* Specifies whether a particular color-encoding uses full- or limited-range
* values. NONE means the value is unset or unknown.
*/
enum wlr_color_range {
WLR_COLOR_RANGE_NONE,
WLR_COLOR_RANGE_LIMITED,
WLR_COLOR_RANGE_FULL,
};
/**
* Chroma sample locations, corresponding to Chroma420SampleLocType code
* points in H.273. NONE means the value is unset or unknown.
*/
enum wlr_color_chroma_location {
WLR_COLOR_CHROMA_LOCATION_NONE,
WLR_COLOR_CHROMA_LOCATION_TYPE0,
WLR_COLOR_CHROMA_LOCATION_TYPE1,
WLR_COLOR_CHROMA_LOCATION_TYPE2,
WLR_COLOR_CHROMA_LOCATION_TYPE3,
WLR_COLOR_CHROMA_LOCATION_TYPE4,
WLR_COLOR_CHROMA_LOCATION_TYPE5,
};
/**
* CIE 1931 xy chromaticity coordinates.
*/
struct wlr_color_cie1931_xy {
float x, y;
};
/**
* Color primaries and white point describing a color volume.
*/
struct wlr_color_primaries {
struct wlr_color_cie1931_xy red, green, blue, white;
};
/**
* Luminance range and reference white luminance level, in cd/m².
*/
struct wlr_color_luminances {
float min, max, reference;
};
/**
* A color transformation formula, which maps a linear color space with
* sRGB primaries to an output color space.
@ -36,15 +128,30 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_icc(
const void *data, size_t size);
/**
* Initialize a color transformation to apply sRGB encoding.
* Returns NULL on failure.
* Initialize a color transformation to apply EOTF¹ encoding. Returns
* NULL on failure.
*/
struct wlr_color_transform *wlr_color_transform_init_srgb(void);
struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf(
enum wlr_color_transfer_function tf);
/**
* Initialize a color transformation to apply three 1D look-up tables. dim
* is the number of elements in each individual LUT. Returns NULL on failure.
*/
struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim,
const uint16_t *r, const uint16_t *g, const uint16_t *b);
/**
* Initialize a color transformation to apply a sequence of color transforms
* one after another.
*/
struct wlr_color_transform *wlr_color_transform_init_pipeline(
struct wlr_color_transform **transforms, size_t len);
/**
* Increase the reference count of the color transform by 1.
*/
void wlr_color_transform_ref(struct wlr_color_transform *tr);
struct wlr_color_transform *wlr_color_transform_ref(struct wlr_color_transform *tr);
/**
* Reduce the reference count of the color transform by 1; freeing it and
@ -52,4 +159,10 @@ void wlr_color_transform_ref(struct wlr_color_transform *tr);
*/
void wlr_color_transform_unref(struct wlr_color_transform *tr);
/**
* Evaluate a color transform for a given RGB triplet.
*/
void wlr_color_transform_eval(struct wlr_color_transform *tr,
float out[static 3], const float in[static 3]);
#endif

View file

@ -69,6 +69,9 @@ void wlr_drm_format_set_finish(struct wlr_drm_format_set *set);
const struct wlr_drm_format *wlr_drm_format_set_get(
const struct wlr_drm_format_set *set, uint32_t format);
bool wlr_drm_format_set_remove(struct wlr_drm_format_set *set, uint32_t format,
uint64_t modifier);
bool wlr_drm_format_set_has(const struct wlr_drm_format_set *set,
uint32_t format, uint64_t modifier);

View file

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <wayland-server-core.h>
#include <wlr/util/addon.h>
/**
* A synchronization timeline.
@ -29,20 +30,24 @@ struct wlr_drm_syncobj_timeline {
int drm_fd;
uint32_t handle;
// private state
struct wlr_addon_set addons;
size_t n_refs;
struct {
size_t n_refs;
} WLR_PRIVATE;
};
struct wlr_drm_syncobj_timeline_waiter;
typedef void (*wlr_drm_syncobj_timeline_ready_callback)(
struct wlr_drm_syncobj_timeline_waiter *waiter);
struct wlr_drm_syncobj_timeline_waiter {
struct {
struct wl_signal ready;
} events;
// private state
int ev_fd;
struct wl_event_source *event_source;
int ev_fd;
struct wl_event_source *event_source;
wlr_drm_syncobj_timeline_ready_callback callback;
} WLR_PRIVATE;
};
/**
@ -62,6 +67,17 @@ struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_ref(struct wlr_drm_syn
* Unreference a synchronization timeline.
*/
void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline);
/**
* Export a drm_syncobj FD from a timeline.
*/
int wlr_drm_syncobj_timeline_export(struct wlr_drm_syncobj_timeline *timeline);
/**
* Transfer a point from a timeline to another.
*
* Both timelines must have been created with the same DRM FD.
*/
bool wlr_drm_syncobj_timeline_transfer(struct wlr_drm_syncobj_timeline *dst,
uint64_t dst_point, struct wlr_drm_syncobj_timeline *src, uint64_t src_point);
/**
* Check if a timeline point has been signalled or has materialized.
*
@ -78,10 +94,12 @@ bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline,
* Asynchronously wait for a timeline point.
*
* See wlr_drm_syncobj_timeline_check() for a definition of flags.
*
* A callback must be provided that will be invoked when the waiter has finished.
*/
bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter *waiter,
struct wlr_drm_syncobj_timeline *timeline, uint64_t point, uint32_t flags,
struct wl_event_loop *loop);
struct wl_event_loop *loop, wlr_drm_syncobj_timeline_ready_callback callback);
/**
* Cancel a timeline waiter.
*/

View file

@ -12,6 +12,7 @@
#include <pixman.h>
#include <stdint.h>
#include <wayland-server-core.h>
#include <wlr/render/color.h>
#include <wlr/util/box.h>
struct wlr_renderer;
@ -30,9 +31,23 @@ struct wlr_render_timer;
struct wlr_buffer_pass_options {
/* Timer to measure the duration of the render pass */
struct wlr_render_timer *timer;
/* Color transform to apply to the output of the render pass,
* leave NULL to indicate sRGB/no custom transform */
/* Color transform to apply to the output of the render pass.
* Leave NULL to indicate the default transform (Gamma 2.2 encoding for
* sRGB monitors) */
struct wlr_color_transform *color_transform;
/** Primaries describing the color volume of the destination buffer */
const struct wlr_color_primaries *primaries;
/* Signal a timeline synchronization point when the render pass completes.
*
* When a compositor provides a signal timeline, the renderer may skip
* implicit signal synchronization.
*
* Support for this feature is advertised by features.timeline in
* struct wlr_renderer.
*/
struct wlr_drm_syncobj_timeline *signal_timeline;
uint64_t signal_point;
};
/**
@ -88,6 +103,21 @@ struct wlr_render_texture_options {
enum wlr_scale_filter_mode filter_mode;
/* Blend mode */
enum wlr_render_blend_mode blend_mode;
/* Transfer function the source texture is encoded with */
enum wlr_color_transfer_function transfer_function;
/* Primaries describing the color volume of the source texture */
const struct wlr_color_primaries *primaries;
/* Wait for a timeline synchronization point before texturing.
*
* When a compositor provides a wait timeline, the renderer may skip
* implicit wait synchronization.
*
* Support for this feature is advertised by features.timeline in
* struct wlr_renderer.
*/
struct wlr_drm_syncobj_timeline *wait_timeline;
uint64_t wait_point;
};
/**

View file

@ -10,9 +10,10 @@
struct wlr_swapchain_slot {
struct wlr_buffer *buffer;
bool acquired; // waiting for release
int age;
struct wl_listener release;
struct {
struct wl_listener release;
} WLR_PRIVATE;
};
struct wlr_swapchain {
@ -23,7 +24,9 @@ struct wlr_swapchain {
struct wlr_swapchain_slot slots[WLR_SWAPCHAIN_CAP];
struct wl_listener allocator_destroy;
struct {
struct wl_listener allocator_destroy;
} WLR_PRIVATE;
};
struct wlr_swapchain *wlr_swapchain_create(
@ -36,21 +39,12 @@ void wlr_swapchain_destroy(struct wlr_swapchain *swapchain);
* The returned buffer is locked. When the caller is done with it, they must
* unlock it by calling wlr_buffer_unlock.
*/
struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain,
int *age);
struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain);
/**
* Returns true if this buffer has been created by this swapchain, and false
* otherwise.
*/
bool wlr_swapchain_has_buffer(struct wlr_swapchain *swapchain,
struct wlr_buffer *buffer);
/**
* Mark the buffer as submitted for presentation. This needs to be called by
* swap chain users on frame boundaries.
*
* If the buffer hasn't been created via the swap chain, the call is ignored.
*/
void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain,
struct wlr_buffer *buffer);
#endif

View file

@ -41,15 +41,25 @@ struct wlr_renderer {
} events;
struct {
/**
* Whether color transforms are supported for input textures
*/
bool input_color_transform;
/**
* Does the renderer support color transforms on its output?
*/
bool output_color_transform;
/**
* Whether wait/signal timelines are supported.
*
* See struct wlr_drm_syncobj_timeline.
*/
bool timeline;
} features;
// private state
const struct wlr_renderer_impl *impl;
struct {
const struct wlr_renderer_impl *impl;
} WLR_PRIVATE;
};
/**

View file

@ -20,9 +20,9 @@ struct wlr_alpha_modifier_surface_v1_state {
struct wlr_alpha_modifier_v1 {
struct wl_global *global;
// private state
struct wl_listener display_destroy;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_alpha_modifier_v1 *wlr_alpha_modifier_v1_create(struct wl_display *display);

View file

@ -17,11 +17,15 @@
struct wlr_buffer;
struct wlr_renderer;
/**
* Shared-memory attributes for a buffer.
*/
struct wlr_shm_attributes {
int fd;
uint32_t format;
int width, height, stride;
off_t offset;
uint32_t format; // FourCC code, see DRM_FORMAT_* in <drm_fourcc.h>
int width, height;
int stride; // Number of bytes between consecutive pixel lines
off_t offset; // Offset in bytes of the first pixel in FD
};
/**
@ -105,6 +109,15 @@ bool wlr_buffer_get_shm(struct wlr_buffer *buffer,
*/
struct wlr_buffer *wlr_buffer_try_from_resource(struct wl_resource *resource);
/**
* Check whether a buffer is fully opaque.
*
* When true is returned, the buffer is guaranteed to be fully opaque, but the
* reverse is not true: false may be returned in cases where the buffer is fully
* opaque.
*/
bool wlr_buffer_is_opaque(struct wlr_buffer *buffer);
/**
* Buffer data pointer access flags.
*/
@ -130,6 +143,12 @@ enum wlr_buffer_data_ptr_access_flag {
*/
bool wlr_buffer_begin_data_ptr_access(struct wlr_buffer *buffer, uint32_t flags,
void **data, uint32_t *format, size_t *stride);
/**
* Indicate that a pointer to a buffer's underlying memory will no longer be
* used.
*
* This function must be called after wlr_buffer_begin_data_ptr_access().
*/
void wlr_buffer_end_data_ptr_access(struct wlr_buffer *buffer);
/**
@ -148,12 +167,12 @@ struct wlr_client_buffer {
*/
struct wlr_buffer *source;
// private state
struct {
struct wl_listener source_destroy;
struct wl_listener renderer_destroy;
struct wl_listener source_destroy;
struct wl_listener renderer_destroy;
size_t n_ignore_locks;
size_t n_ignore_locks;
} WLR_PRIVATE;
};
/**
@ -162,4 +181,29 @@ struct wlr_client_buffer {
*/
struct wlr_client_buffer *wlr_client_buffer_get(struct wlr_buffer *buffer);
/**
* A single-pixel buffer. Used by clients to draw solid-color rectangles.
*/
struct wlr_single_pixel_buffer_v1 {
struct wlr_buffer base;
// Full-scale for each component is UINT32_MAX
uint32_t r, g, b, a;
struct {
struct wl_resource *resource;
struct wl_listener release;
// Packed little-endian DRM_FORMAT_ARGB8888. Used for data_ptr_access
uint8_t argb8888[4];
} WLR_PRIVATE;
};
/**
* If the wlr_buffer is a wlr_single_pixel_buffer_v1 then unwrap it.
* Otherwise, returns NULL.
*/
struct wlr_single_pixel_buffer_v1 *wlr_single_pixel_buffer_v1_try_from_buffer(
struct wlr_buffer *buffer);
#endif

View file

@ -0,0 +1,122 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H
#define WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H
#include <wayland-server-core.h>
#include <wayland-protocols/color-management-v1-enum.h>
#include <wlr/render/color.h>
struct wlr_surface;
struct wlr_image_description_v1_data {
uint32_t tf_named; // enum wp_color_manager_v1_transfer_function, zero if unset
uint32_t primaries_named; // enum wp_color_manager_v1_primaries, zero if unset
bool has_mastering_display_primaries;
struct wlr_color_primaries mastering_display_primaries;
bool has_mastering_luminance;
struct {
float min, max; // cd/m²
} mastering_luminance;
uint32_t max_cll, max_fall; // cd/m², zero if unset
};
struct wlr_color_manager_v1_features {
bool icc_v2_v4;
bool parametric;
bool set_primaries;
bool set_tf_power;
bool set_luminances;
bool set_mastering_display_primaries;
bool extended_target_volume;
bool windows_scrgb;
};
struct wlr_color_manager_v1_options {
struct wlr_color_manager_v1_features features;
const enum wp_color_manager_v1_render_intent *render_intents;
size_t render_intents_len;
const enum wp_color_manager_v1_transfer_function *transfer_functions;
size_t transfer_functions_len;
const enum wp_color_manager_v1_primaries *primaries;
size_t primaries_len;
};
struct wlr_color_manager_v1 {
struct wl_global *global;
struct {
struct wl_signal destroy;
} events;
struct {
struct wlr_color_manager_v1_features features;
enum wp_color_manager_v1_render_intent *render_intents;
size_t render_intents_len;
enum wp_color_manager_v1_transfer_function *transfer_functions;
size_t transfer_functions_len;
enum wp_color_manager_v1_primaries *primaries;
size_t primaries_len;
struct wl_list outputs; // wlr_color_management_output_v1.link
struct wl_list surface_feedbacks; // wlr_color_management_surface_feedback_v1.link
uint32_t last_image_desc_identity;
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *display,
uint32_t version, const struct wlr_color_manager_v1_options *options);
const struct wlr_image_description_v1_data *
wlr_surface_get_image_description_v1_data(struct wlr_surface *surface);
void wlr_color_manager_v1_set_surface_preferred_image_description(
struct wlr_color_manager_v1 *manager, struct wlr_surface *surface,
const struct wlr_image_description_v1_data *data);
/**
* Convert a protocol transfer function to enum wlr_color_transfer_function.
* Aborts if there is no matching wlroots entry.
*/
enum wlr_color_transfer_function
wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_function tf);
/**
* Convert an enum wlr_color_transfer_function value into a protocol transfer function.
*/
enum wp_color_manager_v1_transfer_function
wlr_color_manager_v1_transfer_function_from_wlr(enum wlr_color_transfer_function tf);
/**
* Convert a protocol named primaries to enum wlr_color_named_primaries.
* Aborts if there is no matching wlroots entry.
*/
enum wlr_color_named_primaries
wlr_color_manager_v1_primaries_to_wlr(enum wp_color_manager_v1_primaries primaries);
/**
* Convert an enum wlr_color_named_primaries value into protocol primaries.
*/
enum wp_color_manager_v1_primaries
wlr_color_manager_v1_primaries_from_wlr(enum wlr_color_named_primaries primaries);
#endif

View file

@ -0,0 +1,93 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H
#define WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H
#include <wayland-server-core.h>
#include <wayland-protocols/color-representation-v1-enum.h>
#include <wlr/render/color.h>
struct wlr_surface;
// Supported coefficients and range are always paired together
struct wlr_color_representation_v1_coeffs_and_range {
enum wp_color_representation_surface_v1_coefficients coeffs;
enum wp_color_representation_surface_v1_range range;
};
struct wlr_color_representation_manager_v1 {
struct wl_global *global;
struct {
// Manager is being destroyed
struct wl_signal destroy;
} events;
struct {
enum wp_color_representation_surface_v1_alpha_mode
*supported_alpha_modes;
size_t supported_alpha_modes_len;
struct wlr_color_representation_v1_coeffs_and_range
*supported_coeffs_and_ranges;
size_t supported_coeffs_and_ranges_len;
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
// Options used when initialising a wlr_color_representation_manager_v1
struct wlr_color_representation_v1_options {
enum wp_color_representation_surface_v1_alpha_mode
*supported_alpha_modes;
size_t supported_alpha_modes_len;
const struct wlr_color_representation_v1_coeffs_and_range
*supported_coeffs_and_ranges;
size_t supported_coeffs_and_ranges_len;
};
struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_create(
struct wl_display *display, uint32_t version,
const struct wlr_color_representation_v1_options *options);
// This is all the color-representation state which can be attached to a
// surface, double-buffered and made current on commit
struct wlr_color_representation_v1_surface_state {
// The enum premultiplied_electrical has value zero and is defined
// to be the default if unspecified.
enum wp_color_representation_surface_v1_alpha_mode alpha_mode;
// If zero then indicates unset, otherwise values correspond to
// enum wp_color_representation_surface_v1_coefficients
uint32_t coefficients;
// If zero then indicates unset, otherwise values correspond to
// enum wp_color_representation_surface_v1_range
uint32_t range;
// If zero then indicates unset, otherwise values correspond to
// enum wp_color_representation_surface_v1_chroma_location
uint32_t chroma_location;
};
// Get the current color representation state committed to a surface
const struct wlr_color_representation_v1_surface_state *wlr_color_representation_v1_get_surface_state(
struct wlr_surface *surface);
enum wlr_alpha_mode wlr_color_representation_v1_alpha_mode_to_wlr(
enum wp_color_representation_surface_v1_alpha_mode wp_val);
enum wlr_color_encoding wlr_color_representation_v1_color_encoding_to_wlr(
enum wp_color_representation_surface_v1_coefficients wp_val);
enum wlr_color_range wlr_color_representation_v1_color_range_to_wlr(
enum wp_color_representation_surface_v1_range wp_val);
enum wlr_color_chroma_location wlr_color_representation_v1_chroma_location_to_wlr(
enum wp_color_representation_surface_v1_chroma_location wp_val);
#endif // WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H

View file

@ -14,10 +14,11 @@
#include <stdint.h>
#include <time.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/addon.h>
#include <wlr/util/box.h>
struct wlr_surface;
enum wlr_surface_state_field {
WLR_SURFACE_STATE_BUFFER = 1 << 0,
WLR_SURFACE_STATE_SURFACE_DAMAGE = 1 << 1,
@ -97,6 +98,13 @@ struct wlr_surface_role {
* such object exists.
*/
void (*commit)(struct wlr_surface *surface);
/**
* Called when the surface is mapped. May be NULL.
*
* If the role is represented by an object, this is only called if
* such object exists.
*/
void (*map)(struct wlr_surface *surface);
/**
* Called when the surface is unmapped. May be NULL.
*
@ -115,8 +123,11 @@ struct wlr_surface_output {
struct wlr_output *output;
struct wl_list link; // wlr_surface.current_outputs
struct wl_listener bind;
struct wl_listener destroy;
struct {
struct wl_listener bind;
struct wl_listener destroy;
} WLR_PRIVATE;
};
struct wlr_surface {
@ -180,28 +191,47 @@ struct wlr_surface {
struct wl_resource *role_resource;
struct {
/**
* Signals that the client has sent a wl_surface.commit request.
*
* The state to be committed can be accessed in wlr_surface.pending.
*
* The commit may not be applied immediately, in which case it's marked
* as "cached" and put into a queue. See wlr_surface_lock_pending().
*/
struct wl_signal client_commit;
/**
* Signals that a commit has been applied.
*
* The new state can be accessed in wlr_surface.current.
*/
struct wl_signal commit;
/**
* The `map` event signals that the surface has a non-null buffer
* committed and is ready to be displayed.
* Signals that the surface has a non-null buffer committed and is
* ready to be displayed.
*/
struct wl_signal map;
/**
* The `unmap` event signals that the surface shouldn't be displayed
* anymore. This can happen when a null buffer is committed,
* the associated role object is destroyed, or when the role-specific
* conditions for the surface to be mapped no longer apply.
* Signals that the surface shouldn't be displayed anymore. This can
* happen when a null buffer is committed, the associated role object
* is destroyed, or when the role-specific conditions for the surface
* to be mapped no longer apply.
*/
struct wl_signal unmap;
/**
* Signals that a new child sub-surface has been added.
*
* Note: unlike other new_* signals, new_subsurface is emitted when
* the subsurface is added to the parent surface's current state,
* not when the object is created.
*/
struct wl_signal new_subsurface; // struct wlr_subsurface
/**
* Signals that the surface is being destroyed.
*/
struct wl_signal destroy;
} events;
@ -210,33 +240,33 @@ struct wlr_surface {
struct wlr_addon_set addons;
void *data;
// private state
struct wl_listener role_resource_destroy;
struct {
int32_t scale;
enum wl_output_transform transform;
int width, height;
int buffer_width, buffer_height;
} previous;
struct wl_listener role_resource_destroy;
bool unmap_commit;
struct {
int32_t scale;
enum wl_output_transform transform;
int width, height;
int buffer_width, buffer_height;
} previous;
bool opaque;
bool unmap_commit;
bool handling_commit;
bool pending_rejected;
bool opaque;
int32_t preferred_buffer_scale;
bool preferred_buffer_transform_sent;
enum wl_output_transform preferred_buffer_transform;
bool handling_commit;
bool pending_rejected;
struct wl_list synced; // wlr_surface_synced.link
size_t synced_len;
int32_t preferred_buffer_scale;
bool preferred_buffer_transform_sent;
enum wl_output_transform preferred_buffer_transform;
struct wl_resource *pending_buffer_resource;
struct wl_listener pending_buffer_resource_destroy;
struct wl_list synced; // wlr_surface_synced.link
size_t synced_len;
struct wl_resource *pending_buffer_resource;
struct wl_listener pending_buffer_resource_destroy;
} WLR_PRIVATE;
};
struct wlr_renderer;
@ -245,13 +275,15 @@ struct wlr_compositor {
struct wl_global *global;
struct wlr_renderer *renderer; // may be NULL
struct wl_listener display_destroy;
struct wl_listener renderer_destroy;
struct {
struct wl_signal new_surface;
struct wl_signal destroy;
} events;
struct {
struct wl_listener display_destroy;
struct wl_listener renderer_destroy;
} WLR_PRIVATE;
};
typedef void (*wlr_surface_iterator_func_t)(struct wlr_surface *surface,
@ -381,7 +413,7 @@ void wlr_surface_send_frame_done(struct wlr_surface *surface,
* surface coordinates.
* X and y may be negative, if there are subsurfaces with negative position.
*/
void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box);
void wlr_surface_get_extents(struct wlr_surface *surface, struct wlr_box *box);
/**
* Get the struct wlr_surface corresponding to a wl_surface resource.
@ -453,6 +485,8 @@ void wlr_surface_set_preferred_buffer_scale(struct wlr_surface *surface,
void wlr_surface_set_preferred_buffer_transform(struct wlr_surface *surface,
enum wl_output_transform transform);
struct wlr_surface_synced;
/**
* Implementation for struct wlr_surface_synced.
*
@ -469,6 +503,11 @@ struct wlr_surface_synced_impl {
void (*finish_state)(void *state);
// Move a state. If NULL, memcpy() is used.
void (*move_state)(void *dst, void *src);
// Called when the state is committed. If NULL, this is a no-op.
// If an object is a surface role object which has state synchronized with
// the surface state, the role commit hook should be preferred over this.
void (*commit)(struct wlr_surface_synced *synced);
};
/**

View file

@ -10,7 +10,7 @@
#define WLR_TYPES_WLR_CONTENT_TYPE_V1_H
#include <wayland-server-core.h>
#include "content-type-v1-protocol.h"
#include <wayland-protocols/content-type-v1-enum.h>
struct wlr_surface;
@ -23,9 +23,9 @@ struct wlr_content_type_manager_v1 {
void *data;
// private state
struct wl_listener display_destroy;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_content_type_manager_v1 *wlr_content_type_manager_v1_create(

View file

@ -11,9 +11,9 @@
#include <wayland-server-core.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
struct wlr_input_device;
struct wlr_surface;
struct wlr_xcursor_manager;
/**

View file

@ -10,7 +10,7 @@
#define WLR_TYPES_WLR_CURSOR_SHAPE_V1_H
#include <wayland-server-core.h>
#include "cursor-shape-v1-protocol.h"
#include <wayland-protocols/cursor-shape-v1-enum.h>
/**
* Manager for the cursor-shape-v1 protocol.
@ -28,9 +28,9 @@ struct wlr_cursor_shape_manager_v1 {
void *data;
// private state
struct wl_listener display_destroy;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
enum wlr_cursor_shape_manager_v1_device_type {

View file

@ -15,32 +15,27 @@
#include <pixman.h>
#include <wayland-server-core.h>
/* For triple buffering, a history of two frames is required. */
#define WLR_DAMAGE_RING_PREVIOUS_LEN 2
struct wlr_box;
struct wlr_damage_ring_buffer {
struct wlr_buffer *buffer;
struct wl_listener destroy;
pixman_region32_t damage;
struct wlr_damage_ring *ring;
struct wl_list link; // wlr_damage_ring.buffers
struct {
struct wl_listener destroy;
} WLR_PRIVATE;
};
struct wlr_damage_ring {
int32_t width, height;
// Difference between the current buffer and the previous one
pixman_region32_t current;
// private state
pixman_region32_t previous[WLR_DAMAGE_RING_PREVIOUS_LEN];
size_t previous_idx;
struct wl_list buffers; // wlr_damage_ring_buffer.link
struct {
struct wl_list buffers; // wlr_damage_ring_buffer.link
} WLR_PRIVATE;
};
void wlr_damage_ring_init(struct wlr_damage_ring *ring);
@ -48,30 +43,17 @@ void wlr_damage_ring_init(struct wlr_damage_ring *ring);
void wlr_damage_ring_finish(struct wlr_damage_ring *ring);
/**
* Set ring bounds and damage the ring fully.
*
* Next time damage will be added, it will be cropped to the ring bounds.
* If at least one of the dimensions is 0, bounds are removed.
*
* By default, a damage ring doesn't have bounds.
* Add a region to the current damage. The region must be in the buffer-local
* coordinate space.
*/
void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring,
int32_t width, int32_t height);
/**
* Add a region to the current damage.
*
* Returns true if the region intersects the ring bounds, false otherwise.
*/
bool wlr_damage_ring_add(struct wlr_damage_ring *ring,
void wlr_damage_ring_add(struct wlr_damage_ring *ring,
const pixman_region32_t *damage);
/**
* Add a box to the current damage.
*
* Returns true if the box intersects the ring bounds, false otherwise.
* Add a box to the current damage. The box must be in the buffer-local
* coordinate space.
*/
bool wlr_damage_ring_add_box(struct wlr_damage_ring *ring,
void wlr_damage_ring_add_box(struct wlr_damage_ring *ring,
const struct wlr_box *box);
/**
@ -79,20 +61,6 @@ bool wlr_damage_ring_add_box(struct wlr_damage_ring *ring,
*/
void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring);
/**
* Rotate the damage ring. This needs to be called after using the accumulated
* damage, e.g. after rendering to an output's back buffer.
*/
void wlr_damage_ring_rotate(struct wlr_damage_ring *ring);
/**
* Get accumulated damage, which is the difference between the current buffer
* and the buffer with age of buffer_age; in context of rendering, this is
* the region that needs to be redrawn.
*/
void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring,
int buffer_age, pixman_region32_t *damage);
/**
* Get accumulated buffer damage and rotate the damage ring.
*
@ -102,6 +70,8 @@ void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring,
*
* Users should damage the ring if an error occurs while rendering or
* submitting the new buffer to the backend.
*
* The returned damage will be in the buffer-local coordinate space.
*/
void wlr_damage_ring_rotate_buffer(struct wlr_damage_ring *ring,
struct wlr_buffer *buffer, pixman_region32_t *damage);

View file

@ -12,6 +12,12 @@
#include <wayland-server-core.h>
#include <wlr/types/wlr_seat.h>
/**
* Deprecated: this protocol is legacy and superseded by ext-data-control-v1.
* The implementation will be dropped in a future wlroots version.
*
* Consider using `wlr_ext_data_control_manager_v1` as a replacement.
*/
struct wlr_data_control_manager_v1 {
struct wl_global *global;
struct wl_list devices; // wlr_data_control_device_v1.link
@ -21,7 +27,9 @@ struct wlr_data_control_manager_v1 {
struct wl_signal new_device; // wlr_data_control_device_v1
} events;
struct wl_listener display_destroy;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_data_control_device_v1 {
@ -33,9 +41,11 @@ struct wlr_data_control_device_v1 {
struct wl_resource *selection_offer_resource; // current selection offer
struct wl_resource *primary_selection_offer_resource; // current primary selection offer
struct wl_listener seat_destroy;
struct wl_listener seat_set_selection;
struct wl_listener seat_set_primary_selection;
struct {
struct wl_listener seat_destroy;
struct wl_listener seat_set_selection;
struct wl_listener seat_set_primary_selection;
} WLR_PRIVATE;
};
struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create(

View file

@ -16,13 +16,15 @@ struct wlr_data_device_manager {
struct wl_global *global;
struct wl_list data_sources;
struct wl_listener display_destroy;
struct {
struct wl_signal destroy;
} events;
void *data;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
enum wlr_data_offer_type {
@ -40,7 +42,9 @@ struct wlr_data_offer {
enum wl_data_device_manager_dnd_action preferred_action;
bool in_ask;
struct wl_listener source_destroy;
struct {
struct wl_listener source_destroy;
} WLR_PRIVATE;
};
/**
@ -89,9 +93,11 @@ struct wlr_drag_icon {
struct wl_signal destroy;
} events;
struct wl_listener surface_destroy;
void *data;
struct {
struct wl_listener surface_destroy;
} WLR_PRIVATE;
};
enum wlr_drag_grab_type {
@ -124,11 +130,14 @@ struct wlr_drag {
struct wl_signal destroy;
} events;
struct wl_listener source_destroy;
struct wl_listener seat_client_destroy;
struct wl_listener icon_destroy;
void *data;
struct {
struct wl_listener source_destroy;
struct wl_listener seat_client_destroy;
struct wl_listener focus_destroy;
struct wl_listener icon_destroy;
} WLR_PRIVATE;
};
struct wlr_drag_motion_event {

View file

@ -21,7 +21,9 @@ struct wlr_drm_buffer {
struct wl_resource *resource; // can be NULL if the client destroyed it
struct wlr_dmabuf_attributes dmabuf;
struct wl_listener release;
struct {
struct wl_listener release;
} WLR_PRIVATE;
};
/**
@ -40,12 +42,12 @@ struct wlr_drm {
struct wl_signal destroy;
} events;
// private state
struct {
char *node_name;
struct wlr_drm_format_set formats;
char *node_name;
struct wlr_drm_format_set formats;
struct wl_listener display_destroy;
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_drm_buffer *wlr_drm_buffer_try_from_resource(

View file

@ -18,9 +18,10 @@ struct wlr_drm_lease_v1_manager {
struct wl_list devices; // wlr_drm_lease_device_v1.link
struct wl_display *display;
struct wl_listener display_destroy;
struct {
struct wl_signal destroy;
/**
* Upon receiving this signal, call
* wlr_drm_lease_device_v1_grant_lease_request() to grant a lease of the
@ -29,6 +30,10 @@ struct wlr_drm_lease_v1_manager {
*/
struct wl_signal request;
} events;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_drm_lease_device_v1 {
@ -43,9 +48,11 @@ struct wlr_drm_lease_device_v1 {
struct wl_list requests; // wlr_drm_lease_request_v1.link
struct wl_list link; // wlr_drm_lease_v1_manager.devices
struct wl_listener backend_destroy;
void *data;
struct {
struct wl_listener backend_destroy;
} WLR_PRIVATE;
};
struct wlr_drm_lease_v1;
@ -55,12 +62,12 @@ struct wlr_drm_lease_connector_v1 {
struct wlr_output *output;
struct wlr_drm_lease_device_v1 *device;
/** NULL if no client is currently leasing this connector */
struct wlr_drm_lease_v1 *active_lease;
struct wl_listener destroy;
struct wl_list link; // wlr_drm_lease_device_v1.connectors
struct {
struct wl_listener destroy;
} WLR_PRIVATE;
};
struct wlr_drm_lease_request_v1 {
@ -84,14 +91,13 @@ struct wlr_drm_lease_v1 {
struct wlr_drm_lease_device_v1 *device;
struct wlr_drm_lease_connector_v1 **connectors;
size_t n_connectors;
struct wl_list link; // wlr_drm_lease_device_v1.leases
struct wl_listener destroy;
void *data;
struct {
struct wl_listener destroy;
} WLR_PRIVATE;
};
/**

View file

@ -17,11 +17,13 @@ struct wlr_export_dmabuf_manager_v1 {
struct wl_global *global;
struct wl_list frames; // wlr_export_dmabuf_frame_v1.link
struct wl_listener display_destroy;
struct {
struct wl_signal destroy;
} events;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_export_dmabuf_frame_v1 {
@ -33,8 +35,10 @@ struct wlr_export_dmabuf_frame_v1 {
bool cursor_locked;
struct wl_listener output_commit;
struct wl_listener output_destroy;
struct {
struct wl_listener output_commit;
struct wl_listener output_destroy;
} WLR_PRIVATE;
};
struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create(

View file

@ -0,0 +1,51 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_TYPES_WLR_EXT_DATA_CONTROL_V1_H
#define WLR_TYPES_WLR_EXT_DATA_CONTROL_V1_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_seat.h>
struct wlr_ext_data_control_manager_v1 {
struct wl_global *global;
struct wl_list devices; // wlr_ext_data_control_device_v1.link
struct {
struct wl_signal destroy;
struct wl_signal new_device; // wlr_ext_data_control_device_v1
} events;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_ext_data_control_device_v1 {
struct wl_resource *resource;
struct wlr_ext_data_control_manager_v1 *manager;
struct wl_list link; // wlr_ext_data_control_manager_v1.devices
struct wlr_seat *seat;
struct wl_resource *selection_offer_resource; // current selection offer
struct wl_resource *primary_selection_offer_resource; // current primary selection offer
struct {
struct wl_listener seat_destroy;
struct wl_listener seat_set_selection;
struct wl_listener seat_set_primary_selection;
} WLR_PRIVATE;
};
struct wlr_ext_data_control_manager_v1 *wlr_ext_data_control_manager_v1_create(
struct wl_display *display, uint32_t version);
void wlr_ext_data_control_device_v1_destroy(
struct wlr_ext_data_control_device_v1 *device);
#endif

View file

@ -16,13 +16,15 @@ struct wlr_ext_foreign_toplevel_list_v1 {
struct wl_list resources; // wl_resource_get_link()
struct wl_list toplevels; // ext_foreign_toplevel_handle_v1.link
struct wl_listener display_destroy;
struct {
struct wl_signal destroy;
} events;
void *data;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_ext_foreign_toplevel_handle_v1 {
@ -64,4 +66,7 @@ void wlr_ext_foreign_toplevel_handle_v1_update_state(
struct wlr_ext_foreign_toplevel_handle_v1 *toplevel,
const struct wlr_ext_foreign_toplevel_handle_v1_state *state);
struct wlr_ext_foreign_toplevel_handle_v1 *wlr_ext_foreign_toplevel_handle_v1_from_resource(
struct wl_resource *resource);
#endif

Some files were not shown because too many files have changed in this diff Show more