Compare commits

...

125 commits

Author SHA1 Message Date
YaNing Lu
2c7b47ef41 Merge branch 'dnd-popup' into 'master'
data-device: fix dnd handling during popup interactions

See merge request wlroots/wlroots!4943
2025-10-25 11:08:17 +00:00
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
Lu YaNing
2e53932337 data-device: fix dnd handling during popup interactions
When a popup appears during dnd operations in wlroots-based
compositors, several issues occur:
1.Drag data is lost
2.Keyboard focus changes unexpectedly
3.Other windows cannot receive drag enter events
4.The compositor may crash in some cases
5.Event propagation is interrupted

Forwarding pointer/keyboard events from popup grab to drag grab when
drag is active, preventing data loss and maintaining proper event
propagation during popup interactions.
2024-12-18 15:57:40 +08:00
93 changed files with 2692 additions and 573 deletions

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

@ -485,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

@ -178,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:
@ -302,11 +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;
}
@ -321,6 +414,7 @@ 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;
@ -335,6 +429,8 @@ void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) {
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) {
@ -344,6 +440,7 @@ 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) {
@ -435,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) {

View file

@ -42,7 +42,8 @@ static const uint32_t COMMIT_OUTPUT_STATE =
WLR_OUTPUT_STATE_LAYERS |
WLR_OUTPUT_STATE_WAIT_TIMELINE |
WLR_OUTPUT_STATE_SIGNAL_TIMELINE |
WLR_OUTPUT_STATE_COLOR_TRANSFORM;
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;
@ -864,6 +865,12 @@ static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bo
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.
@ -1710,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;

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

@ -22,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) },

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);
}

View file

@ -367,7 +367,10 @@ void wlr_session_close_file(struct wlr_session *session,
}
assert(wl_list_empty(&dev->events.change.listener_list));
assert(wl_list_empty(&dev->events.remove.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);
@ -516,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) {
@ -533,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;
}
}
}
@ -554,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

@ -55,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);
@ -75,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);

View file

