Compare commits

...

14 commits

Author SHA1 Message Date
Xu Rui
c7ae497eab Merge branch 'xwayland' into 'master'
xwayland: don't ping if client not supports

See merge request wlroots/wlroots!5103
2025-10-28 07:08:48 +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
xurui
18c707d90a xwayland: don't ping if client not supports
Signed-off-by: xurui <xurui@kylinos.cn>
2025-07-04 17:15:27 +08:00
18 changed files with 310 additions and 64 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

@ -519,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) {
@ -536,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;
}
}
}
@ -557,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

@ -9,6 +9,7 @@ enum wlr_color_transform_type {
COLOR_TRANSFORM_INVERSE_EOTF,
COLOR_TRANSFORM_LCMS2,
COLOR_TRANSFORM_LUT_3X1D,
COLOR_TRANSFORM_PIPELINE,
};
struct wlr_color_transform {
@ -39,6 +40,13 @@ 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);
@ -72,12 +80,6 @@ struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_b
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

@ -141,6 +141,13 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf(
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.
*/
@ -152,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

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

@ -267,6 +267,7 @@ 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;
};

View file

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

View file

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

@ -62,6 +62,33 @@ 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_INVERSE_EOTF:
@ -73,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);
@ -108,6 +143,65 @@ 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) {
i = len - 1;
@ -125,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) {

View file

@ -964,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_INVERSE_EOTF:
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;
@ -1074,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];

View file

@ -242,6 +242,15 @@ static void output_apply_state(struct wlr_output *output,
}
}
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);
@ -407,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);
@ -515,8 +525,7 @@ const struct wlr_output_image_description *output_pending_image_description(
* 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) {
@ -562,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;
}

View file

@ -1530,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);
}
@ -1547,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;
}
}
@ -1766,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);
}
@ -2104,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;
}
@ -2122,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};
@ -2145,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,
@ -2245,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);
}
@ -2319,6 +2371,17 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
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){
@ -2441,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

@ -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,6 +56,7 @@ 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);
}
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));
@ -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);
@ -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;

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

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

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

View file

@ -2530,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;
}
@ -2544,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;
}
@ -2745,6 +2748,22 @@ bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms,
}
void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface) {
if (surface->pinging) {
return;
}
// don't ping if client not supports
bool supports_ping = false;
for(size_t i = 0; i < surface->protocols_len; i++) {
if (surface->protocols[i] == surface->xwm->atoms[NET_WM_PING]) {
supports_ping = true;
break;
}
}
if (!supports_ping) {
return;
}
xcb_client_message_data_t data = { 0 };
data.data32[0] = surface->xwm->atoms[NET_WM_PING];
data.data32[1] = XCB_CURRENT_TIME;