diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 8457ed585..fae04ab31 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -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 ] diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 86b52c684..732ab4a9f 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -612,9 +612,6 @@ static void drm_connector_rollback_commit(const struct wlr_drm_connector_state * static bool drm_commit(struct wlr_drm_backend *drm, const struct wlr_drm_device_state *state, uint32_t flags, bool test_only) { - // Disallow atomic-only flags - assert((flags & ~DRM_MODE_PAGE_FLIP_FLAGS) == 0); - struct wlr_drm_page_flip *page_flip = NULL; if (flags & DRM_MODE_PAGE_FLIP_EVENT) { page_flip = drm_page_flip_create(drm, state); @@ -1922,6 +1919,7 @@ bool commit_drm_device(struct wlr_drm_backend *drm, bool ok = false; bool modeset = false; size_t conn_states_len = 0; + bool nonblock = drm->iface == &atomic_iface; for (size_t i = 0; i < output_states_len; i++) { const struct wlr_backend_output_state *output_state = &output_states[i]; struct wlr_output *output = output_state->output; @@ -1932,6 +1930,9 @@ bool commit_drm_device(struct wlr_drm_backend *drm, } struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + if (!test_only && conn->pending_page_flip != NULL) { + nonblock = false; + } if (output_pending_enabled(output, &output_state->base) && !drm_connector_alloc_crtc(conn)) { wlr_drm_conn_log(conn, WLR_DEBUG, @@ -1966,6 +1967,9 @@ bool commit_drm_device(struct wlr_drm_backend *drm, if (!test_only) { flags |= DRM_MODE_PAGE_FLIP_EVENT; } + if (nonblock) { + flags |= DRM_MODE_ATOMIC_NONBLOCK; + } struct wlr_drm_device_state dev_state = { .modeset = modeset, .connectors = conn_states, diff --git a/backend/session/session.c b/backend/session/session.c index 48f4ab187..32522fb42 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -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; diff --git a/include/render/color.h b/include/render/color.h index 8730ac6e9..128b345e1 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -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. */ diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index d906e3425..bc3baf181 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -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 diff --git a/include/wlr/types/wlr_gamma_control_v1.h b/include/wlr/types/wlr_gamma_control_v1.h index b3a70f11e..7a6df98a9 100644 --- a/include/wlr/types/wlr_gamma_control_v1.h +++ b/include/wlr/types/wlr_gamma_control_v1.h @@ -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 diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 6a0dd0455..2ae11a4d3 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -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; }; diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 58794e8db..9658a02b4 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -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; diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index e460bbb63..73f440d29 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -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); diff --git a/render/color.c b/render/color.c index da2f938f8..0a1a67be3 100644 --- a/render/color.c +++ b/render/color.c @@ -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) { diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 9e212aacc..398ee2104 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -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]; diff --git a/types/output/output.c b/types/output/output.c index 1fb0347a0..ca2e55538 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -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; } diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index a06b641e3..c5cbcd29f 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -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; } diff --git a/types/wlr_gamma_control_v1.c b/types/wlr_gamma_control_v1.c index 207d1977f..42d14799d 100644 --- a/types/wlr_gamma_control_v1.c +++ b/types/wlr_gamma_control_v1.c @@ -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; } diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index 0a521df48..a9501654c 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -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; diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index 7873a09c4..988d44e01 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -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; diff --git a/util/box.c b/util/box.c index a9b42579a..aae09888f 100644 --- a/util/box.c +++ b/util/box.c @@ -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; } diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 5d51df074..3aa47bac2 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -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; } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index a82e8b145..2bb4e4c64 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -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; }