@ -158,6 +158,8 @@ struct wlr_drm_connector_state {
uint32_t fb_damage_clips;
int primary_in_fence_fd, out_fence_fd;
bool vrr_enabled;
uint32_t colorspace;
uint32_t hdr_output_metadata;
};
/**
@ -212,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;
};

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 {

View file

@ -6,9 +6,10 @@
#include <wlr/util/addon.h>
enum wlr_color_transform_type {
COLOR_TRANSFORM_SRGB,
COLOR_TRANSFORM_INVERSE_EOTF,
COLOR_TRANSFORM_LCMS2,
COLOR_TRANSFORM_LUT_3X1D,
COLOR_TRANSFORM_PIPELINE,
};
struct wlr_color_transform {
@ -18,8 +19,11 @@ struct wlr_color_transform {
enum wlr_color_transform_type type;
};
void wlr_color_transform_init(struct wlr_color_transform *tr,
enum wlr_color_transform_type type);
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
@ -36,6 +40,16 @@ struct wlr_color_transform_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.
@ -51,6 +65,13 @@ void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr);
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
@ -59,12 +80,6 @@ void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr,
struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
struct wlr_color_transform *tr);
/**
* Evaluate a 3x1D LUT color transform for a given RGB triplet.
*/
void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr,
float out[static 3], const float in[static 3]);
/**
* Obtain primaries values from a well-known primaries name.
*/

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>
@ -151,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 {
@ -161,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 {
@ -191,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;
@ -209,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 {
@ -334,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;
};
@ -342,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;
@ -356,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);
@ -381,14 +413,19 @@ 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;
@ -453,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
@ -468,7 +504,7 @@ struct wlr_vk_texture {
struct wlr_buffer *buffer;
struct wlr_addon buffer_addon;
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);

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,
@ -25,6 +27,7 @@ void output_defer_present(struct wlr_output *output, struct wlr_output_event_pre
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);

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

@ -27,6 +27,60 @@ enum wlr_color_named_primaries {
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,
};
/**
@ -74,10 +128,11 @@ 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
@ -86,6 +141,13 @@ struct wlr_color_transform *wlr_color_transform_init_srgb(void);
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.
*/
@ -97,4 +159,10 @@ struct wlr_color_transform *wlr_color_transform_ref(struct wlr_color_transform *
*/
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

@ -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,12 @@ 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.
*
@ -99,6 +103,10 @@ 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.
*

View file

@ -41,6 +41,10 @@ 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?
*/

View file

@ -9,10 +9,10 @@
#ifndef WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H
#define WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H
#include <wayland-server.h>
#include <wlr/render/color.h>
#include <wayland-server-core.h>
#include <wayland-protocols/color-management-v1-enum.h>
#include "color-management-v1-protocol.h"
#include <wlr/render/color.h>
struct wlr_surface;
@ -58,6 +58,10 @@ struct wlr_color_manager_v1_options {
struct wlr_color_manager_v1 {
struct wl_global *global;
struct {
struct wl_signal destroy;
} events;
struct {
struct wlr_color_manager_v1_features features;
@ -89,4 +93,30 @@ 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

@ -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;

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.

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

View file

@ -62,8 +62,6 @@ 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_list link; // wlr_drm_lease_device_v1.connectors
@ -93,9 +91,6 @@ 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
void *data;

View file

@ -21,7 +21,9 @@ struct wlr_ext_data_control_manager_v1 {
struct wl_signal new_device; // wlr_ext_data_control_device_v1
} events;
struct wl_listener display_destroy;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_ext_data_control_device_v1 {
@ -33,9 +35,11 @@ struct wlr_ext_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_ext_data_control_manager_v1 *wlr_ext_data_control_manager_v1_create(

View file

@ -10,9 +10,9 @@
#define WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H
#include <pixman.h>
#include <wayland-server.h>
#include <wayland-server-protocol.h>
#include <wayland-protocols/ext-image-copy-capture-v1-enum.h>
#include <time.h>
#include "ext-image-copy-capture-v1-protocol.h"
struct wlr_renderer;

View file

@ -0,0 +1,28 @@
/*
* 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_FIXES_H
#define WLR_TYPES_WLR_FIXES_H
#include <wayland-server-core.h>
struct wlr_fixes {
struct wl_global *global;
struct {
struct wl_signal destroy;
} events;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_fixes *wlr_fixes_create(struct wl_display *display, uint32_t version);
#endif

View file

@ -10,6 +10,11 @@ struct wlr_gamma_control_manager_v1 {
struct wl_global *global;
struct wl_list controls; // wlr_gamma_control_v1.link
// Fallback to use when an struct wlr_output doesn't support gamma LUTs.
// Can be used to apply gamma LUTs via a struct wlr_renderer. Leave zero to
// indicate that the fallback is unsupported.
size_t fallback_gamma_size;
struct {
struct wl_signal destroy;
struct wl_signal set_gamma; // struct wlr_gamma_control_manager_v1_set_gamma_event
@ -49,6 +54,8 @@ struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control(
struct wlr_gamma_control_manager_v1 *manager, struct wlr_output *output);
bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control,
struct wlr_output_state *output_state);
struct wlr_color_transform *wlr_gamma_control_v1_get_color_transform(
struct wlr_gamma_control_v1 *gamma_control);
void wlr_gamma_control_v1_send_failed_and_destroy(struct wlr_gamma_control_v1 *gamma_control);
#endif

View file

@ -48,10 +48,10 @@ struct wlr_input_method_v2 {
struct wl_list link;
struct {
struct wl_signal commit; // struct wlr_input_method_v2
struct wl_signal commit;
struct wl_signal new_popup_surface; // struct wlr_input_popup_surface_v2
struct wl_signal grab_keyboard; // struct wlr_input_method_keyboard_grab_v2
struct wl_signal destroy; // struct wlr_input_method_v2
struct wl_signal destroy;
} events;
struct {
@ -94,8 +94,8 @@ struct wlr_input_method_manager_v2 {
struct wl_list input_methods; // struct wlr_input_method_v2.link
struct {
struct wl_signal input_method; // struct wlr_input_method_v2
struct wl_signal destroy; // struct wlr_input_method_manager_v2
struct wl_signal new_input_method; // struct wlr_input_method_v2
struct wl_signal destroy;
} events;
struct {

View file

@ -14,6 +14,7 @@
#include <time.h>
#include <wayland-server-protocol.h>
#include <wayland-util.h>
#include <wlr/render/color.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/util/addon.h>
@ -74,6 +75,7 @@ enum wlr_output_state_field {
WLR_OUTPUT_STATE_WAIT_TIMELINE = 1 << 10,
WLR_OUTPUT_STATE_SIGNAL_TIMELINE = 1 << 11,
WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12,
WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13,
};
enum wlr_output_state_mode_type {
@ -81,6 +83,30 @@ enum wlr_output_state_mode_type {
WLR_OUTPUT_STATE_MODE_CUSTOM,
};
/**
* Colorimetric image description.
*
* Carries information about the color encoding used for a struct wlr_buffer.
*
* Supported primaries are advertised in wlr_output.supported_primaries.
* Supported transfer functions are advertised in
* wlr_output.supported_transfer_functions.
*
* mastering_display_primaries, mastering_luminance, max_cll and max_fall are
* optional. Luminances are given in cd/m².
*/
struct wlr_output_image_description {
enum wlr_color_named_primaries primaries;
enum wlr_color_transfer_function transfer_function;
struct wlr_color_primaries mastering_display_primaries;
struct {
double min, max;
} mastering_luminance;
double max_cll; // max content light level
double max_fall; // max frame-average light level
};
/**
* Holds the double-buffered output state.
*/
@ -131,6 +157,8 @@ struct wlr_output_state {
uint64_t signal_point;
struct wlr_color_transform *color_transform;
struct wlr_output_image_description *image_description;
};
struct wlr_output_impl;
@ -166,12 +194,16 @@ struct wlr_output {
int32_t width, height;
int32_t refresh; // mHz, may be zero
uint32_t supported_primaries; // bitfield of enum wlr_color_named_primaries
uint32_t supported_transfer_functions; // bitfield of enum wlr_color_transfer_function
bool enabled;
float scale;
enum wl_output_subpixel subpixel;
enum wl_output_transform transform;
enum wlr_output_adaptive_sync_status adaptive_sync_status;
uint32_t render_format;
const struct wlr_output_image_description *image_description;
// Indicates whether making changes to adaptive sync status is supported.
// If false, changes to adaptive sync status is guaranteed to fail. If
@ -234,6 +266,8 @@ struct wlr_output {
struct {
struct wl_listener display_destroy;
struct wlr_output_image_description image_description_value;
struct wlr_color_transform *color_transform;
} WLR_PRIVATE;
};
@ -582,6 +616,12 @@ void wlr_output_state_set_signal_timeline(struct wlr_output_state *state,
void wlr_output_state_set_color_transform(struct wlr_output_state *state,
struct wlr_color_transform *tr);
/**
* Set the colorimetry image description.
*/
bool wlr_output_state_set_image_description(struct wlr_output_state *state,
const struct wlr_output_image_description *image_desc);
/**
* Copies the output state from src to dst. It is safe to then
* wlr_output_state_finish() src and have dst still be valid.

View file

@ -11,10 +11,10 @@
#include <stdint.h>
#include <wayland-server-core.h>
#include <wayland-protocols/pointer-constraints-unstable-v1-enum.h>
#include <pixman.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_seat.h>
#include "pointer-constraints-unstable-v1-protocol.h"
struct wlr_seat;

View file

@ -43,6 +43,7 @@ struct wlr_scene_output_layout;
struct wlr_presentation;
struct wlr_linux_dmabuf_v1;
struct wlr_gamma_control_manager_v1;
struct wlr_color_manager_v1;
struct wlr_output_state;
typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)(
@ -102,11 +103,13 @@ struct wlr_scene {
// May be NULL
struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1;
struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1;
struct wlr_color_manager_v1 *color_manager_v1;
struct {
struct wl_listener linux_dmabuf_v1_destroy;
struct wl_listener gamma_control_manager_v1_destroy;
struct wl_listener gamma_control_manager_v1_set_gamma;
struct wl_listener color_manager_v1_destroy;
enum wlr_scene_debug_damage_option debug_damage_option;
bool direct_scanout;
@ -192,6 +195,8 @@ struct wlr_scene_buffer {
int dst_width, dst_height;
enum wl_output_transform transform;
pixman_region32_t opaque_region;
enum wlr_color_transfer_function transfer_function;
enum wlr_color_named_primaries primaries;
struct {
uint64_t active_outputs;
@ -247,6 +252,11 @@ struct wlr_scene_output {
bool gamma_lut_changed;
struct wlr_gamma_control_v1 *gamma_lut;
struct wlr_color_transform *gamma_lut_color_transform;
struct wlr_color_transform *prev_gamma_lut_color_transform;
struct wlr_color_transform *prev_supplied_color_transform;
struct wlr_color_transform *prev_combined_color_transform;
struct wl_listener output_commit;
struct wl_listener output_damage;
@ -364,6 +374,13 @@ void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene,
void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene,
struct wlr_gamma_control_manager_v1 *gamma_control);
/**
* Handles color_management_v1 feedback for all surfaces in the scene.
*
* Asserts that a struct wlr_color_manager_v1 hasn't already been set for the scene.
*/
void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager);
/**
* Add a node displaying nothing but its children.
*/
@ -541,6 +558,12 @@ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer,
void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer,
enum wlr_scale_filter_mode filter_mode);
void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer,
enum wlr_color_transfer_function transfer_function);
void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer,
enum wlr_color_named_primaries primaries);
/**
* Calls the buffer's frame_done signal.
*/
@ -566,6 +589,11 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output,
struct wlr_scene_output_state_options {
struct wlr_scene_timer *timer;
/**
* Color transform to apply before the output's color transform. Cannot be
* used when the output has a non-NULL image description set.
*/
struct wlr_color_transform *color_transform;
/**

View file

@ -14,6 +14,13 @@
#include <wlr/types/wlr_buffer.h>
#include <wlr/util/box.h>
/**
* Deprecated: this protocol is deprecated and superseded by ext-image-copy-capture-v1.
* The implementation will be dropped in a future wlroots version.
*
* Consider using `wlr_ext_image_capture_source_v1` instead.
*/
struct wlr_screencopy_manager_v1 {
struct wl_global *global;
struct wl_list frames; // wlr_screencopy_frame_v1.link

View file

@ -10,10 +10,9 @@
#define WLR_TYPES_WLR_TABLET_V2_H
#include <wayland-server-core.h>
#include <wayland-protocols/tablet-v2-enum.h>
#include <wlr/types/wlr_seat.h>
#include "tablet-v2-protocol.h"
/* This can probably be even lower,the tools don't have a lot of buttons */
#define WLR_TABLET_V2_TOOL_BUTTONS_CAP 16

View file

@ -11,11 +11,9 @@
#include <stdint.h>
#include <wayland-server-core.h>
#include <wayland-server-protocol.h>
#include <wayland-protocols/tearing-control-v1-enum.h>
#include <wlr/types/wlr_compositor.h>
#include "tearing-control-v1-protocol.h"
struct wlr_tearing_control_v1 {
struct wl_client *client;
struct wl_list link;

View file

@ -57,10 +57,10 @@ struct wlr_text_input_v3 {
struct wl_list link;
struct {
struct wl_signal enable; // struct wlr_text_input_v3
struct wl_signal commit; // struct wlr_text_input_v3
struct wl_signal disable; // struct wlr_text_input_v3
struct wl_signal destroy; // struct wlr_text_input_v3
struct wl_signal enable;
struct wl_signal commit;
struct wl_signal disable;
struct wl_signal destroy;
} events;
struct {
@ -75,7 +75,7 @@ struct wlr_text_input_manager_v3 {
struct {
struct wl_signal new_text_input; // struct wlr_text_input_v3
struct wl_signal destroy; // struct wlr_text_input_manager_v3
struct wl_signal destroy;
} events;
struct {

View file

@ -10,10 +10,10 @@
#define WLR_TYPES_WLR_XDG_SHELL_H
#include <wayland-server-core.h>
#include <wayland-protocols/xdg-shell-enum.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/util/box.h>
#include "xdg-shell-protocol.h"
struct wlr_xdg_shell {
struct wl_global *global;

View file

@ -0,0 +1,41 @@
/*
* 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_XDG_TOPLEVEL_TAG_V1_H
#define WLR_TYPES_WLR_XDG_TOPLEVEL_TAG_V1_H
#include <wayland-server-core.h>
struct wlr_xdg_toplevel_tag_manager_v1 {
struct wl_global *global;
struct {
struct wl_signal set_tag; // struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event
struct wl_signal set_description; // struct wlr_xdg_toplevel_tag_manager_v1_set_description_event
struct wl_signal destroy;
} events;
struct {
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event {
struct wlr_xdg_toplevel *toplevel;
const char *tag;
};
struct wlr_xdg_toplevel_tag_manager_v1_set_description_event {
struct wlr_xdg_toplevel *toplevel;
const char *description;
};
struct wlr_xdg_toplevel_tag_manager_v1 *wlr_xdg_toplevel_tag_manager_v1_create(
struct wl_display *display, uint32_t version);
#endif

View file

@ -221,6 +221,7 @@ struct wlr_xwayland_surface {
struct wl_signal set_override_redirect;
struct wl_signal set_geometry;
struct wl_signal set_opacity;
struct wl_signal set_icon;
struct wl_signal focus_in;
struct wl_signal grab_focus;
/* can be used to set initial maximized/fullscreen geometry */
@ -401,6 +402,15 @@ enum wlr_xwayland_icccm_input_model wlr_xwayland_surface_icccm_input_model(
void wlr_xwayland_set_workareas(struct wlr_xwayland *wlr_xwayland,
const struct wlr_box *workareas, size_t num_workareas);
/**
* Fetches the icon set via the _NET_WM_ICON property.
*
* Returns true on success. The caller is responsible for freeing the reply
* using xcb_ewmh_get_wm_icon_reply_wipe().
*/
bool wlr_xwayland_surface_fetch_icon(
const struct wlr_xwayland_surface *xsurface,
xcb_ewmh_get_wm_icon_reply_t *icon_reply);
/**
* Get the XCB connection of the XWM.

View file

@ -36,6 +36,7 @@ enum atom_name {
NET_WM_STATE,
NET_WM_STRUT_PARTIAL,
NET_WM_WINDOW_TYPE,
NET_WM_ICON,
WM_TAKE_FOCUS,
WINDOW,
NET_ACTIVE_WINDOW,
@ -163,6 +164,7 @@ struct wlr_xwm {
struct wl_listener drop_focus_destroy;
};
// xwm_create takes ownership of wm_fd and will close it under all circumstances.
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd);
void xwm_destroy(struct wlr_xwm *xwm);

View file

@ -86,7 +86,7 @@ internal_features = {
internal_config = configuration_data()
wayland_kwargs = {
'version': '>=1.23.1',
'version': '>=1.24.0',
'fallback': 'wayland',
'default_options': [
'tests=false',

View file

@ -1,9 +1,10 @@
wayland_protos = dependency('wayland-protocols',
version: '>=1.43',
version: '>=1.44',
fallback: 'wayland-protocols',
default_options: ['tests=false'],
)
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
wlr_deps += wayland_protos
wayland_scanner_dep = dependency('wayland-scanner',
kwargs: wayland_kwargs,
@ -25,6 +26,7 @@ protocols = {
# Staging upstream protocols
'alpha-modifier-v1': wl_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml',
'color-management-v1': wl_protocol_dir / 'staging/color-management/color-management-v1.xml',
'color-representation-v1': wl_protocol_dir / 'staging/color-representation/color-representation-v1.xml',
'content-type-v1': wl_protocol_dir / 'staging/content-type/content-type-v1.xml',
'cursor-shape-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
@ -42,6 +44,7 @@ protocols = {
'xdg-dialog-v1': wl_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml',
'xdg-system-bell-v1': wl_protocol_dir / 'staging/xdg-system-bell/xdg-system-bell-v1.xml',
'xdg-toplevel-icon-v1': wl_protocol_dir / 'staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml',
'xdg-toplevel-tag-v1': wl_protocol_dir / 'staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml',
'xwayland-shell-v1': wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
'tearing-control-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',

View file

@ -43,7 +43,7 @@
<request name="capture_output">
<description summary="capture a frame from an output">
Capture the next frame of a an entire output.
Capture the next frame of an entire output.
</description>
<arg name="frame" type="new_id" interface="zwlr_export_dmabuf_frame_v1"/>
<arg name="overlay_cursor" type="int"
@ -136,7 +136,7 @@
<arg name="stride" type="uint"
summary="line size in bytes"/>
<arg name="plane_index" type="uint"
summary="index of the the plane the data in the object applies to"/>
summary="index of the plane the data in the object applies to"/>
</event>
<event name="ready">

View file

@ -58,7 +58,7 @@
</description>
</request>
<event name="finished">
<event name="finished" type="destructor">
<description summary="the compositor has finished with the toplevel manager">
This event indicates that the compositor is done sending events to the
zwlr_foreign_toplevel_manager_v1. The server will destroy the object

View file

@ -72,7 +72,7 @@
tables. At any time the compositor can send a failed event indicating that
this object is no longer valid.
There must always be at most one gamma control object per output, which
There can only be at most one gamma control object per output, which
has exclusive access to this particular output. When the gamma control
object is destroyed, the gamma table is restored to its original value.
</description>

View file

@ -156,8 +156,8 @@
not assume that the name is a reflection of an underlying DRM
connector, X11 connection, etc.
If the compositor implements the xdg-output protocol and this head is
enabled, the xdg_output.name event must report the same name.
If this head matches a wl_output, the wl_output.name event must report
the same name.
The name event is sent after a wlr_output_head object is created. This
event is only sent once per object, and the name does not change over
@ -176,8 +176,8 @@
the make, model, serial of the underlying DRM connector or the display
name of the underlying X11 connection, etc.
If the compositor implements xdg-output and this head is enabled,
the xdg_output.description must report the same description.
If this head matches a wl_output, the wl_output.description event must
report the same name.
The description event is sent after a wlr_output_head object is created.
This event is only sent once per object, and the description does not
@ -191,6 +191,10 @@
This event describes the physical size of the head. This event is only
sent if the head has a physical size (e.g. is not a projector or a
virtual device).
The physical size event is sent after a wlr_output_head object is created. This
event is only sent once per object, and the physical size does not change over
the lifetime of the wlr_output_head object.
</description>
<arg name="width" type="int" summary="width in millimeters of the output"/>
<arg name="height" type="int" summary="height in millimeters of the output"/>
@ -264,9 +268,6 @@
<description summary="head manufacturer">
This event describes the manufacturer of the head.
This must report the same make as the wl_output interface does in its
geometry event.
Together with the model and serial_number events the purpose is to
allow clients to recognize heads from previous sessions and for example
load head-specific configurations back.
@ -278,6 +279,10 @@
identify the head by available information from other events but should
be aware that there is an increased risk of false positives.
If sent, the make event is sent after a wlr_output_head object is
created and only sent once per object. The make does not change over
the lifetime of the wlr_output_head object.
It is not recommended to display the make string in UI to users. For
that the string provided by the description event should be preferred.
</description>
@ -288,9 +293,6 @@
<description summary="head model">
This event describes the model of the head.
This must report the same model as the wl_output interface does in its
geometry event.
Together with the make and serial_number events the purpose is to
allow clients to recognize heads from previous sessions and for example
load head-specific configurations back.
@ -302,6 +304,10 @@
identify the head by available information from other events but should
be aware that there is an increased risk of false positives.
If sent, the model event is sent after a wlr_output_head object is
created and only sent once per object. The model does not change over
the lifetime of the wlr_output_head object.
It is not recommended to display the model string in UI to users. For
that the string provided by the description event should be preferred.
</description>
@ -323,6 +329,10 @@
available information from other events but should be aware that there
is an increased risk of false positives.
If sent, the serial number event is sent after a wlr_output_head object
is created and only sent once per object. The serial number does not
change over the lifetime of the wlr_output_head object.
It is not recommended to display the serial_number string in UI to
users. For that the string provided by the description event should be
preferred.

View file

@ -50,7 +50,7 @@
<request name="get_output_power">
<description summary="get a power management for an output">
Create a output power management mode control that can be used to
Create an output power management mode control that can be used to
adjust the power management mode for a given output.
</description>
<arg name="id" type="new_id" interface="zwlr_output_power_v1"/>
@ -79,7 +79,7 @@
</enum>
<enum name="error">
<entry name="invalid_mode" value="1" summary="inexistent power save mode"/>
<entry name="invalid_mode" value="1" summary="nonexistent power save mode"/>
</enum>
<request name="set_mode">

View file

@ -88,7 +88,7 @@
supported buffer type. The "buffer_done" event is sent afterwards to
indicate that all supported buffer types have been enumerated. The client
will then be able to send a "copy" request. If the capture is successful,
the compositor will send a "flags" followed by a "ready" event.
the compositor will send a "flags" event followed by a "ready" event.
For objects version 2 or lower, wl_shm buffers are always supported, ie.
the "buffer" event is guaranteed to be sent.
@ -114,12 +114,12 @@
<request name="copy">
<description summary="copy the frame">
Copy the frame to the supplied buffer. The buffer must have a the
Copy the frame to the supplied buffer. The buffer must have the
correct size, see zwlr_screencopy_frame_v1.buffer and
zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a
supported format.
If the frame is successfully copied, a "flags" and a "ready" events are
If the frame is successfully copied, "flags" and "ready" events are
sent. Otherwise, a "failed" event is sent.
</description>
<arg name="buffer" type="object" interface="wl_buffer"/>
@ -147,8 +147,7 @@
<event name="ready">
<description summary="indicates frame is available for reading">
Called as soon as the frame is copied, indicating it is available
for reading. This event includes the time at which presentation happened
at.
for reading. This event includes the time at which the presentation took place.
The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples,
each component being an unsigned 32-bit value. Whole seconds are in

32
release.sh Executable file
View file

@ -0,0 +1,32 @@
#!/bin/sh -eu
prev=$(git describe --tags --abbrev=0)
next=$(meson rewrite kwargs info project / | jq -r '.kwargs["project#/"].version')
case "$next" in
*-dev)
echo "This is a development version"
exit 1
;;
esac
if [ "$prev" = "$next" ]; then
echo "Version not bumped in meson.build"
exit 1
fi
if ! git diff-index --quiet HEAD -- meson.build; then
echo "meson.build not committed"
exit 1
fi
shortlog="$(git shortlog --no-merges "$prev..")"
(echo "wlroots $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F -
prefix=wlroots-$next
archive=$prefix.tar.gz
git archive --prefix="$prefix/" -o "$archive" "$next"
gpg --output "$archive".sig --detach-sig "$archive"
git push --follow-tags
glab release create "$next" "$archive" "$archive.sig" --notes ""

View file

@ -97,7 +97,6 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc,
}
wlr_buffer_init(&buffer->base, &buffer_impl, width, height);
buffer->gbm_bo = bo;
wl_list_insert(&alloc->buffers, &buffer->link);
if (!export_gbm_bo(bo, &buffer->dmabuf)) {
free(buffer);
@ -112,6 +111,8 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc,
buffer->dmabuf.modifier = fallback_modifier;
}
wl_list_insert(&alloc->buffers, &buffer->link);
char *format_name = drmGetFormatName(buffer->dmabuf.format);
char *modifier_name = drmGetFormatModifierName(buffer->dmabuf.modifier);
wlr_log(WLR_DEBUG, "Allocated %dx%d GBM buffer "

View file

@ -29,13 +29,15 @@ void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_tra
wlr_addon_set_init(&tr->addons);
}
struct wlr_color_transform *wlr_color_transform_init_srgb(void) {
struct wlr_color_transform *tx = calloc(1, sizeof(*tx));
struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf(
enum wlr_color_transfer_function tf) {
struct wlr_color_transform_inverse_eotf *tx = calloc(1, sizeof(*tx));
if (!tx) {
return NULL;
}
wlr_color_transform_init(tx, COLOR_TRANSFORM_SRGB);
return tx;
wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_INVERSE_EOTF);
tx->tf = tf;
return &tx->base;
}
struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim,
@ -60,9 +62,36 @@ struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim,
return &tx->base;
}
struct wlr_color_transform *wlr_color_transform_init_pipeline(
struct wlr_color_transform **transforms, size_t len) {
assert(len > 0);
struct wlr_color_transform **copy = calloc(len, sizeof(copy[0]));
if (copy == NULL) {
return NULL;
}
struct wlr_color_transform_pipeline *tx = calloc(1, sizeof(*tx));
if (!tx) {
free(copy);
return NULL;
}
wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_PIPELINE);
// TODO: flatten nested pipeline transforms
for (size_t i = 0; i < len; i++) {
copy[i] = wlr_color_transform_ref(transforms[i]);
}
tx->transforms = copy;
tx->len = len;
return &tx->base;
}
static void color_transform_destroy(struct wlr_color_transform *tr) {
switch (tr->type) {
case COLOR_TRANSFORM_SRGB:
case COLOR_TRANSFORM_INVERSE_EOTF:
break;
case COLOR_TRANSFORM_LCMS2:
color_transform_lcms2_finish(color_transform_lcms2_from_base(tr));
@ -71,6 +100,14 @@ static void color_transform_destroy(struct wlr_color_transform *tr) {
struct wlr_color_transform_lut_3x1d *lut_3x1d = color_transform_lut_3x1d_from_base(tr);
free(lut_3x1d->lut_3x1d);
break;
case COLOR_TRANSFORM_PIPELINE:;
struct wlr_color_transform_pipeline *pipeline =
wl_container_of(tr, pipeline, base);
for (size_t i = 0; i < pipeline->len; i++) {
wlr_color_transform_unref(pipeline->transforms[i]);
}
free(pipeline->transforms);
break;
}
wlr_addon_set_finish(&tr->addons);
free(tr);
@ -92,6 +129,13 @@ void wlr_color_transform_unref(struct wlr_color_transform *tr) {
}
}
struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_base(
struct wlr_color_transform *tr) {
assert(tr->type == COLOR_TRANSFORM_INVERSE_EOTF);
struct wlr_color_transform_inverse_eotf *inverse_eotf = wl_container_of(tr, inverse_eotf, base);
return inverse_eotf;
}
struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
struct wlr_color_transform *tr) {
assert(tr->type == COLOR_TRANSFORM_LUT_3X1D);
@ -99,8 +143,67 @@ struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
return lut_3x1d;
}
static float srgb_eval_inverse_eotf(float x) {
// See https://www.w3.org/Graphics/Color/srgb
if (x <= 0.0031308) {
return 12.92 * x;
} else {
return 1.055 * powf(x, 1.0 / 2.4) - 0.055;
}
}
static float st2084_pq_eval_inverse_eotf(float x) {
// H.273 TransferCharacteristics code point 16
float c1 = 0.8359375;
float c2 = 18.8515625;
float c3 = 18.6875;
float m = 78.84375;
float n = 0.1593017578125;
if (x < 0) {
x = 0;
}
if (x > 1) {
x = 1;
}
float pow_n = powf(x, n);
return powf((c1 + c2 * pow_n) / (1 + c3 * pow_n), m);
}
static float bt1886_eval_inverse_eotf(float x) {
float lb = powf(0.0001, 1.0 / 2.4);
float lw = powf(1.0, 1.0 / 2.4);
float a = powf(lw - lb, 2.4);
float b = lb / (lw - lb);
return powf(x / a, 1.0 / 2.4) - b;
}
static float transfer_function_eval_inverse_eotf(
enum wlr_color_transfer_function tf, float x) {
switch (tf) {
case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
return srgb_eval_inverse_eotf(x);
case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
return st2084_pq_eval_inverse_eotf(x);
case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
return x;
case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
return powf(x, 1.0 / 2.2);
case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
return bt1886_eval_inverse_eotf(x);
}
abort(); // unreachable
}
static void color_transform_inverse_eotf_eval(
struct wlr_color_transform_inverse_eotf *tr,
float out[static 3], const float in[static 3]) {
for (size_t i = 0; i < 3; i++) {
out[i] = transfer_function_eval_inverse_eotf(tr->tf, in[i]);
}
}
static float lut_1d_get(const uint16_t *lut, size_t len, size_t i) {
if (i > len) {
if (i >= len) {
i = len - 1;
}
return (float) lut[i] / UINT16_MAX;
@ -116,13 +219,38 @@ static float lut_1d_eval(const uint16_t *lut, size_t len, float x) {
return a * (1 - frac_part) + b * frac_part;
}
void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr,
static void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr,
float out[static 3], const float in[static 3]) {
for (size_t i = 0; i < 3; i++) {
out[i] = lut_1d_eval(&tr->lut_3x1d[tr->dim * i], tr->dim, in[i]);
}
}
void wlr_color_transform_eval(struct wlr_color_transform *tr,
float out[static 3], const float in[static 3]) {
switch (tr->type) {
case COLOR_TRANSFORM_INVERSE_EOTF:
color_transform_inverse_eotf_eval(wlr_color_transform_inverse_eotf_from_base(tr), out, in);
break;
case COLOR_TRANSFORM_LCMS2:
color_transform_lcms2_eval(color_transform_lcms2_from_base(tr), out, in);
break;
case COLOR_TRANSFORM_LUT_3X1D:
color_transform_lut_3x1d_eval(color_transform_lut_3x1d_from_base(tr), out, in);
break;
case COLOR_TRANSFORM_PIPELINE:;
struct wlr_color_transform_pipeline *pipeline =
wl_container_of(tr, pipeline, base);
float color[3];
memcpy(color, in, sizeof(color));
for (size_t i = 0; i < pipeline->len; i++) {
wlr_color_transform_eval(pipeline->transforms[i], color, color);
}
memcpy(out, color, sizeof(color));
break;
}
}
void wlr_color_primaries_from_named(struct wlr_color_primaries *out,
enum wlr_color_named_primaries named) {
switch (named) {
@ -193,6 +321,13 @@ void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_f
.reference = 203,
};
break;
case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
*lum = (struct wlr_color_luminances){
.min = 0.01,
.max = 100,
.reference = 100,
};
break;
default:
*lum = (struct wlr_color_luminances){
.min = 0.2,

View file

@ -260,7 +260,8 @@ static struct wlr_egl *egl_create(void) {
return egl;
}
static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display,
bool allow_software) {
egl->display = display;
EGLint major, minor;
@ -326,9 +327,8 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
// The only way a non-DRM device is selected is when the user
// explicitly picks software rendering
if (check_egl_ext(device_exts_str, "EGL_MESA_device_software") &&
egl->exts.EXT_device_drm) {
if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) {
if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) {
if (allow_software || env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) {
wlr_log(WLR_INFO, "Using software rendering");
} else {
wlr_log(WLR_ERROR, "Software rendering detected, please use "
@ -382,7 +382,7 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
}
static bool egl_init(struct wlr_egl *egl, EGLenum platform,
void *remote_display) {
void *remote_display, bool allow_software) {
EGLint display_attribs[3] = {0};
size_t display_attribs_len = 0;
@ -401,7 +401,7 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform,
return false;
}
if (!egl_init_display(egl, display)) {
if (!egl_init_display(egl, display, allow_software)) {
if (egl->exts.KHR_display_reference) {
eglTerminate(display);
}
@ -556,6 +556,8 @@ static int open_render_node(int drm_fd) {
}
struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
bool allow_software = drm_fd < 0;
struct wlr_egl *egl = egl_create();
if (egl == NULL) {
wlr_log(WLR_ERROR, "Failed to create EGL context");
@ -569,7 +571,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
*/
EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd);
if (egl_device != EGL_NO_DEVICE_EXT) {
if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) {
if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device, allow_software)) {
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT");
return egl;
}
@ -594,7 +596,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
goto error;
}
if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) {
if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device, allow_software)) {
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR");
return egl;
}
@ -633,7 +635,7 @@ struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display,
return NULL;
}
if (!egl_init_display(egl, display)) {
if (!egl_init_display(egl, display, true)) {
free(egl);
return NULL;
}

View file

@ -57,28 +57,33 @@ static void convert_pixman_box_to_vk_rect(const pixman_box32_t *box, VkRect2D *r
}
static float color_to_linear(float non_linear) {
// See https://www.w3.org/Graphics/Color/srgb
return (non_linear > 0.04045) ?
pow((non_linear + 0.055) / 1.055, 2.4) :
non_linear / 12.92;
return pow(non_linear, 2.2);
}
static float color_to_linear_premult(float non_linear, float alpha) {
return (alpha == 0) ? 0 : color_to_linear(non_linear / alpha) * alpha;
}
static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) {
memset(mat4, 0, sizeof(float) * 16);
mat4[0][0] = mat3[0];
mat4[0][1] = mat3[1];
mat4[0][3] = mat3[2];
static void encode_proj_matrix(const float mat3[9], float mat4[4][4]) {
float result[4][4] = {
{ mat3[0], mat3[1], 0, mat3[2] },
{ mat3[3], mat3[4], 0, mat3[5] },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 1 },
};
mat4[1][0] = mat3[3];
mat4[1][1] = mat3[4];
mat4[1][3] = mat3[5];
memcpy(mat4, result, sizeof(result));
}
mat4[2][2] = 1.f;
mat4[3][3] = 1.f;
static void encode_color_matrix(const float mat3[9], float mat4[4][4]) {
float result[4][4] = {
{ mat3[0], mat3[1], mat3[2], 0 },
{ mat3[3], mat3[4], mat3[5], 0 },
{ mat3[6], mat3[7], mat3[8], 0 },
{ 0, 0, 0, 0 },
};
memcpy(mat4, result, sizeof(result));
}
static void render_pass_destroy(struct wlr_vk_render_pass *pass) {
@ -141,6 +146,11 @@ static VkSemaphore render_pass_wait_sync_file(struct wlr_vk_render_pass *pass,
return *sem_ptr;
}
static float get_luminance_multiplier(const struct wlr_color_luminances *src_lum,
const struct wlr_color_luminances *dst_lum) {
return (dst_lum->reference / src_lum->reference) * (src_lum->max / dst_lum->max);
}
static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass);
struct wlr_vk_renderer *renderer = pass->renderer;
@ -162,7 +172,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
assert(stage_cb != NULL);
renderer->stage.cb = NULL;
if (!pass->srgb_pathway) {
if (pass->two_pass) {
// Apply output shader to map blend image to actual output image
vkCmdNextSubpass(render_cb->vk, VK_SUBPASS_CONTENTS_INLINE);
@ -178,26 +188,76 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
.uv_off = { 0, 0 },
.uv_size = { 1, 1 },
};
encode_proj_matrix(final_matrix, vert_pcr_data.mat4);
struct wlr_vk_color_transform *transform = NULL;
size_t dim = 1;
if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_SRGB) {
if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_INVERSE_EOTF) {
transform = get_color_transform(pass->color_transform, renderer);
assert(transform);
dim = transform->lut_3d.dim;
}
struct wlr_vk_frag_output_pcr_data frag_pcr_data = {
.luminance_multiplier = 1,
.lut_3d_offset = 0.5f / dim,
.lut_3d_scale = (float)(dim - 1) / dim,
};
mat3_to_mat4(final_matrix, vert_pcr_data.mat4);
if (pass->color_transform) {
bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_lut3d);
float matrix[9];
if (pass->has_primaries) {
struct wlr_color_primaries srgb;
wlr_color_primaries_from_named(&srgb, WLR_COLOR_NAMED_PRIMARIES_SRGB);
float srgb_to_xyz[9];
wlr_color_primaries_to_xyz(&srgb, srgb_to_xyz);
float dst_primaries_to_xyz[9];
wlr_color_primaries_to_xyz(&pass->primaries, dst_primaries_to_xyz);
float xyz_to_dst_primaries[9];
matrix_invert(xyz_to_dst_primaries, dst_primaries_to_xyz);
wlr_matrix_multiply(matrix, xyz_to_dst_primaries, srgb_to_xyz);
} else {
bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_srgb);
wlr_matrix_identity(matrix);
}
encode_color_matrix(matrix, frag_pcr_data.matrix);
VkPipeline pipeline = VK_NULL_HANDLE;
if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_INVERSE_EOTF) {
pipeline = render_buffer->two_pass.render_setup->output_pipe_lut3d;
} else {
enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22;
if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_INVERSE_EOTF) {
struct wlr_color_transform_inverse_eotf *inverse_eotf =
wlr_color_transform_inverse_eotf_from_base(pass->color_transform);
tf = inverse_eotf->tf;
}
switch (tf) {
case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
pipeline = render_buffer->two_pass.render_setup->output_pipe_identity;
break;
case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
pipeline = render_buffer->two_pass.render_setup->output_pipe_srgb;
break;
case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
pipeline = render_buffer->two_pass.render_setup->output_pipe_pq;
break;
case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
pipeline = render_buffer->two_pass.render_setup->output_pipe_gamma22;
break;
case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
pipeline = render_buffer->two_pass.render_setup->output_pipe_bt1886;
break;
}
struct wlr_color_luminances srgb_lum, dst_lum;
wlr_color_transfer_function_get_default_luminance(
WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum);
wlr_color_transfer_function_get_default_luminance(tf, &dst_lum);
frag_pcr_data.luminance_multiplier = get_luminance_multiplier(&srgb_lum, &dst_lum);
}
bind_pipeline(pass, pipeline);
vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout,
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data);
vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout,
@ -211,7 +271,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
lut_ds = renderer->output_ds_lut3d_dummy;
}
VkDescriptorSet ds[] = {
render_buffer->plain.blend_descriptor_set, // set 0
render_buffer->two_pass.blend_descriptor_set, // set 0
lut_ds, // set 1
};
size_t ds_len = sizeof(ds) / sizeof(ds[0]);
@ -341,30 +401,26 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
// also add acquire/release barriers for the current render buffer
VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL;
if (pass->srgb_pathway) {
if (!render_buffer->srgb.transitioned) {
src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
render_buffer->srgb.transitioned = true;
}
} else {
if (!render_buffer->plain.transitioned) {
src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
render_buffer->plain.transitioned = true;
}
if (!pass->render_buffer_out->transitioned) {
src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
pass->render_buffer_out->transitioned = true;
}
if (pass->two_pass) {
// The render pass changes the blend image layout from
// color attachment to read only, so on each frame, before
// the render pass starts, we change it back
VkImageLayout blend_src_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
if (!render_buffer->plain.blend_transitioned) {
if (!render_buffer->two_pass.blend_transitioned) {
blend_src_layout = VK_IMAGE_LAYOUT_UNDEFINED;
render_buffer->plain.blend_transitioned = true;
render_buffer->two_pass.blend_transitioned = true;
}
VkImageMemoryBarrier blend_acq_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = render_buffer->plain.blend_image,
.image = render_buffer->two_pass.blend_image,
.oldLayout = blend_src_layout,
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.srcAccessMask = VK_ACCESS_SHADER_READ_BIT,
@ -561,7 +617,7 @@ error:
static void render_pass_mark_box_updated(struct wlr_vk_render_pass *pass,
const struct wlr_box *box) {
if (pass->srgb_pathway) {
if (!pass->two_pass) {
return;
}
@ -621,11 +677,8 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, proj);
wlr_matrix_multiply(matrix, pass->projection, matrix);
struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ?
pass->render_buffer->srgb.render_setup :
pass->render_buffer->plain.render_setup;
struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline(
setup,
pass->render_setup,
&(struct wlr_vk_pipeline_key) {
.source = WLR_VK_SHADER_SOURCE_SINGLE_COLOR,
.layout = { .ycbcr_format = NULL },
@ -639,7 +692,7 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
.uv_off = { 0, 0 },
.uv_size = { 1, 1 },
};
mat3_to_mat4(matrix, vert_pcr_data.mat4);
encode_proj_matrix(matrix, vert_pcr_data.mat4);
bind_pipeline(pass, pipe->vk);
vkCmdPushConstants(cb, pipe->layout->vk,
@ -722,20 +775,47 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
src_box.height / options->texture->height,
},
};
mat3_to_mat4(matrix, vert_pcr_data.mat4);
encode_proj_matrix(matrix, vert_pcr_data.mat4);
enum wlr_color_transfer_function tf = options->transfer_function;
if (tf == 0) {
tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22;
}
bool srgb_image_view = false;
enum wlr_vk_texture_transform tex_transform = 0;
switch (tf) {
case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
if (texture->using_mutable_srgb) {
tex_transform = WLR_VK_TEXTURE_TRANSFORM_IDENTITY;
srgb_image_view = true;
} else {
tex_transform = WLR_VK_TEXTURE_TRANSFORM_SRGB;
}
break;
case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
tex_transform = WLR_VK_TEXTURE_TRANSFORM_IDENTITY;
break;
case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
tex_transform = WLR_VK_TEXTURE_TRANSFORM_ST2084_PQ;
break;
case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
tex_transform = WLR_VK_TEXTURE_TRANSFORM_GAMMA22;
break;
case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
tex_transform = WLR_VK_TEXTURE_TRANSFORM_BT1886;
break;
}
struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ?
pass->render_buffer->srgb.render_setup :
pass->render_buffer->plain.render_setup;
struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline(
setup,
pass->render_setup,
&(struct wlr_vk_pipeline_key) {
.source = WLR_VK_SHADER_SOURCE_TEXTURE,
.layout = {
.ycbcr_format = texture->format->is_ycbcr ? texture->format : NULL,
.filter_mode = options->filter_mode,
},
.texture_transform = texture->transform,
.texture_transform = tex_transform,
.blend_mode = !texture->has_alpha && alpha == 1.0 ?
WLR_RENDER_BLEND_MODE_NONE : options->blend_mode,
});
@ -745,12 +825,45 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
}
struct wlr_vk_texture_view *view =
vulkan_texture_get_or_create_view(texture, pipe->layout);
vulkan_texture_get_or_create_view(texture, pipe->layout, srgb_image_view);
if (!view) {
pass->failed = true;
return;
}
float color_matrix[9];
if (options->primaries != NULL) {
struct wlr_color_primaries srgb;
wlr_color_primaries_from_named(&srgb, WLR_COLOR_NAMED_PRIMARIES_SRGB);
float src_primaries_to_xyz[9];
wlr_color_primaries_to_xyz(options->primaries, src_primaries_to_xyz);
float srgb_to_xyz[9];
wlr_color_primaries_to_xyz(&srgb, srgb_to_xyz);
float xyz_to_srgb[9];
matrix_invert(xyz_to_srgb, srgb_to_xyz);
wlr_matrix_multiply(color_matrix, xyz_to_srgb, src_primaries_to_xyz);
} else {
wlr_matrix_identity(color_matrix);
}
float luminance_multiplier = 1;
if (tf != WLR_COLOR_TRANSFER_FUNCTION_SRGB
&& tf != WLR_COLOR_TRANSFER_FUNCTION_GAMMA22) {
struct wlr_color_luminances src_lum, srgb_lum;
wlr_color_transfer_function_get_default_luminance(tf, &src_lum);
wlr_color_transfer_function_get_default_luminance(
WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum);
luminance_multiplier = get_luminance_multiplier(&src_lum, &srgb_lum);
}
struct wlr_vk_frag_texture_pcr_data frag_pcr_data = {
.alpha = alpha,
.luminance_multiplier = luminance_multiplier,
};
encode_color_matrix(color_matrix, frag_pcr_data.matrix);
bind_pipeline(pass, pipe->vk);
vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
@ -759,8 +872,8 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
vkCmdPushConstants(cb, pipe->layout->vk,
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data);
vkCmdPushConstants(cb, pipe->layout->vk,
VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float),
&alpha);
VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data),
sizeof(frag_pcr_data), &frag_pcr_data);
pixman_region32_t clip;
get_clip_region(pass, options->clip, &clip);
@ -851,19 +964,6 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer,
*ds = VK_NULL_HANDLE;
*ds_pool = NULL;
struct wlr_color_transform_lcms2 *tr_lcms2 = NULL;
struct wlr_color_transform_lut_3x1d *tr_lut_3x1d = NULL;
switch (tr->type) {
case COLOR_TRANSFORM_SRGB:
abort(); // unreachable
case COLOR_TRANSFORM_LCMS2:
tr_lcms2 = color_transform_lcms2_from_base(tr);
break;
case COLOR_TRANSFORM_LUT_3X1D:
tr_lut_3x1d = color_transform_lut_3x1d_from_base(tr);
break;
}
// R32G32B32 is not a required Vulkan format
// TODO: use it when available
VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT;
@ -961,11 +1061,7 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer,
b_index * sample_range,
};
float rgb_out[3];
if (tr_lcms2 != NULL) {
color_transform_lcms2_eval(tr_lcms2, rgb_out, rgb_in);
} else {
color_transform_lut_3x1d_eval(tr_lut_3x1d, rgb_out, rgb_in);
}
wlr_color_transform_eval(tr, rgb_out, rgb_in);
dst[dst_offset] = rgb_out[0];
dst[dst_offset + 1] = rgb_out[1];
@ -1035,7 +1131,7 @@ static struct wlr_vk_color_transform *vk_color_transform_create(
return NULL;
}
if (transform->type != COLOR_TRANSFORM_SRGB) {
if (transform->type != COLOR_TRANSFORM_INVERSE_EOTF) {
vk_transform->lut_3d.dim = 33;
if (!create_3d_lut_image(renderer, transform,
vk_transform->lut_3d.dim,
@ -1064,29 +1160,68 @@ static const struct wlr_addon_interface vk_color_transform_impl = {
struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer,
struct wlr_vk_render_buffer *buffer, const struct wlr_buffer_pass_options *options) {
bool using_srgb_pathway;
uint32_t inv_eotf;
if (options != NULL && options->color_transform != NULL) {
using_srgb_pathway = false;
if (options->color_transform->type == COLOR_TRANSFORM_INVERSE_EOTF) {
struct wlr_color_transform_inverse_eotf *tr =
wlr_color_transform_inverse_eotf_from_base(options->color_transform);
inv_eotf = tr->tf;
} else {
// Color transform is not an inverse EOTF
inv_eotf = 0;
}
} else {
// This is the default when unspecified
inv_eotf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22;
}
if (!get_color_transform(options->color_transform, renderer)) {
bool using_linear_pathway = inv_eotf == WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR;
bool using_srgb_pathway = inv_eotf == WLR_COLOR_TRANSFER_FUNCTION_SRGB &&
buffer->srgb.out.framebuffer != VK_NULL_HANDLE;
bool using_two_pass_pathway = !using_linear_pathway && !using_srgb_pathway;
if (using_linear_pathway && !buffer->linear.out.image_view) {
struct wlr_dmabuf_attributes attribs;
wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs);
if (!vulkan_setup_one_pass_framebuffer(buffer, &attribs, false)) {
wlr_log(WLR_ERROR, "Failed to set up blend image");
return NULL;
}
}
if (using_two_pass_pathway) {
if (options != NULL && options->color_transform != NULL &&
!get_color_transform(options->color_transform, renderer)) {
/* Try to create a new color transform */
if (!vk_color_transform_create(renderer, options->color_transform)) {
wlr_log(WLR_ERROR, "Failed to create color transform");
return NULL;
}
}
} else {
// Use srgb pathway if it is the default/has already been set up
using_srgb_pathway = buffer->srgb.framebuffer != VK_NULL_HANDLE;
if (!buffer->two_pass.out.image_view) {
struct wlr_dmabuf_attributes attribs;
wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs);
if (!vulkan_setup_two_pass_framebuffer(buffer, &attribs)) {
wlr_log(WLR_ERROR, "Failed to set up blend image");
return NULL;
}
}
}
if (!using_srgb_pathway && !buffer->plain.image_view) {
struct wlr_dmabuf_attributes attribs;
wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs);
if (!vulkan_setup_plain_framebuffer(buffer, &attribs)) {
wlr_log(WLR_ERROR, "Failed to set up blend image");
return NULL;
}
struct wlr_vk_render_format_setup *render_setup;
struct wlr_vk_render_buffer_out *buffer_out;
if (using_two_pass_pathway) {
render_setup = buffer->two_pass.render_setup;
buffer_out = &buffer->two_pass.out;
} else if (using_srgb_pathway) {
render_setup = buffer->srgb.render_setup;
buffer_out = &buffer->srgb.out;
} else if (using_linear_pathway) {
render_setup = buffer->linear.render_setup;
buffer_out = &buffer->linear.out;
} else {
abort(); // unreachable
}
struct wlr_vk_render_pass *pass = calloc(1, sizeof(*pass));
@ -1096,7 +1231,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend
wlr_render_pass_init(&pass->base, &render_pass_impl);
pass->renderer = renderer;
pass->srgb_pathway = using_srgb_pathway;
pass->two_pass = using_two_pass_pathway;
if (options != NULL && options->color_transform != NULL) {
pass->color_transform = wlr_color_transform_ref(options->color_transform);
}
@ -1104,6 +1239,10 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend
pass->signal_timeline = wlr_drm_syncobj_timeline_ref(options->signal_timeline);
pass->signal_point = options->signal_point;
}
if (options != NULL && options->primaries != NULL) {
pass->has_primaries = true;
pass->primaries = *options->primaries;
}
rect_union_init(&pass->updated_region);
@ -1140,14 +1279,9 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderArea = rect,
.clearValueCount = 0,
.renderPass = render_setup->render_pass,
.framebuffer = buffer_out->framebuffer,
};
if (pass->srgb_pathway) {
rp_info.renderPass = buffer->srgb.render_setup->render_pass;
rp_info.framebuffer = buffer->srgb.framebuffer;
} else {
rp_info.renderPass = buffer->plain.render_setup->render_pass;
rp_info.framebuffer = buffer->plain.framebuffer;
}
vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE);
vkCmdSetViewport(cb->vk, 0, 1, &(VkViewport){
@ -1162,6 +1296,8 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend
wlr_buffer_lock(buffer->wlr_buffer);
pass->render_buffer = buffer;
pass->render_buffer_out = buffer_out;
pass->render_setup = render_setup;
pass->command_buffer = cb;
return pass;
}

View file

@ -66,59 +66,72 @@ static struct wlr_vk_descriptor_pool *alloc_ds(
struct wl_list *pool_list, size_t *last_pool_size) {
VkResult res;
bool found = false;
struct wlr_vk_descriptor_pool *pool;
wl_list_for_each(pool, pool_list, link) {
if (pool->free > 0) {
found = true;
break;
}
}
if (!found) { // create new pool
pool = calloc(1, sizeof(*pool));
if (!pool) {
wlr_log_errno(WLR_ERROR, "allocation failed");
return NULL;
}
size_t count = 2 * (*last_pool_size);
if (!count) {
count = start_descriptor_pool_size;
}
pool->free = count;
VkDescriptorPoolSize pool_size = {
.descriptorCount = count,
.type = type,
};
VkDescriptorPoolCreateInfo dpool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = count,
.poolSizeCount = 1,
.pPoolSizes = &pool_size,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
};
res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL,
&pool->pool);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateDescriptorPool", res);
free(pool);
return NULL;
}
*last_pool_size = count;
wl_list_insert(pool_list, &pool->link);
}
VkDescriptorSetAllocateInfo ds_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorSetCount = 1,
.pSetLayouts = layout,
.descriptorPool = pool->pool,
};
struct wlr_vk_descriptor_pool *pool;
wl_list_for_each(pool, pool_list, link) {
if (pool->free > 0) {
ds_info.descriptorPool = pool->pool;
res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds);
switch (res) {
case VK_ERROR_FRAGMENTED_POOL:
case VK_ERROR_OUT_OF_POOL_MEMORY:
// Descriptor sets with more than one descriptor can cause us
// to run out of pool memory early or lead to fragmentation
// that makes the pool unable to service our allocation
// request. Try the next pool or allocate a new one.
continue;
case VK_SUCCESS:
--pool->free;
return pool;
default:
wlr_vk_error("vkAllocateDescriptorSets", res);
return NULL;
}
}
}
pool = calloc(1, sizeof(*pool));
if (!pool) {
wlr_log_errno(WLR_ERROR, "allocation failed");
return NULL;
}
size_t count = 2 * (*last_pool_size);
if (!count) {
count = start_descriptor_pool_size;
}
pool->free = count;
VkDescriptorPoolSize pool_size = {
.descriptorCount = count,
.type = type,
};
VkDescriptorPoolCreateInfo dpool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = count,
.poolSizeCount = 1,
.pPoolSizes = &pool_size,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
};
res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL,
&pool->pool);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateDescriptorPool", res);
free(pool);
return NULL;
}
*last_pool_size = count;
wl_list_insert(pool_list, &pool->link);
ds_info.descriptorPool = pool->pool;
res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds);
if (res != VK_SUCCESS) {
wlr_vk_error("vkAllocateDescriptorSets", res);
@ -158,8 +171,12 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer,
VkDevice dev = renderer->dev->dev;
vkDestroyRenderPass(dev, setup->render_pass, NULL);
vkDestroyPipeline(dev, setup->output_pipe_identity, NULL);
vkDestroyPipeline(dev, setup->output_pipe_srgb, NULL);
vkDestroyPipeline(dev, setup->output_pipe_pq, NULL);
vkDestroyPipeline(dev, setup->output_pipe_lut3d, NULL);
vkDestroyPipeline(dev, setup->output_pipe_gamma22, NULL);
vkDestroyPipeline(dev, setup->output_pipe_bt1886, NULL);
struct wlr_vk_pipeline *pipeline, *tmp_pipeline;
wl_list_for_each_safe(pipeline, tmp_pipeline, &setup->pipelines, link) {
@ -589,6 +606,12 @@ void vulkan_reset_command_buffer(struct wlr_vk_command_buffer *cb) {
}
}
static void finish_render_buffer_out(struct wlr_vk_render_buffer_out *out,
VkDevice dev) {
vkDestroyFramebuffer(dev, out->framebuffer, NULL);
vkDestroyImageView(dev, out->image_view, NULL);
}
static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) {
wl_list_remove(&buffer->link);
wlr_addon_finish(&buffer->addon);
@ -602,17 +625,16 @@ static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) {
wlr_vk_error("vkQueueWaitIdle", res);
}
vkDestroyFramebuffer(dev, buffer->srgb.framebuffer, NULL);
vkDestroyImageView(dev, buffer->srgb.image_view, NULL);
finish_render_buffer_out(&buffer->linear.out, dev);
finish_render_buffer_out(&buffer->srgb.out, dev);
vkDestroyFramebuffer(dev, buffer->plain.framebuffer, NULL);
vkDestroyImageView(dev, buffer->plain.image_view, NULL);
vkDestroyImage(dev, buffer->plain.blend_image, NULL);
vkFreeMemory(dev, buffer->plain.blend_memory, NULL);
vkDestroyImageView(dev, buffer->plain.blend_image_view, NULL);
if (buffer->plain.blend_attachment_pool) {
vulkan_free_ds(buffer->renderer, buffer->plain.blend_attachment_pool,
buffer->plain.blend_descriptor_set);
finish_render_buffer_out(&buffer->two_pass.out, dev);
vkDestroyImage(dev, buffer->two_pass.blend_image, NULL);
vkFreeMemory(dev, buffer->two_pass.blend_memory, NULL);
vkDestroyImageView(dev, buffer->two_pass.blend_image_view, NULL);
if (buffer->two_pass.blend_attachment_pool) {
vulkan_free_ds(buffer->renderer, buffer->two_pass.blend_attachment_pool,
buffer->two_pass.blend_descriptor_set);
}
vkDestroyImage(dev, buffer->image, NULL);
@ -633,7 +655,7 @@ static struct wlr_addon_interface render_buffer_addon_impl = {
.destroy = handle_render_buffer_destroy,
};
bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer,
bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer,
const struct wlr_dmabuf_attributes *dmabuf) {
struct wlr_vk_renderer *renderer = buffer->renderer;
VkResult res;
@ -661,15 +683,15 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer,
},
};
res = vkCreateImageView(dev, &view_info, NULL, &buffer->plain.image_view);
res = vkCreateImageView(dev, &view_info, NULL, &buffer->two_pass.out.image_view);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateImageView failed", res);
goto error;
}
buffer->plain.render_setup = find_or_create_render_setup(
buffer->two_pass.render_setup = find_or_create_render_setup(
renderer, &fmt->format, true);
if (!buffer->plain.render_setup) {
if (!buffer->two_pass.render_setup) {
goto error;
}
@ -689,14 +711,14 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer,
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT,
};
res = vkCreateImage(dev, &img_info, NULL, &buffer->plain.blend_image);
res = vkCreateImage(dev, &img_info, NULL, &buffer->two_pass.blend_image);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateImage failed", res);
goto error;
}
VkMemoryRequirements mem_reqs;
vkGetImageMemoryRequirements(dev, buffer->plain.blend_image, &mem_reqs);
vkGetImageMemoryRequirements(dev, buffer->two_pass.blend_image, &mem_reqs);
int mem_type_index = vulkan_find_mem_type(renderer->dev,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits);
@ -711,13 +733,13 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer,
.memoryTypeIndex = mem_type_index,
};
res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->plain.blend_memory);
res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->two_pass.blend_memory);
if (res != VK_SUCCESS) {
wlr_vk_error("vkAllocatorMemory failed", res);
goto error;
}
res = vkBindImageMemory(dev, buffer->plain.blend_image, buffer->plain.blend_memory, 0);
res = vkBindImageMemory(dev, buffer->two_pass.blend_image, buffer->two_pass.blend_memory, 0);
if (res != VK_SUCCESS) {
wlr_vk_error("vkBindMemory failed", res);
goto error;
@ -725,7 +747,7 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer,
VkImageViewCreateInfo blend_view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = buffer->plain.blend_image,
.image = buffer->two_pass.blend_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = img_info.format,
.components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
@ -741,50 +763,50 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer,
},
};
res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->plain.blend_image_view);
res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->two_pass.blend_image_view);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateImageView failed", res);
goto error;
}
buffer->plain.blend_attachment_pool = vulkan_alloc_blend_ds(renderer,
&buffer->plain.blend_descriptor_set);
if (!buffer->plain.blend_attachment_pool) {
buffer->two_pass.blend_attachment_pool = vulkan_alloc_blend_ds(renderer,
&buffer->two_pass.blend_descriptor_set);
if (!buffer->two_pass.blend_attachment_pool) {
wlr_log(WLR_ERROR, "failed to allocate descriptor");
goto error;
}
VkDescriptorImageInfo ds_attach_info = {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = buffer->plain.blend_image_view,
.imageView = buffer->two_pass.blend_image_view,
.sampler = VK_NULL_HANDLE,
};
VkWriteDescriptorSet ds_write = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
.dstSet = buffer->plain.blend_descriptor_set,
.dstSet = buffer->two_pass.blend_descriptor_set,
.dstBinding = 0,
.pImageInfo = &ds_attach_info,
};
vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL);
VkImageView attachments[2] = {
buffer->plain.blend_image_view,
buffer->plain.image_view
VkImageView attachments[] = {
buffer->two_pass.blend_image_view,
buffer->two_pass.out.image_view,
};
VkFramebufferCreateInfo fb_info = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.attachmentCount = 2,
.attachmentCount = sizeof(attachments) / sizeof(attachments[0]),
.pAttachments = attachments,
.flags = 0u,
.width = dmabuf->width,
.height = dmabuf->height,
.layers = 1u,
.renderPass = buffer->plain.render_setup->render_pass,
.renderPass = buffer->two_pass.render_setup->render_pass,
};
res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->plain.framebuffer);
res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->two_pass.out.framebuffer);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateFramebuffer", res);
goto error;
@ -798,8 +820,8 @@ error:
return false;
}
static bool vulkan_setup_srgb_framebuffer(struct wlr_vk_render_buffer *buffer,
const struct wlr_dmabuf_attributes *dmabuf) {
bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffer,
const struct wlr_dmabuf_attributes *dmabuf, bool srgb) {
struct wlr_vk_renderer *renderer = buffer->renderer;
VkResult res;
VkDevice dev = renderer->dev->dev;
@ -808,14 +830,18 @@ static bool vulkan_setup_srgb_framebuffer(struct wlr_vk_render_buffer *buffer,
renderer->dev, dmabuf->format);
assert(fmt);
assert(fmt->format.vk_srgb);
// Set up the srgb framebuffer by default; plain framebuffer and
VkFormat vk_fmt = srgb ? fmt->format.vk_srgb : fmt->format.vk;
assert(vk_fmt != VK_FORMAT_UNDEFINED);
struct wlr_vk_render_buffer_out *out = srgb ? &buffer->srgb.out : &buffer->linear.out;
// Set up the srgb framebuffer by default; two-pass framebuffer and
// blending image will be set up later if necessary
VkImageViewCreateInfo view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = buffer->image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = fmt->format.vk_srgb,
.format = vk_fmt,
.components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.b = VK_COMPONENT_SWIZZLE_IDENTITY,
@ -829,35 +855,43 @@ static bool vulkan_setup_srgb_framebuffer(struct wlr_vk_render_buffer *buffer,
},
};
res = vkCreateImageView(dev, &view_info, NULL, &buffer->srgb.image_view);
res = vkCreateImageView(dev, &view_info, NULL, &out->image_view);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateImageView failed", res);
goto error;
}
buffer->srgb.render_setup = find_or_create_render_setup(
renderer, &fmt->format, false);
if (!buffer->srgb.render_setup) {
struct wlr_vk_render_format_setup *render_setup =
find_or_create_render_setup(renderer, &fmt->format, false);
if (!render_setup) {
goto error;
}
VkFramebufferCreateInfo fb_info = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &buffer->srgb.image_view,
.pAttachments = &out->image_view,
.flags = 0u,
.width = dmabuf->width,
.height = dmabuf->height,
.layers = 1u,
.renderPass = buffer->srgb.render_setup->render_pass,
.renderPass = render_setup->render_pass,
};
res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->srgb.framebuffer);
res = vkCreateFramebuffer(dev, &fb_info, NULL, &out->framebuffer);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateFramebuffer", res);
goto error;
}
if (srgb) {
buffer->srgb.render_setup = render_setup;
} else {
buffer->linear.render_setup = render_setup;
}
return true;
error:
// cleaning up everything is the caller's responsibility,
// since it will need to do this anyway if framebuffer setup fails
@ -901,12 +935,12 @@ static struct wlr_vk_render_buffer *create_render_buffer(
}
if (using_mutable_srgb) {
if (!vulkan_setup_srgb_framebuffer(buffer, &dmabuf)) {
if (!vulkan_setup_one_pass_framebuffer(buffer, &dmabuf, true)) {
goto error;
}
} else {
// Set up the plain framebuffer & blending image
if (!vulkan_setup_plain_framebuffer(buffer, &dmabuf)) {
// Set up the two-pass framebuffer & blending image
if (!vulkan_setup_two_pass_framebuffer(buffer, &dmabuf)) {
goto error;
}
}
@ -1464,14 +1498,14 @@ static bool init_tex_layouts(struct wlr_vk_renderer *renderer,
return false;
}
VkPushConstantRange pc_ranges[2] = {
VkPushConstantRange pc_ranges[] = {
{
.size = sizeof(struct wlr_vk_vert_pcr_data),
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
},
{
.offset = pc_ranges[0].size,
.size = sizeof(float) * 4, // alpha or color
.size = sizeof(struct wlr_vk_frag_texture_pcr_data),
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
},
};
@ -1480,7 +1514,7 @@ static bool init_tex_layouts(struct wlr_vk_renderer *renderer,
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = out_ds_layout,
.pushConstantRangeCount = 2,
.pushConstantRangeCount = sizeof(pc_ranges) / sizeof(pc_ranges[0]),
.pPushConstantRanges = pc_ranges,
};
@ -1558,7 +1592,7 @@ static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer) {
}
// pipeline layout -- standard vertex uniforms, no shader uniforms
VkPushConstantRange pc_ranges[2] = {
VkPushConstantRange pc_ranges[] = {
{
.offset = 0,
.size = sizeof(struct wlr_vk_vert_pcr_data),
@ -1571,16 +1605,16 @@ static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer) {
},
};
VkDescriptorSetLayout out_ds_layouts[2] = {
VkDescriptorSetLayout out_ds_layouts[] = {
renderer->output_ds_srgb_layout,
renderer->output_ds_lut3d_layout,
};
VkPipelineLayoutCreateInfo pl_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 2,
.setLayoutCount = sizeof(out_ds_layouts) / sizeof(out_ds_layouts[0]),
.pSetLayouts = out_ds_layouts,
.pushConstantRangeCount = 2,
.pushConstantRangeCount = sizeof(pc_ranges) / sizeof(pc_ranges[0]),
.pPushConstantRanges = pc_ranges,
};
@ -1753,14 +1787,14 @@ struct wlr_vk_pipeline *setup_get_or_create_pipeline(
.scissorCount = 1,
};
VkDynamicState dynStates[2] = {
VkDynamicState dyn_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo dynamic = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pDynamicStates = dynStates,
.dynamicStateCount = 2,
.pDynamicStates = dyn_states,
.dynamicStateCount = sizeof(dyn_states) / sizeof(dyn_states[0]),
};
VkPipelineVertexInputStateCreateInfo vertex = {
@ -1772,7 +1806,7 @@ struct wlr_vk_pipeline *setup_get_or_create_pipeline(
.layout = pipeline_layout->vk,
.renderPass = setup->render_pass,
.subpass = 0,
.stageCount = 2,
.stageCount = sizeof(stages) / sizeof(stages[0]),
.pStages = stages,
.pInputAssemblyState = &assembly,
@ -1815,7 +1849,7 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer,
.pData = &output_transform_type,
};
VkPipelineShaderStageCreateInfo tex_stages[2] = {
VkPipelineShaderStageCreateInfo tex_stages[] = {
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
@ -1870,14 +1904,14 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer,
.scissorCount = 1,
};
VkDynamicState dynStates[2] = {
VkDynamicState dyn_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo dynamic = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pDynamicStates = dynStates,
.dynamicStateCount = 2,
.pDynamicStates = dyn_states,
.dynamicStateCount = sizeof(dyn_states) / sizeof(dyn_states[0]),
};
VkPipelineVertexInputStateCreateInfo vertex = {
@ -1890,7 +1924,7 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer,
.layout = pipe_layout,
.renderPass = rp,
.subpass = 1, // second subpass!
.stageCount = 2,
.stageCount = sizeof(tex_stages) / sizeof(tex_stages[0]),
.pStages = tex_stages,
.pInputAssemblyState = &assembly,
.pRasterizationState = &rasterization,
@ -2183,7 +2217,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup(
VkResult res;
if (use_blending_buffer) {
VkAttachmentDescription attachments[2] = {
VkAttachmentDescription attachments[] = {
{
.format = VK_FORMAT_R16G16B16A16_SFLOAT,
.samples = VK_SAMPLE_COUNT_1_BIT,
@ -2221,7 +2255,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup(
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
VkSubpassDescription subpasses[2] = {
VkSubpassDescription subpasses[] = {
{
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1,
@ -2236,7 +2270,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup(
}
};
VkSubpassDependency deps[3] = {
VkSubpassDependency deps[] = {
{
.srcSubpass = VK_SUBPASS_EXTERNAL,
.srcStageMask = VK_PIPELINE_STAGE_HOST_BIT |
@ -2278,11 +2312,11 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup(
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.attachmentCount = 2u,
.attachmentCount = sizeof(attachments) / sizeof(attachments[0]),
.pAttachments = attachments,
.subpassCount = 2u,
.subpassCount = sizeof(subpasses) / sizeof(subpasses[0]),
.pSubpasses = subpasses,
.dependencyCount = 3u,
.dependencyCount = sizeof(deps) / sizeof(deps[0]),
.pDependencies = deps,
};
@ -2293,14 +2327,34 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup(
}
// this is only well defined if render pass has a 2nd subpass
if (!init_blend_to_output_pipeline(
renderer, setup->render_pass, renderer->output_pipe_layout,
&setup->output_pipe_identity, WLR_VK_OUTPUT_TRANSFORM_IDENTITY)) {
goto error;
}
if (!init_blend_to_output_pipeline(
renderer, setup->render_pass, renderer->output_pipe_layout,
&setup->output_pipe_lut3d, WLR_VK_OUTPUT_TRANSFORM_LUT3D)) {
goto error;
}
if (!init_blend_to_output_pipeline(
renderer, setup->render_pass, renderer->output_pipe_layout,
&setup->output_pipe_srgb, WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB)) {
goto error;
}
if (!init_blend_to_output_pipeline(
renderer, setup->render_pass, renderer->output_pipe_layout,
&setup->output_pipe_pq, WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ)) {
goto error;
}
if (!init_blend_to_output_pipeline(
renderer, setup->render_pass, renderer->output_pipe_layout,
&setup->output_pipe_gamma22, WLR_VK_OUTPUT_TRANSFORM_INVERSE_GAMMA22)) {
goto error;
}
if (!init_blend_to_output_pipeline(
renderer, setup->render_pass, renderer->output_pipe_layout,
&setup->output_pipe_srgb, WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB)) {
&setup->output_pipe_bt1886, WLR_VK_OUTPUT_TRANSFORM_INVERSE_BT1886)) {
goto error;
}
} else {
@ -2327,7 +2381,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup(
.pColorAttachments = &color_ref,
};
VkSubpassDependency deps[2] = {
VkSubpassDependency deps[] = {
{
.srcSubpass = VK_SUBPASS_EXTERNAL,
.srcStageMask = VK_PIPELINE_STAGE_HOST_BIT |
@ -2362,7 +2416,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup(
.pAttachments = &attachment,
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 2u,
.dependencyCount = sizeof(deps) / sizeof(deps[0]),
.pDependencies = deps,
};
@ -2430,6 +2484,7 @@ struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev
renderer->dev = dev;
wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl, WLR_BUFFER_CAP_DMABUF);
renderer->wlr_renderer.features.input_color_transform = true;
renderer->wlr_renderer.features.output_color_transform = true;
wl_list_init(&renderer->stage.buffers);
wl_list_init(&renderer->foreign_textures);
@ -2501,14 +2556,13 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) {
if (!phdev) {
// We rather fail here than doing some guesswork
wlr_log(WLR_ERROR, "Could not match drm and vulkan device");
return NULL;
goto error;
}
struct wlr_vk_device *dev = vulkan_device_create(ini, phdev);
if (!dev) {
wlr_log(WLR_ERROR, "Failed to create vulkan device");
vulkan_instance_destroy(ini);
return NULL;
goto error;
}
// Do not use the drm_fd that was passed in: we should prefer the render
@ -2516,6 +2570,10 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) {
dev->drm_fd = vulkan_open_phdev_drm_fd(phdev);
return vulkan_renderer_create_for_device(dev);
error:
vulkan_instance_destroy(ini);
return NULL;
}
VkInstance wlr_vk_renderer_get_instance(struct wlr_renderer *renderer) {

View file

@ -8,16 +8,22 @@ layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 out_color;
/* struct wlr_vk_frag_output_pcr_data */
layout(push_constant) uniform UBO {
layout(offset = 80) float lut_3d_offset;
layout(push_constant, row_major) uniform UBO {
layout(offset = 80) mat4 matrix;
float luminance_multiplier;
float lut_3d_offset;
float lut_3d_scale;
} data;
layout (constant_id = 0) const int OUTPUT_TRANSFORM = 0;
// Matches enum wlr_vk_output_transform
#define OUTPUT_TRANSFORM_INVERSE_SRGB 0
#define OUTPUT_TRANSFORM_LUT_3D 1
#define OUTPUT_TRANSFORM_IDENTITY 0
#define OUTPUT_TRANSFORM_INVERSE_SRGB 1
#define OUTPUT_TRANSFORM_INVERSE_ST2084_PQ 2
#define OUTPUT_TRANSFORM_LUT_3D 3
#define OUTPUT_TRANSFORM_INVERSE_GAMMA22 4
#define OUTPUT_TRANSFORM_INVERSE_BT1886 5
float linear_channel_to_srgb(float x) {
return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055);
@ -31,6 +37,25 @@ vec3 linear_color_to_srgb(vec3 color) {
);
}
vec3 linear_color_to_pq(vec3 color) {
// H.273 TransferCharacteristics code point 16
float c1 = 0.8359375;
float c2 = 18.8515625;
float c3 = 18.6875;
float m = 78.84375;
float n = 0.1593017578125;
vec3 pow_n = pow(clamp(color, vec3(0), vec3(1)), vec3(n));
return pow((vec3(c1) + c2 * pow_n) / (vec3(1) + c3 * pow_n), vec3(m));
}
vec3 linear_color_to_bt1886(vec3 color) {
float lb = pow(0.0001, 1.0 / 2.4);
float lw = pow(1.0, 1.0 / 2.4);
float a = pow(lw - lb, 2.4);
float b = lb / (lw - lb);
return pow(color / a, vec3(1.0 / 2.4)) - vec3(b);
}
void main() {
vec4 in_color = subpassLoad(in_color).rgba;
@ -43,13 +68,23 @@ void main() {
rgb = in_color.rgb / alpha;
}
rgb *= data.luminance_multiplier;
rgb = mat3(data.matrix) * rgb;
if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D) {
// Apply 3D LUT
vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale;
rgb = texture(lut_3d, pos).rgb;
} else { // OUTPUT_TRANSFORM_INVERSE_SRGB
} else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_ST2084_PQ) {
rgb = linear_color_to_pq(rgb);
} else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_SRGB) {
// Produce sRGB encoded values
rgb = linear_color_to_srgb(rgb);
} else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_GAMMA22) {
rgb = pow(rgb, vec3(1. / 2.2));
} else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_BT1886) {
rgb = linear_color_to_bt1886(rgb);
}
// Back to pre-multiplied alpha

View file

@ -5,8 +5,11 @@ layout(set = 0, binding = 0) uniform sampler2D tex;
layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 out_color;
layout(push_constant) uniform UBO {
layout(offset = 80) float alpha;
// struct wlr_vk_frag_texture_pcr_data
layout(push_constant, row_major) uniform UBO {
layout(offset = 80) mat4 matrix;
float alpha;
float luminance_multiplier;
} data;
layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0;
@ -14,6 +17,9 @@ layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0;
// Matches enum wlr_vk_texture_transform
#define TEXTURE_TRANSFORM_IDENTITY 0
#define TEXTURE_TRANSFORM_SRGB 1
#define TEXTURE_TRANSFORM_ST2084_PQ 2
#define TEXTURE_TRANSFORM_GAMMA22 3
#define TEXTURE_TRANSFORM_BT1886 4
float srgb_channel_to_linear(float x) {
return mix(x / 12.92,
@ -21,27 +27,61 @@ float srgb_channel_to_linear(float x) {
x > 0.04045);
}
vec4 srgb_color_to_linear(vec4 color) {
if (color.a == 0) {
return vec4(0);
}
color.rgb /= color.a;
color.rgb = vec3(
vec3 srgb_color_to_linear(vec3 color) {
return vec3(
srgb_channel_to_linear(color.r),
srgb_channel_to_linear(color.g),
srgb_channel_to_linear(color.b)
);
color.rgb *= color.a;
return color;
}
vec3 pq_color_to_linear(vec3 color) {
float inv_m1 = 1 / 0.1593017578125;
float inv_m2 = 1 / 78.84375;
float c1 = 0.8359375;
float c2 = 18.8515625;
float c3 = 18.6875;
vec3 num = max(pow(color, vec3(inv_m2)) - c1, 0);
vec3 denom = c2 - c3 * pow(color, vec3(inv_m2));
return pow(num / denom, vec3(inv_m1));
}
vec3 bt1886_color_to_linear(vec3 color) {
float lb = pow(0.0001, 1.0 / 2.4);
float lw = pow(1.0, 1.0 / 2.4);
float a = pow(lw - lb, 2.4);
float b = lb / (lw - lb);
return a * pow(color + vec3(b), vec3(2.4));
}
void main() {
vec4 val = textureLod(tex, uv, 0);
if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_SRGB) {
out_color = srgb_color_to_linear(val);
} else { // TEXTURE_TRANSFORM_IDENTITY
out_color = val;
vec4 in_color = textureLod(tex, uv, 0);
// Convert from pre-multiplied alpha to straight alpha
float alpha = in_color.a;
vec3 rgb;
if (alpha == 0) {
rgb = vec3(0);
} else {
rgb = in_color.rgb / alpha;
}
if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_SRGB) {
rgb = srgb_color_to_linear(rgb);
} else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_ST2084_PQ) {
rgb = pq_color_to_linear(rgb);
} else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_GAMMA22) {
rgb = pow(rgb, vec3(2.2));
} else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_BT1886) {
rgb = bt1886_color_to_linear(rgb);
}
rgb *= data.luminance_multiplier;
rgb = mat3(data.matrix) * rgb;
// Back to pre-multiplied alpha
out_color = vec4(rgb * alpha, alpha);
out_color *= data.alpha;
}

View file

@ -269,10 +269,12 @@ static struct wlr_vk_texture *vulkan_texture_create(
}
struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_texture *texture,
const struct wlr_vk_pipeline_layout *pipeline_layout) {
const struct wlr_vk_pipeline_layout *pipeline_layout, bool srgb) {
assert(texture->using_mutable_srgb || !srgb);
struct wlr_vk_texture_view *view;
wl_list_for_each(view, &texture->views, link) {
if (view->layout == pipeline_layout) {
if (view->layout == pipeline_layout && view->srgb == srgb) {
return view;
}
}
@ -283,6 +285,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text
}
view->layout = pipeline_layout;
view->srgb = srgb;
VkResult res;
VkDevice dev = texture->renderer->dev->dev;
@ -290,8 +293,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text
VkImageViewCreateInfo view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = texture->using_mutable_srgb ? texture->format->vk_srgb
: texture->format->vk,
.format = srgb ? texture->format->vk_srgb : texture->format->vk,
.components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.b = VK_COMPONENT_SWIZZLE_IDENTITY,
@ -353,10 +355,10 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text
static void texture_set_format(struct wlr_vk_texture *texture,
const struct wlr_vk_format *format, bool has_mutable_srgb) {
assert(!(format->is_ycbcr && has_mutable_srgb));
texture->format = format;
texture->using_mutable_srgb = has_mutable_srgb;
texture->transform = !format->is_ycbcr && has_mutable_srgb ?
WLR_VK_TEXTURE_TRANSFORM_IDENTITY : WLR_VK_TEXTURE_TRANSFORM_SRGB;
const struct wlr_pixel_format_info *format_info =
drm_get_pixel_format_info(format->drm);
@ -397,14 +399,14 @@ static struct wlr_texture *vulkan_texture_from_pixels(
texture_set_format(texture, &fmt->format, fmt->shm.has_mutable_srgb);
VkFormat view_formats[2] = {
VkFormat view_formats[] = {
fmt->format.vk,
fmt->format.vk_srgb,
};
VkImageFormatListCreateInfoKHR list_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
.pViewFormats = view_formats,
.viewFormatCount = 2,
.viewFormatCount = sizeof(view_formats) / sizeof(view_formats[0]),
};
VkImageCreateInfo img_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@ -598,14 +600,14 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer,
};
eimg.pNext = &mod_info;
VkFormat view_formats[2] = {
VkFormat view_formats[] = {
fmt->format.vk,
fmt->format.vk_srgb,
};
VkImageFormatListCreateInfoKHR list_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
.pViewFormats = view_formats,
.viewFormatCount = 2,
.viewFormatCount = sizeof(view_formats) / sizeof(view_formats[0]),
};
if (mod->has_mutable_srgb) {
mod_info.pNext = &list_info;

View file

@ -1,6 +1,4 @@
PKG_CONFIG?=pkg-config
WAYLAND_PROTOCOLS!=$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols
WAYLAND_SCANNER!=$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner
PKGS="wlroots-0.20" wayland-server xkbcommon
CFLAGS_PKG_CONFIG!=$(PKG_CONFIG) --cflags $(PKGS)
@ -9,19 +7,12 @@ LIBS!=$(PKG_CONFIG) --libs $(PKGS)
all: tinywl
# wayland-scanner is a tool which generates C headers and rigging for Wayland
# protocols, which are specified in XML. wlroots requires you to rig these up
# to your build system yourself and provide them in the include path.
xdg-shell-protocol.h:
$(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
tinywl.o: tinywl.c xdg-shell-protocol.h
tinywl.o: tinywl.c
$(CC) -c $< -g -Werror $(CFLAGS) -I. -DWLR_USE_UNSTABLE -o $@
tinywl: tinywl.o
$(CC) $^ $> -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@
clean:
rm -f tinywl tinywl.o xdg-shell-protocol.h
rm -f tinywl tinywl.o
.PHONY: all clean

View file

@ -1,5 +1,5 @@
executable(
'tinywl',
['tinywl.c', protocols_server_header['xdg-shell']],
'tinywl.c',
dependencies: wlroots,
)

View file

@ -56,6 +56,7 @@ struct tinywl_server {
struct wlr_seat *seat;
struct wl_listener new_input;
struct wl_listener request_cursor;
struct wl_listener pointer_focus_change;
struct wl_listener request_set_selection;
struct wl_list keyboards;
enum tinywl_cursor_mode cursor_mode;
@ -333,6 +334,18 @@ static void seat_request_cursor(struct wl_listener *listener, void *data) {
}
}
static void seat_pointer_focus_change(struct wl_listener *listener, void *data) {
struct tinywl_server *server = wl_container_of(
listener, server, pointer_focus_change);
/* This event is raised when the pointer focus is changed, including when the
* client is closed. We set the cursor image to its default if target surface
* is NULL */
struct wlr_seat_pointer_focus_change_event *event = data;
if (event->new_surface == NULL) {
wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default");
}
}
static void seat_request_set_selection(struct wl_listener *listener, void *data) {
/* This event is raised by the seat when a client wants to set the selection,
* usually when the user copies something. wlroots allows compositors to
@ -1018,6 +1031,9 @@ int main(int argc, char *argv[]) {
server.request_cursor.notify = seat_request_cursor;
wl_signal_add(&server.seat->events.request_set_cursor,
&server.request_cursor);
server.pointer_focus_change.notify = seat_pointer_focus_change;
wl_signal_add(&server.seat->pointer_state.events.focus_change,
&server.pointer_focus_change);
server.request_set_selection.notify = seat_request_set_selection;
wl_signal_add(&server.seat->events.request_set_selection,
&server.request_set_selection);
@ -1069,6 +1085,7 @@ int main(int argc, char *argv[]) {
wl_list_remove(&server.new_input.link);
wl_list_remove(&server.request_cursor.link);
wl_list_remove(&server.pointer_focus_change.link);
wl_list_remove(&server.request_set_selection.link);
wl_list_remove(&server.new_output.link);

View file

@ -308,6 +308,14 @@ static void drag_handle_touch_motion(struct wlr_seat_touch_grab *grab,
wl_fixed_from_double(point->sx),
wl_fixed_from_double(point->sy));
}
struct wlr_drag_motion_event event = {
.drag = drag,
.time = time,
.sx = point->sx,
.sy = point->sy,
};
wl_signal_emit_mutable(&drag->events.motion, &event);
}
}

View file

@ -10,6 +10,7 @@
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
#include <wlr/util/log.h>
#include "ext-image-capture-source-v1-protocol.h"
#include "render/wlr_renderer.h"
static void source_handle_destroy(struct wl_client *client,
struct wl_resource *source_resource) {
@ -96,6 +97,12 @@ static uint32_t get_swapchain_shm_format(struct wlr_swapchain *swapchain,
return format;
}
static void add_drm_format(struct wlr_drm_format_set *set, const struct wlr_drm_format *fmt) {
for (size_t i = 0; i < fmt->len; i++) {
wlr_drm_format_set_add(set, fmt->format, fmt->modifiers[i]);
}
}
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) {
source->width = swapchain->width;
@ -130,9 +137,21 @@ bool wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(struct wlr_e
wlr_drm_format_set_finish(&source->dmabuf_formats);
source->dmabuf_formats = (struct wlr_drm_format_set){0};
for (size_t i = 0; i < swapchain->format.len; i++) {
wlr_drm_format_set_add(&source->dmabuf_formats,
swapchain->format.format, swapchain->format.modifiers[i]);
add_drm_format(&source->dmabuf_formats, &swapchain->format);
const struct wlr_drm_format_set *render_formats =
wlr_renderer_get_render_formats(renderer);
assert(render_formats != NULL);
// Not all clients support fancy formats. Always ensure we provide
// support for ARGB8888 and XRGB8888 for simple clients.
uint32_t fallback_formats[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888 };
for (size_t i = 0; i < sizeof(fallback_formats) / sizeof(fallback_formats[0]); i++) {
const struct wlr_drm_format *fmt =
wlr_drm_format_set_get(render_formats, fallback_formats[i]);
if (fmt != NULL && swapchain->format.format != fmt->format) {
add_drm_format(&source->dmabuf_formats, fmt);
}
}
}

View file

@ -6,10 +6,6 @@
#define FOREIGN_TOPLEVEL_IMAGE_SOURCE_MANAGER_V1_VERSION 1
struct wlr_ext_foreign_toplevel_image_capture_source_v1 {
struct wlr_ext_image_capture_source_v1 base;
};
static const struct ext_foreign_toplevel_image_capture_source_manager_v1_interface foreign_toplevel_manager_impl;
static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *

View file

@ -283,7 +283,8 @@ static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_ext_image_copy_capture_frame_v1_ready(frame, WL_OUTPUT_TRANSFORM_NORMAL, &now);
wlr_ext_image_copy_capture_frame_v1_ready(frame,
cursor_source->output->transform, &now);
}
static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = {

View file

@ -39,18 +39,20 @@ wlr_files += files(
'buffer/resource.c',
'wlr_alpha_modifier_v1.c',
'wlr_color_management_v1.c',
'wlr_color_representation_v1.c',
'wlr_compositor.c',
'wlr_content_type_v1.c',
'wlr_cursor_shape_v1.c',
'wlr_cursor.c',
'wlr_cursor_shape_v1.c',
'wlr_damage_ring.c',
'wlr_data_control_v1.c',
'wlr_drm.c',
'wlr_export_dmabuf_v1.c',
'wlr_foreign_toplevel_management_v1.c',
'wlr_ext_image_copy_capture_v1.c',
'wlr_ext_foreign_toplevel_list_v1.c',
'wlr_ext_data_control_v1.c',
'wlr_ext_foreign_toplevel_list_v1.c',
'wlr_ext_image_copy_capture_v1.c',
'wlr_fixes.c',
'wlr_foreign_toplevel_management_v1.c',
'wlr_fractional_scale_v1.c',
'wlr_gamma_control_v1.c',
'wlr_idle_inhibit_v1.c',
@ -103,6 +105,7 @@ wlr_files += files(
'wlr_xdg_output_v1.c',
'wlr_xdg_system_bell_v1.c',
'wlr_xdg_toplevel_icon_v1.c',
'wlr_xdg_toplevel_tag_v1.c',
)
if features.get('drm-backend')

View file

@ -288,18 +288,10 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor)
static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) {
struct wlr_output *output = cursor->output;
if (!output->impl->set_cursor ||
output->software_cursor_locks > 0) {
if (!output->impl->set_cursor || output->software_cursor_locks > 0) {
return false;
}
struct wlr_output_cursor *hwcur = output->hardware_cursor;
if (hwcur != NULL && hwcur != cursor) {
return false;
}
output->hardware_cursor = NULL;
struct wlr_texture *texture = cursor->texture;
// If the cursor was hidden or was a software cursor, the hardware
@ -424,12 +416,15 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor,
wl_list_init(&cursor->renderer_destroy.link);
}
if (output_cursor_attempt_hardware(cursor)) {
return true;
if (output->hardware_cursor == NULL || output->hardware_cursor == cursor) {
if (output_cursor_attempt_hardware(cursor)) {
return true;
}
wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name);
output_disable_hardware_cursor(output);
}
wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name);
output_disable_hardware_cursor(output);
output_cursor_damage_whole(cursor);
return true;
}

View file

@ -233,6 +233,24 @@ static void output_apply_state(struct wlr_output *output,
output->transform = state->transform;
}
if (state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) {
if (state->image_description != NULL) {
output->image_description_value = *state->image_description;
output->image_description = &output->image_description_value;
} else {
output->image_description = NULL;
}
}
if (state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) {
wlr_color_transform_unref(output->color_transform);
if (state->color_transform != NULL) {
output->color_transform = wlr_color_transform_ref(state->color_transform);
} else {
output->color_transform = NULL;
}
}
bool geometry_updated = state->committed &
(WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM |
WLR_OUTPUT_STATE_SUBPIXEL);
@ -398,6 +416,7 @@ void wlr_output_finish(struct wlr_output *output) {
wlr_swapchain_destroy(output->cursor_swapchain);
wlr_buffer_unlock(output->cursor_front_buffer);
wlr_color_transform_unref(output->color_transform);
wlr_swapchain_destroy(output->swapchain);
@ -491,6 +510,14 @@ bool output_pending_enabled(struct wlr_output *output,
return output->enabled;
}
const struct wlr_output_image_description *output_pending_image_description(
struct wlr_output *output, const struct wlr_output_state *state) {
if (state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) {
return state->image_description;
}
return output->image_description;
}
/**
* Compare a struct wlr_output_state with the current state of a struct
* wlr_output.
@ -498,8 +525,7 @@ bool output_pending_enabled(struct wlr_output *output,
* Returns a bitfield of the unchanged fields.
*
* Some fields are not checked: damage always changes in-between frames, the
* gamma LUT is too expensive to check, the contents of the buffer might have
* changed, etc.
* contents of the buffer might have changed, etc.
*/
static uint32_t output_compare_state(struct wlr_output *output,
const struct wlr_output_state *state) {
@ -545,6 +571,10 @@ static uint32_t output_compare_state(struct wlr_output *output,
output->subpixel == state->subpixel) {
fields |= WLR_OUTPUT_STATE_SUBPIXEL;
}
if ((state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) &&
output->color_transform == state->color_transform) {
fields |= WLR_OUTPUT_STATE_COLOR_TRANSFORM;
}
return fields;
}
@ -627,29 +657,25 @@ static bool output_basic_test(struct wlr_output *output,
}
}
if (!enabled && state->committed & WLR_OUTPUT_STATE_BUFFER) {
wlr_log(WLR_DEBUG, "Tried to commit a buffer on a disabled output");
return false;
}
if (!enabled && state->committed & WLR_OUTPUT_STATE_MODE) {
wlr_log(WLR_DEBUG, "Tried to modeset a disabled output");
return false;
}
if (!enabled && state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {
wlr_log(WLR_DEBUG, "Tried to enable adaptive sync on a disabled output");
return false;
}
if (!enabled && state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
wlr_log(WLR_DEBUG, "Tried to set format for a disabled output");
return false;
}
if (!enabled && state->committed & WLR_OUTPUT_STATE_SUBPIXEL) {
wlr_log(WLR_DEBUG, "Tried to set the subpixel layout on a disabled output");
return false;
}
if (!enabled && state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) {
wlr_log(WLR_DEBUG, "Tried to set a color transform on a disabled output");
return false;
const struct {
enum wlr_output_state_field field;
const char *name;
} needs_enabled[] = {
{ WLR_OUTPUT_STATE_BUFFER, "buffer" },
{ WLR_OUTPUT_STATE_MODE, "mode" },
{ WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED, "adaptive sync" },
{ WLR_OUTPUT_STATE_RENDER_FORMAT, "render format" },
{ WLR_OUTPUT_STATE_SUBPIXEL, "subpixel" },
{ WLR_OUTPUT_STATE_COLOR_TRANSFORM, "color transform" },
{ WLR_OUTPUT_STATE_IMAGE_DESCRIPTION, "image description" },
};
if (!enabled) {
for (size_t i = 0; i < sizeof(needs_enabled) / sizeof(needs_enabled[0]); i++) {
if (state->committed & needs_enabled[i].field) {
wlr_log(WLR_DEBUG, "Tried to set %s on a disabled output", needs_enabled[i].name);
return false;
}
}
}
if (state->committed & WLR_OUTPUT_STATE_LAYERS) {
@ -669,6 +695,18 @@ static bool output_basic_test(struct wlr_output *output,
return false;
}
if ((state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) &&
state->image_description != NULL) {
if (!(output->supported_primaries & state->image_description->primaries)) {
wlr_log(WLR_DEBUG, "Unsupported image description primaries");
return false;
}
if (!(output->supported_transfer_functions & state->image_description->transfer_function)) {
wlr_log(WLR_DEBUG, "Unsupported image description transfer function");
return false;
}
}
return true;
}
@ -734,7 +772,9 @@ void output_apply_commit(struct wlr_output *output, const struct wlr_output_stat
}
output_apply_state(output, state);
}
void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct wlr_output_event_commit event = {
@ -776,6 +816,7 @@ bool wlr_output_commit_state(struct wlr_output *output,
}
output_apply_commit(output, &pending);
output_send_commit_event(output, &pending);
if (new_back_buffer) {
wlr_buffer_unlock(pending.buffer);

View file

@ -19,6 +19,7 @@ void wlr_output_state_finish(struct wlr_output_state *state) {
pixman_region32_fini(&state->damage);
wlr_drm_syncobj_timeline_unref(state->wait_timeline);
wlr_drm_syncobj_timeline_unref(state->signal_timeline);
free(state->image_description);
}
void wlr_output_state_set_enabled(struct wlr_output_state *state,
@ -122,6 +123,23 @@ void wlr_output_state_set_color_transform(struct wlr_output_state *state,
}
}
bool wlr_output_state_set_image_description(struct wlr_output_state *state,
const struct wlr_output_image_description *image_desc) {
struct wlr_output_image_description *copy = NULL;
if (image_desc != NULL) {
copy = malloc(sizeof(*copy));
if (copy == NULL) {
return false;
}
*copy = *image_desc;
}
state->committed |= WLR_OUTPUT_STATE_IMAGE_DESCRIPTION;
free(state->image_description);
state->image_description = copy;
return true;
}
bool wlr_output_state_copy(struct wlr_output_state *dst,
const struct wlr_output_state *src) {
struct wlr_output_state copy = *src;
@ -129,7 +147,8 @@ bool wlr_output_state_copy(struct wlr_output_state *dst,
WLR_OUTPUT_STATE_DAMAGE |
WLR_OUTPUT_STATE_WAIT_TIMELINE |
WLR_OUTPUT_STATE_SIGNAL_TIMELINE |
WLR_OUTPUT_STATE_COLOR_TRANSFORM);
WLR_OUTPUT_STATE_COLOR_TRANSFORM |
WLR_OUTPUT_STATE_IMAGE_DESCRIPTION);
copy.buffer = NULL;
copy.buffer_src_box = (struct wlr_fbox){0};
copy.buffer_dst_box = (struct wlr_box){0};
@ -137,6 +156,7 @@ bool wlr_output_state_copy(struct wlr_output_state *dst,
copy.wait_timeline = NULL;
copy.signal_timeline = NULL;
copy.color_transform = NULL;
copy.image_description = NULL;
if (src->committed & WLR_OUTPUT_STATE_BUFFER) {
wlr_output_state_set_buffer(&copy, src->buffer);
@ -160,8 +180,17 @@ bool wlr_output_state_copy(struct wlr_output_state *dst,
if (src->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) {
wlr_output_state_set_color_transform(&copy, src->color_transform);
}
if (src->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) {
if (!wlr_output_state_set_image_description(&copy, src->image_description)) {
goto err;
}
}
wlr_output_state_finish(dst);
*dst = copy;
return true;
err:
wlr_output_state_finish(dst);
return false;
}

View file

@ -1,6 +1,7 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_alpha_modifier_v1.h>
#include <wlr/types/wlr_color_management_v1.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
@ -34,16 +35,76 @@ static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *su
return frame_pacing_output;
}
static bool get_tf_preference(enum wlr_color_transfer_function tf) {
switch (tf) {
case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
return 0;
case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
return 1;
case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
return -1;
}
abort(); // unreachable
}
static bool get_primaries_preference(enum wlr_color_named_primaries primaries) {
switch (primaries) {
case WLR_COLOR_NAMED_PRIMARIES_SRGB:
return 0;
case WLR_COLOR_NAMED_PRIMARIES_BT2020:
return 1;
}
abort(); // unreachable
}
static void get_surface_preferred_image_description(struct wlr_surface *surface,
struct wlr_image_description_v1_data *out) {
struct wlr_output_image_description preferred = {
.transfer_function = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22,
.primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB,
};
struct wlr_surface_output *surface_output;
wl_list_for_each(surface_output, &surface->current_outputs, link) {
const struct wlr_output_image_description *img_desc =
surface_output->output->image_description;
if (img_desc == NULL) {
continue;
}
if (get_tf_preference(preferred.transfer_function) < get_tf_preference(img_desc->transfer_function)) {
preferred.transfer_function = img_desc->transfer_function;
}
if (get_primaries_preference(preferred.primaries) < get_primaries_preference(img_desc->primaries)) {
preferred.primaries = img_desc->primaries;
}
}
*out = (struct wlr_image_description_v1_data){
.tf_named = wlr_color_manager_v1_transfer_function_from_wlr(preferred.transfer_function),
.primaries_named = wlr_color_manager_v1_primaries_from_wlr(preferred.primaries),
};
}
static void handle_scene_buffer_outputs_update(
struct wl_listener *listener, void *data) {
struct wlr_scene_surface *surface =
wl_container_of(listener, surface, outputs_update);
struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node);
surface->frame_pacing_output = get_surface_frame_pacing_output(surface->surface);
double scale = get_surface_preferred_buffer_scale(surface->surface);
wlr_fractional_scale_v1_notify_scale(surface->surface, scale);
wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale));
if (scene->color_manager_v1 != NULL) {
struct wlr_image_description_v1_data img_desc = {0};
get_surface_preferred_image_description(surface->surface, &img_desc);
wlr_color_manager_v1_set_surface_preferred_image_description(scene->color_manager_v1,
surface->surface, &img_desc);
}
}
static void handle_scene_buffer_output_enter(
@ -126,8 +187,11 @@ static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buf
return;
}
assert(buffer->n_ignore_locks > 0);
buffer->n_ignore_locks--;
// If the buffer was a single-pixel buffer where we cached its color
// then it won't have been marked as damage-allowed.
if (buffer->n_ignore_locks > 0) {
buffer->n_ignore_locks--;
}
}
static int min(int a, int b) {
@ -186,16 +250,41 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) {
opacity = (float)alpha_modifier_state->multiplier;
}
enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22;
enum wlr_color_named_primaries primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB;
const struct wlr_image_description_v1_data *img_desc =
wlr_surface_get_image_description_v1_data(surface);
if (img_desc != NULL) {
tf = wlr_color_manager_v1_transfer_function_to_wlr(img_desc->tf_named);
primaries = wlr_color_manager_v1_primaries_to_wlr(img_desc->primaries_named);
}
wlr_scene_buffer_set_opaque_region(scene_buffer, &opaque);
wlr_scene_buffer_set_source_box(scene_buffer, &src_box);
wlr_scene_buffer_set_dest_size(scene_buffer, width, height);
wlr_scene_buffer_set_transform(scene_buffer, state->transform);
wlr_scene_buffer_set_opacity(scene_buffer, opacity);
wlr_scene_buffer_set_transfer_function(scene_buffer, tf);
wlr_scene_buffer_set_primaries(scene_buffer, primaries);
scene_buffer_unmark_client_buffer(scene_buffer);
if (surface->buffer) {
client_buffer_mark_next_can_damage(surface->buffer);
// If we've cached the buffer's single-pixel buffer color
// then any in-place updates to the texture wouldn't be
// reflected in rendering. So only allow in-place texture
// updates if it's not a single pixel buffer. Note that we
// can't use the cached scene_buffer->is_single_pixel_buffer
// because that's only set later on.
bool is_single_pixel_buffer = false;
if (surface->buffer->source != NULL) {
struct wlr_single_pixel_buffer_v1 *spb =
wlr_single_pixel_buffer_v1_try_from_buffer(surface->buffer->source);
is_single_pixel_buffer = spb != NULL;
}
if (!is_single_pixel_buffer) {
client_buffer_mark_next_can_damage(surface->buffer);
}
struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state =
wlr_linux_drm_syncobj_v1_get_surface_state(surface);
@ -216,7 +305,8 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) {
&surface->buffer->base, &options);
if (syncobj_surface_state != NULL &&
(surface->current.committed & WLR_SURFACE_STATE_BUFFER)) {
(surface->current.committed & WLR_SURFACE_STATE_BUFFER) &&
surface->buffer->source != NULL) {
wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state,
surface->buffer->source);
}

View file

@ -5,6 +5,7 @@
#include <wlr/render/swapchain.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_color_management_v1.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_gamma_control_v1.h>
@ -14,6 +15,7 @@
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include <wlr/util/transform.h>
#include "render/color.h"
#include "types/wlr_output.h"
#include "types/wlr_scene.h"
#include "util/array.h"
@ -1099,6 +1101,26 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer,
scene_node_update(&scene_buffer->node, NULL);
}
void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer,
enum wlr_color_transfer_function transfer_function) {
if (scene_buffer->transfer_function == transfer_function) {
return;
}
scene_buffer->transfer_function = transfer_function;
scene_node_update(&scene_buffer->node, NULL);
}
void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer,
enum wlr_color_named_primaries primaries) {
if (scene_buffer->primaries == primaries) {
return;
}
scene_buffer->primaries = primaries;
scene_node_update(&scene_buffer->node, NULL);
}
static struct wlr_texture *scene_buffer_get_texture(
struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) {
if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) {
@ -1435,6 +1457,11 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
wlr_output_transform_invert(scene_buffer->transform);
transform = wlr_output_transform_compose(transform, data->transform);
struct wlr_color_primaries primaries = {0};
if (scene_buffer->primaries != 0) {
wlr_color_primaries_from_named(&primaries, scene_buffer->primaries);
}
wlr_render_pass_add_texture(data->render_pass, &(struct wlr_render_texture_options) {
.texture = texture,
.src_box = scene_buffer->src_box,
@ -1446,6 +1473,8 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
.blend_mode = !data->output->scene->calculate_visibility ||
!pixman_region32_empty(&opaque) ?
WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE,
.transfer_function = scene_buffer->transfer_function,
.primaries = scene_buffer->primaries != 0 ? &primaries : NULL,
.wait_timeline = scene_buffer->wait_timeline,
.wait_point = scene_buffer->wait_point,
});
@ -1501,6 +1530,8 @@ static void scene_handle_gamma_control_manager_v1_set_gamma(struct wl_listener *
output->gamma_lut_changed = true;
output->gamma_lut = event->control;
wlr_color_transform_unref(output->gamma_lut_color_transform);
output->gamma_lut_color_transform = wlr_gamma_control_v1_get_color_transform(event->control);
wlr_output_schedule_frame(output->output);
}
@ -1518,6 +1549,8 @@ static void scene_handle_gamma_control_manager_v1_destroy(struct wl_listener *li
wl_list_for_each(output, &scene->outputs, link) {
output->gamma_lut_changed = false;
output->gamma_lut = NULL;
wlr_color_transform_unref(output->gamma_lut_color_transform);
output->gamma_lut_color_transform = NULL;
}
}
@ -1534,6 +1567,21 @@ void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene,
wl_signal_add(&gamma_control->events.set_gamma, &scene->gamma_control_manager_v1_set_gamma);
}
static void scene_handle_color_manager_v1_destroy(struct wl_listener *listener, void *data) {
struct wlr_scene *scene = wl_container_of(listener, scene, color_manager_v1_destroy);
wl_list_remove(&scene->color_manager_v1_destroy.link);
wl_list_init(&scene->color_manager_v1_destroy.link);
scene->color_manager_v1 = NULL;
}
void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager) {
assert(scene->color_manager_v1 == NULL);
scene->color_manager_v1 = manager;
scene->color_manager_v1_destroy.notify = scene_handle_color_manager_v1_destroy;
wl_signal_add(&manager->events.destroy, &scene->color_manager_v1_destroy);
}
static void scene_output_handle_destroy(struct wlr_addon *addon) {
struct wlr_scene_output *scene_output =
wl_container_of(addon, scene_output, addon);
@ -1722,6 +1770,10 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wl_list_remove(&scene_output->output_damage.link);
wl_list_remove(&scene_output->output_needs_frame.link);
wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
wlr_color_transform_unref(scene_output->gamma_lut_color_transform);
wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform);
wlr_color_transform_unref(scene_output->prev_supplied_color_transform);
wlr_color_transform_unref(scene_output->prev_combined_color_transform);
wl_array_release(&scene_output->render_list);
free(scene_output);
}
@ -1881,6 +1933,27 @@ static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene,
wlr_linux_dmabuf_feedback_v1_finish(&feedback);
}
static bool color_management_is_scanout_allowed(const struct wlr_output_image_description *img_desc,
const struct wlr_scene_buffer *buffer) {
// Disallow scanout if the output has colorimetry information but buffer
// doesn't; allow it only if the output also lacks it.
if (buffer->transfer_function == 0 && buffer->primaries == 0) {
return img_desc == NULL;
}
// If the output has colorimetry information, the buffer must match it for
// direct scanout to be allowed.
if (img_desc != NULL) {
return img_desc->transfer_function == buffer->transfer_function &&
img_desc->primaries == buffer->primaries;
}
// If the output doesn't have colorimetry image description set, we can only
// scan out buffers with default colorimetry (gamma2.2 transfer and sRGB
// primaries) used in wlroots.
return buffer->transfer_function == WLR_COLOR_TRANSFER_FUNCTION_GAMMA22 &&
buffer->primaries == WLR_COLOR_NAMED_PRIMARIES_SRGB;
}
enum scene_direct_scanout_result {
// This scene node is not a candidate for scanout
SCANOUT_INELIGIBLE,
@ -1937,6 +2010,11 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout(
return SCANOUT_INELIGIBLE;
}
const struct wlr_output_image_description *img_desc = output_pending_image_description(scene_output->output, state);
if (!color_management_is_scanout_allowed(img_desc, buffer)) {
return SCANOUT_INELIGIBLE;
}
// We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled
// on a frame-by-frame basis, we wait for a few frames to send the new format recommendations.
// Maybe we should only send feedback in this case if tests fail.
@ -2034,16 +2112,15 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp
return;
}
if (!wlr_gamma_control_v1_apply(scene_output->gamma_lut, &gamma_pending)) {
wlr_output_state_finish(&gamma_pending);
return;
}
wlr_output_state_set_color_transform(&gamma_pending, scene_output->gamma_lut_color_transform);
scene_output->gamma_lut_changed = false;
if (!wlr_output_test_state(scene_output->output, &gamma_pending)) {
wlr_gamma_control_v1_send_failed_and_destroy(scene_output->gamma_lut);
scene_output->gamma_lut = NULL;
wlr_color_transform_unref(scene_output->gamma_lut_color_transform);
scene_output->gamma_lut_color_transform = NULL;
wlr_output_state_finish(&gamma_pending);
return;
}
@ -2052,6 +2129,41 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp
wlr_output_state_finish(&gamma_pending);
}
static struct wlr_color_transform *scene_output_combine_color_transforms(
struct wlr_scene_output *scene_output, struct wlr_color_transform *supplied) {
struct wlr_color_transform *gamma_lut = scene_output->gamma_lut_color_transform;
assert(gamma_lut != NULL);
if (gamma_lut == scene_output->prev_gamma_lut_color_transform &&
supplied == scene_output->prev_supplied_color_transform) {
return wlr_color_transform_ref(scene_output->prev_combined_color_transform);
}
struct wlr_color_transform *combined;
if (supplied == NULL) {
combined = wlr_color_transform_ref(gamma_lut);
} else {
struct wlr_color_transform *transforms[] = {
gamma_lut,
supplied,
};
size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]);
combined = wlr_color_transform_init_pipeline(transforms, transforms_len);
if (combined == NULL) {
return NULL;
}
}
wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform);
scene_output->prev_gamma_lut_color_transform = wlr_color_transform_ref(gamma_lut);
wlr_color_transform_unref(scene_output->prev_supplied_color_transform);
scene_output->prev_supplied_color_transform = supplied ? wlr_color_transform_ref(supplied) : NULL;
wlr_color_transform_unref(scene_output->prev_combined_color_transform);
scene_output->prev_combined_color_transform = wlr_color_transform_ref(combined);
return combined;
}
bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) {
struct wlr_scene_output_state_options default_options = {0};
@ -2075,6 +2187,16 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
enum wlr_scene_debug_damage_option debug_damage =
scene_output->scene->debug_damage_option;
bool render_gamma_lut = false;
if (wlr_output_get_gamma_size(output) == 0 && output->renderer->features.output_color_transform) {
if (scene_output->gamma_lut_color_transform != scene_output->prev_gamma_lut_color_transform) {
scene_output_damage_whole(scene_output);
}
if (scene_output->gamma_lut_color_transform != NULL) {
render_gamma_lut = true;
}
}
struct render_data render_data = {
.transform = output->transform,
.scale = output->scale,
@ -2175,7 +2297,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
// - There are no color transforms that need to be applied
// - Damage highlight debugging is not enabled
enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE;
if (options->color_transform == NULL && list_len == 1
if (options->color_transform == NULL && !render_gamma_lut && list_len == 1
&& debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data);
}
@ -2235,14 +2357,41 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
timer->pre_render_duration = timespec_to_nsec(&duration);
}
struct wlr_color_transform *color_transform = NULL;
const struct wlr_color_primaries *primaries = NULL;
struct wlr_color_primaries primaries_value;
const struct wlr_output_image_description *img_desc = output_pending_image_description(output, state);
if (img_desc != NULL) {
color_transform = wlr_color_transform_init_linear_to_inverse_eotf(img_desc->transfer_function);
wlr_color_primaries_from_named(&primaries_value, img_desc->primaries);
primaries = &primaries_value;
}
if (options->color_transform != NULL) {
assert(color_transform == NULL);
color_transform = wlr_color_transform_ref(options->color_transform);
}
if (render_gamma_lut) {
struct wlr_color_transform *combined =
scene_output_combine_color_transforms(scene_output, color_transform);
wlr_color_transform_unref(color_transform);
if (combined == NULL) {
wlr_buffer_unlock(buffer);
return false;
}
color_transform = combined;
}
scene_output->in_point++;
struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer,
&(struct wlr_buffer_pass_options){
.timer = timer ? timer->render_timer : NULL,
.color_transform = options->color_transform,
.color_transform = color_transform,
.primaries = primaries,
.signal_timeline = scene_output->in_timeline,
.signal_point = scene_output->in_point,
});
wlr_color_transform_unref(color_transform);
if (render_pass == NULL) {
wlr_buffer_unlock(buffer);
return false;
@ -2355,7 +2504,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
scene_output->in_point);
}
scene_output_state_attempt_gamma(scene_output, state);
if (!render_gamma_lut) {
scene_output_state_attempt_gamma(scene_output, state);
}
return true;
}

View file

@ -4,6 +4,7 @@
#include <time.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/util/log.h>
#include "types/wlr_seat.h"
@ -416,6 +417,11 @@ void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat,
grab->seat = wlr_seat;
wlr_seat->pointer_state.grab = grab;
// Preserve drag grab priority
if (wlr_seat->drag && grab != &wlr_seat->drag->pointer_grab) {
return;
}
wl_signal_emit_mutable(&wlr_seat->events.pointer_grab_begin, grab);
}

View file

@ -1,12 +1,15 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/types/wlr_color_management_v1.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/addon.h>
#include "color-management-v1-protocol.h"
#include "render/color.h"
#include "util/mem.h"
#define COLOR_MANAGEMENT_V1_VERSION 1
@ -62,30 +65,6 @@ static void resource_handle_destroy(struct wl_client *client, struct wl_resource
wl_resource_destroy(resource);
}
static enum wlr_color_named_primaries named_primaries_to_wlr(
enum wp_color_manager_v1_primaries primaries) {
switch (primaries) {
case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB:
return WLR_COLOR_NAMED_PRIMARIES_SRGB;
case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020:
return WLR_COLOR_NAMED_PRIMARIES_BT2020;
default:
abort();
}
}
static enum wlr_color_transfer_function transfer_function_to_wlr(
enum wp_color_manager_v1_transfer_function tf) {
switch (tf) {
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB:
return WLR_COLOR_TRANSFER_FUNCTION_SRGB;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ:
return WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ;
default:
abort();
}
}
static int32_t encode_cie1931_coord(float value) {
return round(value * 1000 * 1000);
}
@ -127,10 +106,12 @@ static void image_desc_handle_get_information(struct wl_client *client,
}
struct wlr_color_primaries primaries;
wlr_color_primaries_from_named(&primaries, named_primaries_to_wlr(image_desc->data.primaries_named));
wlr_color_primaries_from_named(&primaries,
wlr_color_manager_v1_primaries_to_wlr(image_desc->data.primaries_named));
struct wlr_color_luminances luminances;
wlr_color_transfer_function_get_default_luminance(transfer_function_to_wlr(image_desc->data.tf_named), &luminances);
wlr_color_transfer_function_get_default_luminance(
wlr_color_manager_v1_transfer_function_to_wlr(image_desc->data.tf_named), &luminances);
wp_image_description_info_v1_send_primaries_named(resource, image_desc->data.primaries_named);
wp_image_description_info_v1_send_primaries(resource,
@ -231,9 +212,14 @@ static void cm_output_handle_get_image_description(struct wl_client *client,
}
struct wlr_image_description_v1_data data = {
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
};
const struct wlr_output_image_description *image_desc = cm_output->output->image_description;
if (image_desc != NULL) {
data.tf_named = wlr_color_manager_v1_transfer_function_from_wlr(image_desc->transfer_function);
data.primaries_named = wlr_color_manager_v1_primaries_from_wlr(image_desc->primaries);
}
image_desc_create_ready(cm_output->manager, cm_output_resource, id, &data, true);
}
@ -306,6 +292,13 @@ static void cm_surface_handle_set_image_description(struct wl_client *client,
struct wlr_image_description_v1 *image_desc = image_desc_from_resource(image_desc_resource);
if (image_desc == NULL) {
wl_resource_post_error(cm_surface_resource,
WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION,
"Image description to be set is invalid");
return;
}
bool found = false;
for (size_t i = 0; i < cm_surface->manager->render_intents_len; i++) {
if (cm_surface->manager->render_intents[i] == render_intent) {
@ -673,29 +666,35 @@ static void manager_handle_get_output(struct wl_client *client,
struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource);
struct wlr_output *output = wlr_output_from_resource(output_resource);
uint32_t version = wl_resource_get_version(manager_resource);
struct wl_resource *cm_output_resource = wl_resource_create(client,
&wp_color_management_output_v1_interface, version, id);
if (!cm_output_resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(cm_output_resource, &cm_output_impl,
NULL, cm_output_handle_resource_destroy);
if (output == NULL) {
return; // leave the wp_color_management_output_v1 resource inert
}
struct wlr_color_management_output_v1 *cm_output = calloc(1, sizeof(*cm_output));
if (cm_output == NULL) {
wl_client_post_no_memory(client);
return;
}
cm_output->resource = cm_output_resource;
cm_output->manager = manager;
uint32_t version = wl_resource_get_version(manager_resource);
cm_output->resource = wl_resource_create(client,
&wp_color_management_output_v1_interface, version, id);
if (!cm_output->resource) {
wl_client_post_no_memory(client);
free(cm_output);
return;
}
wl_resource_set_implementation(cm_output->resource, &cm_output_impl,
cm_output, cm_output_handle_resource_destroy);
cm_output->output = output;
cm_output->output_destroy.notify = cm_output_handle_output_destroy;
wl_signal_add(&output->events.destroy, &cm_output->output_destroy);
wl_list_insert(&manager->outputs, &cm_output->link);
wl_resource_set_user_data(cm_output->resource, cm_output);
}
static struct wlr_color_management_surface_v1 *cm_surface_from_surface(struct wlr_surface *surface) {
@ -778,7 +777,7 @@ static void manager_handle_get_surface_feedback(struct wl_client *client,
surface_feedback->surface = surface;
surface_feedback->data = (struct wlr_image_description_v1_data){
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
};
@ -888,6 +887,8 @@ static void manager_bind(struct wl_client *client, void *data,
static void manager_handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_color_manager_v1 *manager = wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, NULL);
assert(wl_list_empty(&manager->events.destroy.listener_list));
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager->render_intents);
@ -896,17 +897,6 @@ static void manager_handle_display_destroy(struct wl_listener *listener, void *d
free(manager);
}
static bool memdup(void *out, const void *src, size_t size) {
void *dst = malloc(size);
if (dst == NULL) {
return false;
}
memcpy(dst, src, size);
void **dst_ptr = out;
*dst_ptr = dst;
return true;
}
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) {
assert(version <= COLOR_MANAGEMENT_V1_VERSION);
@ -946,6 +936,7 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp
manager->transfer_functions_len = options->transfer_functions_len;
manager->primaries_len = options->primaries_len;
wl_signal_init(&manager->events.destroy);
wl_list_init(&manager->outputs);
wl_list_init(&manager->surface_feedbacks);
@ -992,3 +983,61 @@ void wlr_color_manager_v1_set_surface_preferred_image_description(
}
}
}
enum wlr_color_transfer_function
wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_function tf) {
switch (tf) {
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB:
return WLR_COLOR_TRANSFER_FUNCTION_SRGB;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ:
return WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR:
return WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22:
return WLR_COLOR_TRANSFER_FUNCTION_GAMMA22;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886:
return WLR_COLOR_TRANSFER_FUNCTION_BT1886;
default:
abort();
}
}
enum wp_color_manager_v1_transfer_function
wlr_color_manager_v1_transfer_function_from_wlr(enum wlr_color_transfer_function tf) {
switch (tf) {
case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB;
case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ;
case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886;
}
abort();
}
enum wlr_color_named_primaries
wlr_color_manager_v1_primaries_to_wlr(enum wp_color_manager_v1_primaries primaries) {
switch (primaries) {
case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB:
return WLR_COLOR_NAMED_PRIMARIES_SRGB;
case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020:
return WLR_COLOR_NAMED_PRIMARIES_BT2020;
default:
abort();
}
}
enum wp_color_manager_v1_primaries
wlr_color_manager_v1_primaries_from_wlr(enum wlr_color_named_primaries primaries) {
switch (primaries) {
case WLR_COLOR_NAMED_PRIMARIES_SRGB:
return WP_COLOR_MANAGER_V1_PRIMARIES_SRGB;
case WLR_COLOR_NAMED_PRIMARIES_BT2020:
return WP_COLOR_MANAGER_V1_PRIMARIES_BT2020;
}
abort();
}

View file

@ -0,0 +1,410 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_color_representation_v1.h>
#include <wlr/util/addon.h>
#include <wlr/util/log.h>
#include "color-representation-v1-protocol.h"
#include "util/mem.h"
#define WP_COLOR_REPRESENTATION_VERSION 1
enum wlr_alpha_mode wlr_color_representation_v1_alpha_mode_to_wlr(
enum wp_color_representation_surface_v1_alpha_mode wp_val) {
switch (wp_val) {
case WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL:
return WLR_COLOR_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL;
case WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_PREMULTIPLIED_OPTICAL:
return WLR_COLOR_ALPHA_MODE_PREMULTIPLIED_OPTICAL;
case WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_STRAIGHT:
return WLR_COLOR_ALPHA_MODE_STRAIGHT;
}
abort(); // unreachable
}
enum wlr_color_encoding wlr_color_representation_v1_color_encoding_to_wlr(
enum wp_color_representation_surface_v1_coefficients wp_val) {
switch (wp_val) {
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_IDENTITY:
return WLR_COLOR_ENCODING_IDENTITY;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT709:
return WLR_COLOR_ENCODING_BT709;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_FCC:
return WLR_COLOR_ENCODING_FCC;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT601:
return WLR_COLOR_ENCODING_BT601;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_SMPTE240:
return WLR_COLOR_ENCODING_SMPTE240;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT2020:
return WLR_COLOR_ENCODING_BT2020;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT2020_CL:
return WLR_COLOR_ENCODING_BT2020_CL;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_ICTCP:
return WLR_COLOR_ENCODING_ICTCP;
}
abort(); // unreachable
}
enum wlr_color_range wlr_color_representation_v1_color_range_to_wlr(
enum wp_color_representation_surface_v1_range wp_val) {
switch (wp_val) {
case WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_LIMITED:
return WLR_COLOR_RANGE_LIMITED;
case WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_FULL:
return WLR_COLOR_RANGE_FULL;
}
abort(); // unreachable
}
enum wlr_color_chroma_location wlr_color_representation_v1_chroma_location_to_wlr(
enum wp_color_representation_surface_v1_chroma_location wp_val) {
switch (wp_val) {
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_0:
return WLR_COLOR_CHROMA_LOCATION_TYPE0;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_1:
return WLR_COLOR_CHROMA_LOCATION_TYPE1;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_2:
return WLR_COLOR_CHROMA_LOCATION_TYPE2;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_3:
return WLR_COLOR_CHROMA_LOCATION_TYPE3;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_4:
return WLR_COLOR_CHROMA_LOCATION_TYPE4;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_5:
return WLR_COLOR_CHROMA_LOCATION_TYPE5;
}
abort(); // unreachable
}
struct wlr_color_representation_v1 {
struct wl_resource *resource;
struct wlr_surface *surface;
struct wlr_color_representation_manager_v1 *manager;
// Associate the wlr_color_representation_v1 with a wlr_surface
struct wlr_addon addon;
struct wlr_surface_synced synced;
struct wlr_color_representation_v1_surface_state pending, current;
};
static const struct wp_color_representation_surface_v1_interface color_repr_impl;
static struct wlr_color_representation_v1 *color_repr_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&wp_color_representation_surface_v1_interface,
&color_repr_impl));
return wl_resource_get_user_data(resource);
}
static void color_repr_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
// Actual destroying is done by the resource-destroy handler
wl_resource_destroy(resource);
}
static void color_repr_handle_set_alpha_mode(struct wl_client *client,
struct wl_resource *resource, uint32_t alpha_mode) {
struct wlr_color_representation_v1 *color_repr =
color_repr_from_resource(resource);
if (color_repr == NULL) {
wl_resource_post_error(resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT,
"Associated surface has been destroyed, object is inert");
return;
}
bool found = false;
for (size_t i = 0; i < color_repr->manager->supported_alpha_modes_len; i++) {
if (color_repr->manager->supported_alpha_modes[i] == alpha_mode) {
found = true;
break;
}
}
if (!found) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_ALPHA_MODE,
"Unsupported alpha mode");
return;
}
color_repr->pending.alpha_mode = alpha_mode;
}
static void color_repr_handle_set_coefficients_and_range(struct wl_client *client,
struct wl_resource *resource, uint32_t coefficients,
uint32_t range) {
struct wlr_color_representation_v1 *color_repr =
color_repr_from_resource(resource);
if (color_repr == NULL) {
wl_resource_post_error(resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT,
"Associated surface has been destroyed, object is inert");
return;
}
bool found = false;
for (size_t i = 0; i < color_repr->manager->supported_coeffs_and_ranges_len; i++) {
struct wlr_color_representation_v1_coeffs_and_range *supported =
&color_repr->manager->supported_coeffs_and_ranges[i];
if (supported->coeffs == coefficients && supported->range == range) {
found = true;
break;
}
}
if (!found) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_COEFFICIENTS,
"Unsupported coefficients/range pair");
return;
}
color_repr->pending.coefficients = coefficients;
color_repr->pending.range = range;
}
static void color_repr_handle_set_chroma_location(struct wl_client *client,
struct wl_resource *resource, uint32_t chroma_location) {
struct wlr_color_representation_v1 *color_repr =
color_repr_from_resource(resource);
if (color_repr == NULL) {
wl_resource_post_error(resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT,
"Associated surface has been destroyed, object is inert");
return;
}
uint32_t version = wl_resource_get_version(resource);
if (!wp_color_representation_surface_v1_chroma_location_is_valid(
version, chroma_location)) {
wlr_log(WLR_ERROR, "Client sent chroma location which isn't a valid enum value");
// TODO: Post actual error once
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/429
// is merged and wlroots depends on a new enough wayland-protocols.
wl_client_post_implementation_error(resource->client,
"Chroma location is not a valid enum value");
return;
}
// In this protocol there's no concept of supported chroma locations
// from a client point-of-view. The compositor should just ignore any
// chroma locations it doesn't know what to do with.
color_repr->pending.chroma_location = chroma_location;
}
static const struct wp_color_representation_surface_v1_interface color_repr_impl = {
.destroy = color_repr_handle_destroy,
.set_alpha_mode = color_repr_handle_set_alpha_mode,
.set_coefficients_and_range = color_repr_handle_set_coefficients_and_range,
.set_chroma_location = color_repr_handle_set_chroma_location,
};
static void color_repr_destroy(struct wlr_color_representation_v1 *color_repr) {
if (color_repr == NULL) {
return;
}
wlr_surface_synced_finish(&color_repr->synced);
wlr_addon_finish(&color_repr->addon);
wl_resource_set_user_data(color_repr->resource, NULL);
free(color_repr);
}
static void color_repr_addon_destroy(struct wlr_addon *addon) {
struct wlr_color_representation_v1 *color_repr =
wl_container_of(addon, color_repr, addon);
color_repr_destroy(color_repr);
}
static const struct wlr_addon_interface surface_addon_impl = {
.name = "wlr_color_representation_v1",
.destroy = color_repr_addon_destroy,
};
static void color_repr_handle_resource_destroy(struct wl_resource *resource) {
struct wlr_color_representation_v1 *color_repr =
color_repr_from_resource(resource);
color_repr_destroy(color_repr);
}
static void color_repr_manager_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
// Actual destroying is done by the resource-destroy handler
wl_resource_destroy(resource);
}
static const struct wlr_surface_synced_impl surface_synced_impl = {
.state_size = sizeof(struct wlr_color_representation_v1_surface_state),
};
static struct wlr_color_representation_v1 *color_repr_from_surface(
struct wlr_surface *surface) {
struct wlr_addon *addon = wlr_addon_find(&surface->addons, NULL, &surface_addon_impl);
if (addon == NULL) {
return NULL;
}
struct wlr_color_representation_v1 *color_repr = wl_container_of(addon, color_repr, addon);
return color_repr;
}
static const struct wp_color_representation_manager_v1_interface color_repr_manager_impl;
static struct wlr_color_representation_manager_v1 *manager_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&wp_color_representation_manager_v1_interface,
&color_repr_manager_impl));
return wl_resource_get_user_data(resource);
}
static void color_repr_manager_handle_get_surface(struct wl_client *client,
struct wl_resource *manager_resource,
uint32_t color_repr_id,
struct wl_resource *surface_resource) {
struct wlr_surface *surface = wlr_surface_from_resource(surface_resource);
// Check if there's already a color-representation attached to
// this surface
if (color_repr_from_surface(surface) != NULL) {
wl_resource_post_error(manager_resource,
WP_COLOR_REPRESENTATION_MANAGER_V1_ERROR_SURFACE_EXISTS,
"wp_color_representation_surface_v1 already exists for this surface");
return;
}
struct wlr_color_representation_v1 *color_repr = calloc(1, sizeof(*color_repr));
if (!color_repr) {
wl_resource_post_no_memory(manager_resource);
return;
}
color_repr->manager = manager_from_resource(manager_resource);
if (!wlr_surface_synced_init(&color_repr->synced, surface,
&surface_synced_impl, &color_repr->pending, &color_repr->current)) {
free(color_repr);
wl_resource_post_no_memory(manager_resource);
return;
}
uint32_t version = wl_resource_get_version(manager_resource);
color_repr->resource = wl_resource_create(client,
&wp_color_representation_surface_v1_interface, version, color_repr_id);
if (color_repr->resource == NULL) {
wlr_surface_synced_finish(&color_repr->synced);
free(color_repr);
wl_resource_post_no_memory(manager_resource);
return;
}
wl_resource_set_implementation(color_repr->resource,
&color_repr_impl, color_repr, color_repr_handle_resource_destroy);
wlr_addon_init(&color_repr->addon, &surface->addons, NULL, &surface_addon_impl);
}
static const struct wp_color_representation_manager_v1_interface color_repr_manager_impl = {
.destroy = color_repr_manager_handle_destroy,
.get_surface = color_repr_manager_handle_get_surface,
};
static void send_supported(struct wlr_color_representation_manager_v1 *manager,
struct wl_resource *resource) {
for (size_t i = 0; i < manager->supported_alpha_modes_len; i++) {
wp_color_representation_manager_v1_send_supported_alpha_mode(
resource, manager->supported_alpha_modes[i]);
}
for (size_t i = 0; i < manager->supported_coeffs_and_ranges_len; i++) {
struct wlr_color_representation_v1_coeffs_and_range *supported =
&manager->supported_coeffs_and_ranges[i];
wp_color_representation_manager_v1_send_supported_coefficients_and_ranges(
resource, supported->coeffs, supported->range);
}
// Note that there is no event for supported chroma locations in the
// v1 protocol.
wp_color_representation_manager_v1_send_done(resource);
}
static void manager_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct wlr_color_representation_manager_v1 *manager = data;
struct wl_resource *resource = wl_resource_create(client,
&wp_color_representation_manager_v1_interface,
version, id);
if (resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &color_repr_manager_impl, manager, NULL);
send_supported(manager, resource);
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_color_representation_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, NULL);
assert(wl_list_empty(&manager->events.destroy.listener_list));
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
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) {
assert(version <= WP_COLOR_REPRESENTATION_VERSION);
struct wlr_color_representation_manager_v1 *manager = calloc(1, sizeof(*manager));
if (manager == NULL) {
return NULL;
}
bool ok = true;
ok &= memdup(&manager->supported_alpha_modes,
options->supported_alpha_modes,
sizeof(options->supported_alpha_modes[0]) * options->supported_alpha_modes_len);
manager->supported_alpha_modes_len = options->supported_alpha_modes_len;
ok &= memdup(&manager->supported_coeffs_and_ranges,
options->supported_coeffs_and_ranges,
sizeof(options->supported_coeffs_and_ranges[0]) * options->supported_coeffs_and_ranges_len);
manager->supported_coeffs_and_ranges_len = options->supported_coeffs_and_ranges_len;
if (!ok) {
goto err_options;
}
manager->global = wl_global_create(display,
&wp_color_representation_manager_v1_interface,
version, manager, manager_bind);
if (manager->global == NULL) {
goto err_options;
}
wl_signal_init(&manager->events.destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
return manager;
err_options:
free(manager->supported_alpha_modes);
free(manager->supported_coeffs_and_ranges);
free(manager);
return NULL;
}
const struct wlr_color_representation_v1_surface_state *wlr_color_representation_v1_get_surface_state(
struct wlr_surface *surface) {
struct wlr_color_representation_v1 *color_repr = color_repr_from_surface(surface);
if (color_repr == NULL) {
return NULL;
}
return &color_repr->current;
}

View file

@ -882,11 +882,7 @@ void wlr_surface_reject_pending(struct wlr_surface *surface, struct wl_resource
va_list args;
va_start(args, msg);
// XXX: libwayland could expose wl_resource_post_error_vargs() instead
char buffer[128]; // Matches the size of the buffer used in libwayland
vsnprintf(buffer, sizeof(buffer), msg, args);
wl_resource_post_error(resource, code, "%s", buffer);
wl_resource_post_error_vargs(resource, code, msg, args);
surface->pending_rejected = true;
va_end(args);

View file

@ -3,6 +3,8 @@
#include <wlr/types/wlr_content_type_v1.h>
#include <wlr/types/wlr_compositor.h>
#include "content-type-v1-protocol.h"
#define CONTENT_TYPE_VERSION 1
struct wlr_content_type_v1_surface {

View file

@ -530,10 +530,6 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_
struct wlr_cursor *cur = output_cursor->cursor;
struct wlr_output *output = output_cursor->output_cursor->output;
if (!output->enabled) {
return;
}
cursor_output_cursor_reset_image(output_cursor);
if (cur->state->buffer != NULL) {
@ -589,10 +585,11 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_
&src_box, dst_width, dst_height, surface->current.transform,
hotspot_x, hotspot_y, wait_timeline, wait_point);
if (syncobj_surface_state != NULL && surface->buffer != NULL &&
if (syncobj_surface_state != NULL &&
surface->buffer != NULL && surface->buffer->source != NULL &&
(surface->current.committed & WLR_SURFACE_STATE_BUFFER)) {
wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state,
&surface->buffer->base);
surface->buffer->source);
}
if (output_cursor->output_cursor->visible) {

View file

@ -5,6 +5,8 @@
#include <wlr/types/wlr_cursor_shape_v1.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_tablet_tool.h>
#include "cursor-shape-v1-protocol.h"
#include "types/wlr_tablet_v2.h"
#define CURSOR_SHAPE_MANAGER_V1_VERSION 2

View file

@ -68,10 +68,6 @@ static void drm_lease_connector_v1_destroy(
wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name);
if (connector->active_lease) {
wlr_drm_lease_terminate(connector->active_lease->drm_lease);
}
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &connector->resources) {
wp_drm_lease_connector_v1_send_withdrawn(resource);
@ -140,14 +136,9 @@ static void lease_handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&lease->destroy.link);
for (size_t i = 0; i < lease->n_connectors; ++i) {
lease->connectors[i]->active_lease = NULL;
}
wl_list_remove(&lease->link);
wl_resource_set_user_data(lease->resource, NULL);
free(lease->connectors);
free(lease);
}
@ -180,20 +171,6 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant(
return NULL;
}
lease->connectors = calloc(request->n_connectors, sizeof(*lease->connectors));
if (!lease->connectors) {
wlr_log(WLR_ERROR, "Failed to allocate lease connectors list");
close(fd);
wp_drm_lease_v1_send_finished(lease->resource);
free(lease);
return NULL;
}
lease->n_connectors = request->n_connectors;
for (size_t i = 0; i < request->n_connectors; ++i) {
lease->connectors[i] = request->connectors[i];
lease->connectors[i]->active_lease = lease;
}
lease->destroy.notify = lease_handle_destroy;
wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy);
@ -338,16 +315,6 @@ static void drm_lease_request_v1_handle_submit(
return;
}
for (size_t i = 0; i < request->n_connectors; ++i) {
struct wlr_drm_lease_connector_v1 *conn = request->connectors[i];
if (conn->active_lease) {
wlr_log(WLR_ERROR, "Failed to create lease, connector %s has "
"already been leased", conn->output->name);
wp_drm_lease_v1_send_finished(lease_resource);
return;
}
}
request->lease_resource = lease_resource;
wl_signal_emit_mutable(&request->device->manager->events.request,
@ -440,10 +407,6 @@ static struct wp_drm_lease_connector_v1_interface lease_connector_impl = {
static void drm_lease_connector_v1_send_to_client(
struct wlr_drm_lease_connector_v1 *connector,
struct wl_resource *resource) {
if (connector->active_lease) {
return;
}
struct wl_client *client = wl_resource_get_client(resource);
uint32_t version = wl_resource_get_version(resource);
@ -490,10 +453,12 @@ static void lease_device_bind(struct wl_client *wl_client, void *data,
if (!device) {
wlr_log(WLR_DEBUG, "Failed to bind lease device, "
"the wlr_drm_lease_device_v1 has been destroyed");
wl_list_init(wl_resource_get_link(device_resource));
return;
}
wl_resource_set_user_data(device_resource, device);
wl_list_insert(&device->resources, wl_resource_get_link(device_resource));
int fd = wlr_drm_backend_get_non_master_fd(device->backend);
if (fd < 0) {
@ -505,8 +470,6 @@ static void lease_device_bind(struct wl_client *wl_client, void *data,
wp_drm_lease_device_v1_send_drm_fd(device_resource, fd);
close(fd);
wl_list_insert(&device->resources, wl_resource_get_link(device_resource));
struct wlr_drm_lease_connector_v1 *connector;
wl_list_for_each(connector, &device->connectors, link) {
drm_lease_connector_v1_send_to_client(connector, device_resource);

View file

@ -6,6 +6,7 @@
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/render/wlr_renderer.h>
#include "ext-image-copy-capture-v1-protocol.h"
#include "render/pixel_format.h"
#define IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION 1

65
types/wlr_fixes.c Normal file
View file

@ -0,0 +1,65 @@
#include <assert.h>
#include <wayland-server-protocol.h>
#include <stdlib.h>
#include <wlr/types/wlr_fixes.h>
#define FIXES_VERSION 1
static void fixes_destroy(struct wl_client *client, struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void fixes_destroy_registry(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *registry) {
wl_resource_destroy(registry);
}
static const struct wl_fixes_interface fixes_impl = {
.destroy = fixes_destroy,
.destroy_registry = fixes_destroy_registry,
};
static void fixes_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) {
struct wlr_fixes *fixes = data;
struct wl_resource *resource = wl_resource_create(wl_client, &wl_fixes_interface, version, id);
if (resource == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
wl_resource_set_implementation(resource, &fixes_impl, fixes, NULL);
}
static void fixes_handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_fixes *fixes = wl_container_of(listener, fixes, display_destroy);
wl_signal_emit_mutable(&fixes->events.destroy, NULL);
assert(wl_list_empty(&fixes->events.destroy.listener_list));
wl_list_remove(&fixes->display_destroy.link);
wl_global_destroy(fixes->global);
free(fixes);
}
struct wlr_fixes *wlr_fixes_create(struct wl_display *display, uint32_t version) {
assert(version <= FIXES_VERSION);
struct wlr_fixes *fixes = calloc(1, sizeof(*fixes));
if (fixes == NULL) {
return NULL;
}
fixes->global = wl_global_create(display, &wl_fixes_interface, version, fixes, fixes_bind);
if (fixes->global == NULL) {
free(fixes);
return NULL;
}
wl_signal_init(&fixes->events.destroy);
fixes->display_destroy.notify = fixes_handle_display_destroy;
wl_display_add_destroy_listener(display, &fixes->display_destroy);
return fixes;
}

View file

@ -157,6 +157,9 @@ static void gamma_control_manager_get_gamma_control(struct wl_client *client,
}
size_t gamma_size = wlr_output_get_gamma_size(output);
if (gamma_size == 0) {
gamma_size = manager->fallback_gamma_size;
}
if (gamma_size == 0) {
zwlr_gamma_control_v1_send_failed(resource);
return;
@ -262,15 +265,24 @@ struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control(
return NULL;
}
struct wlr_color_transform *wlr_gamma_control_v1_get_color_transform(
struct wlr_gamma_control_v1 *gamma_control) {
if (gamma_control == NULL || gamma_control->table == NULL) {
return NULL;
}
const uint16_t *r = gamma_control->table;
const uint16_t *g = gamma_control->table + gamma_control->ramp_size;
const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size;
return wlr_color_transform_init_lut_3x1d(gamma_control->ramp_size, r, g, b);
}
bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control,
struct wlr_output_state *output_state) {
struct wlr_color_transform *tr = NULL;
if (gamma_control != NULL && gamma_control->table != NULL) {
const uint16_t *r = gamma_control->table;
const uint16_t *g = gamma_control->table + gamma_control->ramp_size;
const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size;
tr = wlr_color_transform_init_lut_3x1d(gamma_control->ramp_size, r, g, b);
tr = wlr_gamma_control_v1_get_color_transform(gamma_control);
if (tr == NULL) {
return false;
}

View file

@ -56,7 +56,8 @@ static void input_method_destroy(struct wlr_input_method_v2 *input_method) {
popup_surface, tmp, &input_method->popup_surfaces, link) {
popup_surface_destroy(popup_surface);
}
wl_signal_emit_mutable(&input_method->events.destroy, input_method);
wlr_input_method_keyboard_grab_v2_destroy(input_method->keyboard_grab);
wl_signal_emit_mutable(&input_method->events.destroy, NULL);
assert(wl_list_empty(&input_method->events.commit.listener_list));
assert(wl_list_empty(&input_method->events.new_popup_surface.listener_list));
@ -65,7 +66,6 @@ static void input_method_destroy(struct wlr_input_method_v2 *input_method) {
wl_list_remove(wl_resource_get_link(input_method->resource));
wl_list_remove(&input_method->seat_client_destroy.link);
wlr_input_method_keyboard_grab_v2_destroy(input_method->keyboard_grab);
input_state_reset(&input_method->pending);
input_state_reset(&input_method->current);
free(input_method);
@ -102,7 +102,7 @@ static void im_commit(struct wl_client *client, struct wl_resource *resource,
input_method->current = input_method->pending;
input_method->pending = (struct wlr_input_method_v2_state){0};
wl_signal_emit_mutable(&input_method->events.commit, input_method);
wl_signal_emit_mutable(&input_method->events.commit, NULL);
}
static void im_commit_string(struct wl_client *client,
@ -271,8 +271,7 @@ void wlr_input_method_keyboard_grab_v2_destroy(
if (!keyboard_grab) {
return;
}
wl_signal_emit_mutable(&keyboard_grab->events.destroy, keyboard_grab);
wl_signal_emit_mutable(&keyboard_grab->events.destroy, NULL);
assert(wl_list_empty(&keyboard_grab->events.destroy.listener_list));
keyboard_grab->input_method->keyboard_grab = NULL;
@ -575,7 +574,7 @@ static void manager_get_input_method(struct wl_client *client,
wl_resource_set_user_data(im_resource, input_method);
wl_list_insert(&im_manager->input_methods,
wl_resource_get_link(input_method->resource));
wl_signal_emit_mutable(&im_manager->events.input_method, input_method);
wl_signal_emit_mutable(&im_manager->events.new_input_method, input_method);
}
static void manager_destroy(struct wl_client *client,
@ -606,9 +605,9 @@ static void input_method_manager_bind(struct wl_client *wl_client, void *data,
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_input_method_manager_v2 *manager =
wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, manager);
wl_signal_emit_mutable(&manager->events.destroy, NULL);
assert(wl_list_empty(&manager->events.input_method.listener_list));
assert(wl_list_empty(&manager->events.new_input_method.listener_list));
assert(wl_list_empty(&manager->events.destroy.listener_list));
wl_list_remove(&manager->display_destroy.link);
@ -623,7 +622,7 @@ struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create(
return NULL;
}
wl_signal_init(&im_manager->events.input_method);
wl_signal_init(&im_manager->events.new_input_method);
wl_signal_init(&im_manager->events.destroy);
wl_list_init(&im_manager->input_methods);

View file

@ -26,7 +26,7 @@ struct wlr_linux_drm_syncobj_surface_v1 {
};
struct wlr_linux_drm_syncobj_surface_v1_commit {
struct wlr_linux_drm_syncobj_surface_v1 *surface;
struct wlr_surface *surface;
struct wlr_drm_syncobj_timeline_waiter waiter;
uint32_t cached_seq;
@ -192,7 +192,7 @@ static struct wlr_linux_drm_syncobj_surface_v1 *surface_from_wlr_surface(
}
static void surface_commit_destroy(struct wlr_linux_drm_syncobj_surface_v1_commit *commit) {
wlr_surface_unlock_cached(commit->surface->surface, commit->cached_seq);
wlr_surface_unlock_cached(commit->surface, commit->cached_seq);
wl_list_remove(&commit->surface_destroy.link);
wlr_drm_syncobj_timeline_waiter_finish(&commit->waiter);
free(commit);
@ -237,7 +237,7 @@ static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface
return false;
}
commit->surface = surface;
commit->surface = surface->surface;
commit->cached_seq = wlr_surface_lock_pending(surface->surface);
commit->surface_destroy.notify = surface_commit_handle_surface_destroy;

View file

@ -9,6 +9,8 @@
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include "pointer-constraints-unstable-v1-protocol.h"
static const struct zwp_locked_pointer_v1_interface locked_pointer_impl;
static const struct zwp_confined_pointer_v1_interface confined_pointer_impl;
static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl;

View file

@ -64,6 +64,7 @@ static void manager_create_transient_seat(struct wl_client *client,
wl_resource_set_implementation(seat->resource, &transient_seat_impl,
seat, transient_seat_handle_resource_destroy);
wl_list_init(&seat->seat_destroy.link);
wl_signal_emit_mutable(&manager->events.create_seat, seat);
return;

View file

@ -153,7 +153,7 @@ static void manager_handle_create_icon(struct wl_client *client, struct wl_resou
struct wl_resource *icon_resource = wl_resource_create(client,
&xdg_toplevel_icon_v1_interface, wl_resource_get_version(resource), id);
if (resource == NULL) {
if (icon_resource == NULL) {
wl_client_post_no_memory(client);
free(icon);
return;

View file

@ -0,0 +1,104 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_xdg_toplevel_tag_v1.h>
#include "xdg-toplevel-tag-v1-protocol.h"
#define MANAGER_VERSION 1
static const struct xdg_toplevel_tag_manager_v1_interface manager_impl;
static struct wlr_xdg_toplevel_tag_manager_v1 *manager_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &xdg_toplevel_tag_manager_v1_interface, &manager_impl));
return wl_resource_get_user_data(resource);
}
static void manager_handle_set_tag(struct wl_client *client, struct wl_resource *manager_resource,
struct wl_resource *toplevel_resource, const char *tag) {
struct wlr_xdg_toplevel_tag_manager_v1 *manager = manager_from_resource(manager_resource);
struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(toplevel_resource);
struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event event = {
.toplevel = toplevel,
.tag = tag,
};
wl_signal_emit_mutable(&manager->events.set_tag, &event);
}
static void manager_handle_set_description(struct wl_client *client, struct wl_resource *manager_resource,
struct wl_resource *toplevel_resource, const char *description) {
struct wlr_xdg_toplevel_tag_manager_v1 *manager = manager_from_resource(manager_resource);
struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(toplevel_resource);
struct wlr_xdg_toplevel_tag_manager_v1_set_description_event event = {
.toplevel = toplevel,
.description = description,
};
wl_signal_emit_mutable(&manager->events.set_description, &event);
}
static void manager_handle_destroy(struct wl_client *client, struct wl_resource *manager_resource) {
wl_resource_destroy(manager_resource);
}
static const struct xdg_toplevel_tag_manager_v1_interface manager_impl = {
.destroy = manager_handle_destroy,
.set_toplevel_tag = manager_handle_set_tag,
.set_toplevel_description = manager_handle_set_description,
};
static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) {
struct wlr_xdg_toplevel_tag_manager_v1 *manager = data;
struct wl_resource *resource = wl_resource_create(client,
&xdg_toplevel_tag_manager_v1_interface, version, id);
if (resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &manager_impl, manager, NULL);
}
static void manager_handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_xdg_toplevel_tag_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, NULL);
assert(wl_list_empty(&manager->events.set_tag.listener_list));
assert(wl_list_empty(&manager->events.set_description.listener_list));
assert(wl_list_empty(&manager->events.destroy.listener_list));
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
struct wlr_xdg_toplevel_tag_manager_v1 *wlr_xdg_toplevel_tag_manager_v1_create(
struct wl_display *display, uint32_t version) {
assert(version <= MANAGER_VERSION);
struct wlr_xdg_toplevel_tag_manager_v1 *manager = calloc(1, sizeof(*manager));
if (manager == NULL) {
return NULL;
}
manager->global = wl_global_create(display, &xdg_toplevel_tag_manager_v1_interface,
version, manager, manager_bind);
if (manager->global == NULL) {
free(manager);
return NULL;
}
wl_signal_init(&manager->events.set_tag);
wl_signal_init(&manager->events.set_description);
wl_signal_init(&manager->events.destroy);
manager->display_destroy.notify = manager_handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
return manager;
}

View file

@ -1,6 +1,7 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/types/wlr_data_device.h>
#include "types/wlr_xdg_shell.h"
void handle_xdg_popup_ack_configure(
@ -52,6 +53,18 @@ static void xdg_popup_grab_end(struct wlr_xdg_popup_grab *popup_grab) {
static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab,
struct wlr_surface *surface, double sx, double sy) {
struct wlr_xdg_popup_grab *popup_grab = grab->data;
struct wlr_seat *seat = grab->seat;
// Forward to drag grab if active
if (seat->drag && seat->drag->pointer_grab.interface &&
seat->drag->pointer_grab.interface->enter) {
if (grab != &seat->drag->pointer_grab) {
seat->drag->pointer_grab.interface->enter(&seat->drag->pointer_grab,
surface, sx, sy);
}
return;
}
if (wl_resource_get_client(surface->resource) == popup_grab->client) {
wlr_seat_pointer_enter(grab->seat, surface, sx, sy);
} else {
@ -65,13 +78,33 @@ static void xdg_pointer_grab_clear_focus(struct wlr_seat_pointer_grab *grab) {
static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab,
uint32_t time, double sx, double sy) {
struct wlr_seat *seat = grab->seat;
if (seat->drag && seat->drag->pointer_grab.interface &&
seat->drag->pointer_grab.interface->motion) {
if (grab != &seat->drag->pointer_grab) {
seat->drag->pointer_grab.interface->motion(&seat->drag->pointer_grab,
time, sx, sy);
}
return;
}
wlr_seat_pointer_send_motion(grab->seat, time, sx, sy);
}
static uint32_t xdg_pointer_grab_button(struct wlr_seat_pointer_grab *grab,
uint32_t time, uint32_t button, uint32_t state) {
uint32_t serial =
wlr_seat_pointer_send_button(grab->seat, time, button, state);
struct wlr_seat *seat = grab->seat;
if (seat->drag && seat->drag->pointer_grab.interface &&
seat->drag->pointer_grab.interface->button) {
if (grab != &seat->drag->pointer_grab) {
return seat->drag->pointer_grab.interface->button(&seat->drag->pointer_grab,
time, button, state);
}
}
uint32_t serial = wlr_seat_pointer_send_button(grab->seat, time, button, state);
if (serial) {
return serial;
} else {
@ -84,11 +117,32 @@ static void xdg_pointer_grab_axis(struct wlr_seat_pointer_grab *grab,
uint32_t time, enum wl_pointer_axis orientation, double value,
int32_t value_discrete, enum wl_pointer_axis_source source,
enum wl_pointer_axis_relative_direction relative_direction) {
struct wlr_seat *seat = grab->seat;
if (seat->drag && seat->drag->pointer_grab.interface &&
seat->drag->pointer_grab.interface->axis) {
if (grab != &seat->drag->pointer_grab) {
seat->drag->pointer_grab.interface->axis(&seat->drag->pointer_grab,
time, orientation, value, value_discrete, source, relative_direction);
}
return;
}
wlr_seat_pointer_send_axis(grab->seat, time, orientation, value,
value_discrete, source, relative_direction);
}
static void xdg_pointer_grab_frame(struct wlr_seat_pointer_grab *grab) {
struct wlr_seat *seat = grab->seat;
if (seat->drag && seat->drag->pointer_grab.interface &&
seat->drag->pointer_grab.interface->frame) {
if (grab != &seat->drag->pointer_grab) {
seat->drag->pointer_grab.interface->frame(&seat->drag->pointer_grab);
}
return;
}
wlr_seat_pointer_send_frame(grab->seat);
}
@ -110,6 +164,11 @@ static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab,
struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes,
const struct wlr_keyboard_modifiers *modifiers) {
// keyboard focus should remain on the popup
if (grab->seat->drag) {
return;
}
wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers);
}
static void xdg_keyboard_grab_clear_focus(struct wlr_seat_keyboard_grab *grab) {

View file

@ -19,16 +19,15 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y,
//
// In order to be consistent with e.g. wlr_box_contains_point(),
// this function returns a point inside the bottom and right edges
// of the box by at least 1/65536 of a unit (pixel). 1/65536 is
// of the box by at least 1/256 of a unit (pixel). 1/256 is
// small enough to avoid a "dead zone" with high-resolution mice
// but large enough to avoid rounding to zero (due to loss of
// significant digits) in simple floating-point calculations.
// but large enough to avoid rounding to zero in wl_fixed_from_double().
// find the closest x point
if (x < box->x) {
*dest_x = box->x;
} else if (x > box->x + box->width - 1/65536.0) {
*dest_x = box->x + box->width - 1/65536.0;
} else if (x > box->x + box->width - 1/256.0) {
*dest_x = box->x + box->width - 1/256.0;
} else {
*dest_x = x;
}
@ -36,8 +35,8 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y,
// find closest y point
if (y < box->y) {
*dest_y = box->y;
} else if (y > box->y + box->height - 1/65536.0) {
*dest_y = box->y + box->height - 1/65536.0;
} else if (y > box->y + box->height - 1/256.0) {
*dest_y = box->y + box->height - 1/256.0;
} else {
*dest_y = y;
}
@ -67,7 +66,12 @@ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a,
dest->width = x2 - x1;
dest->height = y2 - y1;
return !wlr_box_empty(dest);
if (wlr_box_empty(dest)) {
*dest = (struct wlr_box){0};
return false;
}
return true;
}
bool wlr_box_contains_point(const struct wlr_box *box, double x, double y) {

16
util/mem.c Normal file
View file

@ -0,0 +1,16 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "util/mem.h"
bool memdup(void *out, const void *src, size_t size) {
void *dst = malloc(size);
if (dst == NULL) {
return false;
}
memcpy(dst, src, size);
void **dst_ptr = out;
*dst_ptr = dst;
return true;
}

View file

@ -6,6 +6,7 @@ wlr_files += files(
'global.c',
'log.c',
'matrix.c',
'mem.c',
'rect_union.c',
'region.c',
'set.c',

View file

@ -42,6 +42,9 @@ static void handle_server_start(struct wl_listener *listener, void *data) {
static void xwayland_mark_ready(struct wlr_xwayland *xwayland) {
assert(xwayland->server->wm_fd[0] >= 0);
xwayland->xwm = xwm_create(xwayland, xwayland->server->wm_fd[0]);
// xwm_create takes ownership of wm_fd[0] under all circumstances
xwayland->server->wm_fd[0] = -1;
if (!xwayland->xwm) {
return;
}
@ -69,6 +72,11 @@ static void handle_shell_destroy(struct wl_listener *listener, void *data) {
struct wlr_xwayland *xwayland =
wl_container_of(listener, xwayland, shell_destroy);
xwayland->shell_v1 = NULL;
wl_list_remove(&xwayland->shell_destroy.link);
// Will remove this list in handle_shell_destroy().
// This ensures the link is always initialized and
// avoids the need to keep check conditions in sync.
wl_list_init(&xwayland->shell_destroy.link);
}
void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) {

View file

@ -34,6 +34,7 @@ static const char *const atom_map[ATOM_LAST] = {
[NET_WM_STATE] = "_NET_WM_STATE",
[NET_WM_STRUT_PARTIAL] = "_NET_WM_STRUT_PARTIAL",
[NET_WM_WINDOW_TYPE] = "_NET_WM_WINDOW_TYPE",
[NET_WM_ICON] = "_NET_WM_ICON",
[WM_TAKE_FOCUS] = "WM_TAKE_FOCUS",
[WINDOW] = "WINDOW",
[NET_ACTIVE_WINDOW] = "_NET_ACTIVE_WINDOW",
@ -241,6 +242,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create(
wl_signal_init(&surface->events.set_override_redirect);
wl_signal_init(&surface->events.set_geometry);
wl_signal_init(&surface->events.set_opacity);
wl_signal_init(&surface->events.set_icon);
wl_signal_init(&surface->events.focus_in);
wl_signal_init(&surface->events.grab_focus);
wl_signal_init(&surface->events.map_request);
@ -602,6 +604,7 @@ static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) {
assert(wl_list_empty(&xsurface->events.set_override_redirect.listener_list));
assert(wl_list_empty(&xsurface->events.set_geometry.listener_list));
assert(wl_list_empty(&xsurface->events.set_opacity.listener_list));
assert(wl_list_empty(&xsurface->events.set_icon.listener_list));
assert(wl_list_empty(&xsurface->events.focus_in.listener_list));
assert(wl_list_empty(&xsurface->events.grab_focus.listener_list));
assert(wl_list_empty(&xsurface->events.map_request.listener_list));
@ -1078,6 +1081,8 @@ static void read_surface_property(struct wlr_xwm *xwm,
// intentionally ignored
} else if (property == xwm->atoms[NET_WM_WINDOW_TYPE]) {
read_surface_window_type(xwm, xsurface, reply);
} else if (property == xwm->atoms[NET_WM_ICON]) {
wl_signal_emit_mutable(&xsurface->events.set_icon, NULL);
} else if (property == xwm->atoms[WM_PROTOCOLS]) {
read_surface_protocols(xwm, xsurface, reply);
} else if (property == xwm->atoms[NET_WM_STATE]) {
@ -1131,6 +1136,38 @@ static const struct wlr_addon_interface surface_addon_impl = {
.destroy = xwayland_surface_handle_addon_destroy,
};
bool wlr_xwayland_surface_fetch_icon(
const struct wlr_xwayland_surface *xsurface,
xcb_ewmh_get_wm_icon_reply_t *icon_reply) {
struct wlr_xwm *xwm = xsurface->xwm;
xcb_get_property_cookie_t cookie = xcb_get_property(xwm->xcb_conn, 0,
xsurface->window_id, xwm->atoms[NET_WM_ICON], XCB_ATOM_CARDINAL,
0, UINT32_MAX);
xcb_get_property_reply_t *reply =
xcb_get_property_reply(xwm->xcb_conn, cookie, NULL);
if (!reply) {
return false;
}
if (!xcb_ewmh_get_wm_icon_from_reply(icon_reply, reply)) {
free(reply);
return false;
}
return true;
}
static xcb_get_property_cookie_t get_property(struct wlr_xwm *xwm,
xcb_window_t window_id, xcb_atom_t atom) {
uint32_t len = 2048;
if (atom == xwm->atoms[NET_WM_ICON]) {
/* Compositors need to fetch icon data wlr_xwayland_surface_fetch_icon() */
len = 0;
}
return xcb_get_property(xwm->xcb_conn, 0, window_id, atom, XCB_ATOM_ANY, 0, len);
}
static void xwayland_surface_associate(struct wlr_xwm *xwm,
struct wlr_xwayland_surface *xsurface, struct wlr_surface *surface) {
assert(xsurface->surface == NULL);
@ -1165,12 +1202,12 @@ static void xwayland_surface_associate(struct wlr_xwm *xwm,
xwm->atoms[NET_WM_STRUT_PARTIAL],
xwm->atoms[NET_WM_WINDOW_TYPE],
xwm->atoms[NET_WM_NAME],
xwm->atoms[NET_WM_ICON],
};
xcb_get_property_cookie_t cookies[sizeof(props) / sizeof(props[0])] = {0};
for (size_t i = 0; i < sizeof(props) / sizeof(props[0]); i++) {
cookies[i] = xcb_get_property(xwm->xcb_conn, 0, xsurface->window_id,
props[i], XCB_ATOM_ANY, 0, 2048);
cookies[i] = get_property(xwm, xsurface->window_id, props[i]);
}
for (size_t i = 0; i < sizeof(props) / sizeof(props[0]); i++) {
@ -1398,8 +1435,7 @@ static void xwm_handle_property_notify(struct wlr_xwm *xwm,
return;
}
xcb_get_property_cookie_t cookie =
xcb_get_property(xwm->xcb_conn, 0, xsurface->window_id, ev->atom, XCB_ATOM_ANY, 0, 2048);
xcb_get_property_cookie_t cookie = get_property(xwm, xsurface->window_id, ev->atom);
xcb_get_property_reply_t *reply =
xcb_get_property_reply(xwm->xcb_conn, cookie, NULL);
if (reply == NULL) {
@ -2494,6 +2530,7 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride,
struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
struct wlr_xwm *xwm = calloc(1, sizeof(*xwm));
if (xwm == NULL) {
close(wm_fd);
return NULL;
}
@ -2508,11 +2545,13 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
xwm->ping_timeout = 10000;
// xcb_connect_to_fd takes ownership of the FD regardless of success/failure
xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL);
int rc = xcb_connection_has_error(xwm->xcb_conn);
if (rc) {
wlr_log(WLR_ERROR, "xcb connect failed: %d", rc);
xcb_disconnect(xwm->xcb_conn);
free(xwm);
return NULL;
}