/* * Copyright © 2011-2014 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of the * copyright holders not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include #include #include #include #include "os/log_priv.h" #include "xwayland-dmabuf.h" #include "xwayland-glamor-gbm.h" #include "xwayland-screen.h" #include "xwayland-types.h" #include "xwayland-window-buffers.h" #include "drm-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" void xwl_device_formats_destroy(struct xwl_device_formats *dev_formats) { for (int j = 0; j < dev_formats->num_formats; j++) free(dev_formats->formats[j].modifiers); free(dev_formats->formats); drmFreeDevice(&dev_formats->drm_dev); } void xwl_dmabuf_feedback_clear_dev_formats(struct xwl_dmabuf_feedback *xwl_feedback) { if (xwl_feedback->dev_formats_len == 0) return; for (int i = 0; i < xwl_feedback->dev_formats_len; i++) { struct xwl_device_formats *dev_format = &xwl_feedback->dev_formats[i]; xwl_device_formats_destroy(dev_format); } free(xwl_feedback->dev_formats); xwl_feedback->dev_formats = NULL; xwl_feedback->dev_formats_len = 0; } void xwl_dmabuf_feedback_destroy(struct xwl_dmabuf_feedback *xwl_feedback) { munmap(xwl_feedback->format_table.entry, xwl_feedback->format_table.len * sizeof(struct xwl_format_table_entry)); xwl_dmabuf_feedback_clear_dev_formats(xwl_feedback); if (xwl_feedback->dmabuf_feedback) zwp_linux_dmabuf_feedback_v1_destroy(xwl_feedback->dmabuf_feedback); xwl_feedback->dmabuf_feedback = NULL; drmFreeDevice(&xwl_feedback->main_dev); } static Bool xwl_glamor_is_modifier_supported_in_formats(struct xwl_format *formats, int num_formats, uint32_t format, uint64_t modifier) { struct xwl_format *xwl_format = NULL; int i; for (i = 0; i < num_formats; i++) { if (formats[i].format == format) { xwl_format = &formats[i]; break; } } if (xwl_format) { for (i = 0; i < xwl_format->num_modifiers; i++) { if (xwl_format->modifiers[i] == modifier) { return TRUE; } } } return FALSE; } static Bool xwl_feedback_is_modifier_supported(struct xwl_dmabuf_feedback *xwl_feedback, uint32_t format, uint64_t modifier, int supports_scanout) { for (int i = 0; i < xwl_feedback->dev_formats_len; i++) { struct xwl_device_formats *dev_formats = &xwl_feedback->dev_formats[i]; if (supports_scanout && !dev_formats->supports_scanout) continue; if (xwl_glamor_is_modifier_supported_in_formats(dev_formats->formats, dev_formats->num_formats, format, modifier)) return TRUE; } return FALSE; } Bool xwl_glamor_is_modifier_supported(struct xwl_screen *xwl_screen, uint32_t format, uint64_t modifier) { struct xwl_window *xwl_window; /* * If we are using dmabuf v4, then we need to check in the main * device and per-window format lists. For older protocol * versions we can just check the list returned by the dmabuf.modifier * events in xwl_screen */ if (xwl_screen->dmabuf_protocol_version < 4) { return xwl_glamor_is_modifier_supported_in_formats(xwl_screen->formats, xwl_screen->num_formats, format, modifier); } if (xwl_feedback_is_modifier_supported(&xwl_screen->default_feedback, format, modifier, FALSE)) return TRUE; xorg_list_for_each_entry(xwl_window, &xwl_screen->window_list, link_window) { if (xwl_feedback_is_modifier_supported(&xwl_window->feedback, format, modifier, FALSE)) return TRUE; } return FALSE; } uint32_t wl_drm_format_for_depth(int depth) { switch (depth) { case 15: return WL_DRM_FORMAT_XRGB1555; case 16: return WL_DRM_FORMAT_RGB565; case 24: return WL_DRM_FORMAT_XRGB8888; case 30: return WL_DRM_FORMAT_ARGB2101010; default: ErrorF("unexpected depth: %d\n", depth); case 32: return WL_DRM_FORMAT_ARGB8888; } } static Bool xwl_dmabuf_get_formats(struct xwl_format *format_array, int format_array_len, CARD32 *num_formats, CARD32 **formats) { *num_formats = 0; *formats = NULL; if (format_array_len == 0) return TRUE; *formats = calloc(format_array_len, sizeof(CARD32)); if (*formats == NULL) return FALSE; for (int i = 0; i < format_array_len; i++) (*formats)[i] = format_array[i].format; *num_formats = format_array_len; return TRUE; } static Bool xwl_dmabuf_get_formats_for_device(struct xwl_dmabuf_feedback *xwl_feedback, drmDevice *device, CARD32 *num_formats, CARD32 **formats) { CARD32 *ret = NULL; uint32_t count = 0; /* go through all matching sets of tranches for the window's device */ for (int i = 0; i < xwl_feedback->dev_formats_len; i++) { if (drmDevicesEqual(xwl_feedback->dev_formats[i].drm_dev, device)) { struct xwl_device_formats *dev_formats = &xwl_feedback->dev_formats[i]; /* Append the formats from this tranche to the list */ ret = XNFreallocarray(ret, count + dev_formats->num_formats, sizeof(CARD32)); for (int j = 0; j < dev_formats->num_formats; j++) { Bool found = FALSE; /* Check if this format is already present in the list */ for (int k = 0; k < count; k++) { if (ret[k] == dev_formats->formats[j].format) { found = TRUE; break; } } /* If this format has not yet been added, do so now */ if (!found) ret[count++] = dev_formats->formats[j].format; } } } *num_formats = count; *formats = ret; return TRUE; } Bool xwl_glamor_get_formats(ScreenPtr screen, CARD32 *num_formats, CARD32 **formats) { struct xwl_screen *xwl_screen = xwl_screen_get(screen); /* Explicitly zero the count as the caller may ignore the return value */ *num_formats = 0; if (!xwl_screen->dmabuf) return FALSE; if (xwl_screen->dmabuf_protocol_version >= 4) { drmDevice *main_dev = xwl_gbm_get_main_device(xwl_screen); return xwl_dmabuf_get_formats_for_device(&xwl_screen->default_feedback, main_dev, num_formats, formats); } return xwl_dmabuf_get_formats(xwl_screen->formats, xwl_screen->num_formats, num_formats, formats); } static Bool xwl_dmabuf_get_modifiers_for_format(struct xwl_format *format_array, int num_formats, uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers) { struct xwl_format *xwl_format = NULL; int i; *num_modifiers = 0; *modifiers = NULL; if (num_formats == 0) return TRUE; for (i = 0; i < num_formats; i++) { if (format_array[i].format == format) { xwl_format = &format_array[i]; break; } } if (!xwl_format || (xwl_format->num_modifiers == 1 && xwl_format->modifiers[0] == DRM_FORMAT_MOD_INVALID)) return FALSE; *modifiers = calloc(xwl_format->num_modifiers, sizeof(uint64_t)); if (*modifiers == NULL) return FALSE; for (i = 0; i < xwl_format->num_modifiers; i++) (*modifiers)[i] = xwl_format->modifiers[i]; *num_modifiers = xwl_format->num_modifiers; return TRUE; } static Bool xwl_dmabuf_get_modifiers_for_device(struct xwl_dmabuf_feedback *feedback, drmDevice *device, uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers, Bool *supports_scanout) { /* Now try to find a matching set of tranches for the window's device */ for (int i = 0; i < feedback->dev_formats_len; i++) { struct xwl_device_formats *dev_formats = &feedback->dev_formats[i]; if (drmDevicesEqual(dev_formats->drm_dev, device) && xwl_dmabuf_get_modifiers_for_format(dev_formats->formats, dev_formats->num_formats, format, num_modifiers, modifiers)) { if (supports_scanout) *supports_scanout = !!dev_formats->supports_scanout; return TRUE; } } return FALSE; } Bool xwl_glamor_get_modifiers(ScreenPtr screen, uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers) { struct xwl_screen *xwl_screen = xwl_screen_get(screen); drmDevice *main_dev; /* Explicitly zero the count as the caller may ignore the return value */ *num_modifiers = 0; *modifiers = NULL; if (!xwl_screen->dmabuf) return FALSE; if (xwl_screen->dmabuf_protocol_version >= 4) { main_dev = xwl_gbm_get_main_device(xwl_screen); return xwl_dmabuf_get_modifiers_for_device(&xwl_screen->default_feedback, main_dev, format, num_modifiers, modifiers, NULL); } else { return xwl_dmabuf_get_modifiers_for_format(xwl_screen->formats, xwl_screen->num_formats, format, num_modifiers, modifiers); } } Bool xwl_glamor_get_drawable_modifiers_and_scanout(DrawablePtr drawable, uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers, Bool *supports_scanout) { struct xwl_screen *xwl_screen = xwl_screen_get(drawable->pScreen); struct xwl_window *xwl_window; drmDevice *main_dev; *num_modifiers = 0; *modifiers = NULL; if (supports_scanout) *supports_scanout = FALSE; /* We can only return per-drawable modifiers if the compositor supports feedback */ if (xwl_screen->dmabuf_protocol_version < 4) return TRUE; if (drawable->type != DRAWABLE_WINDOW || !xwl_screen->dmabuf) return FALSE; xwl_window = xwl_window_from_window((WindowPtr)drawable); /* couldn't find drawable for window */ if (!xwl_window) return FALSE; main_dev = xwl_gbm_get_main_device(xwl_screen); return xwl_dmabuf_get_modifiers_for_device(&xwl_window->feedback, main_dev, format, num_modifiers, modifiers, supports_scanout); } Bool xwl_glamor_get_drawable_modifiers(DrawablePtr drawable, uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers) { return xwl_glamor_get_drawable_modifiers_and_scanout(drawable, format, num_modifiers, modifiers, NULL); } static void xwl_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf, uint32_t format) { } static void xwl_add_format_and_mod_to_list(struct xwl_format **formats, uint32_t *num_formats, uint32_t format, uint64_t modifier) { struct xwl_format *xwl_format = NULL; int i; for (i = 0; i < *num_formats; i++) { if ((*formats)[i].format == format) { xwl_format = &(*formats)[i]; break; } } if (xwl_format == NULL) { (*num_formats)++; *formats = XNFrealloc(*formats, *num_formats * sizeof(*xwl_format)); xwl_format = &(*formats)[*num_formats - 1]; xwl_format->format = format; xwl_format->num_modifiers = 0; xwl_format->modifiers = NULL; } for (i = 0; i < xwl_format->num_modifiers; i++) { /* don't add it if the modifier already exists */ if (xwl_format->modifiers[i] == modifier) return; } xwl_format->num_modifiers++; xwl_format->modifiers = XNFrealloc(xwl_format->modifiers, xwl_format->num_modifiers * sizeof(uint64_t)); xwl_format->modifiers[xwl_format->num_modifiers - 1] = modifier; } static void xwl_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) { struct xwl_screen *xwl_screen = data; xwl_add_format_and_mod_to_list(&xwl_screen->formats, &xwl_screen->num_formats, format, ((uint64_t)modifier_hi << 32 | (uint64_t)modifier_lo)); } static const struct zwp_linux_dmabuf_v1_listener xwl_dmabuf_listener = { .format = xwl_dmabuf_handle_format, .modifier = xwl_dmabuf_handle_modifier }; /* * We need to check if the compositor is resending all of the tranche * information. Each tranche event will call this method to see * if the existing format info should be cleared before refilling. */ static void xwl_check_reset_tranche_info(struct xwl_dmabuf_feedback *xwl_feedback) { if (!xwl_feedback->feedback_done) return; xwl_feedback->feedback_done = FALSE; xwl_dmabuf_feedback_clear_dev_formats(xwl_feedback); } static void xwl_dmabuf_feedback_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *dev) { struct xwl_dmabuf_feedback *xwl_feedback = data; dev_t devid; xwl_check_reset_tranche_info(xwl_feedback); assert(dev->size == sizeof(dev_t)); memcpy(&devid, dev->data, sizeof(dev_t)); drmFreeDevice(&xwl_feedback->main_dev); if (drmGetDeviceFromDevId(devid, 0, &xwl_feedback->main_dev) != 0) ErrorF("linux_dmabuf_feedback.main_device: Failed to fetch DRM device\n"); } static void xwl_dmabuf_feedback_tranche_target_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *dev) { struct xwl_dmabuf_feedback *xwl_feedback = data; dev_t devid; xwl_check_reset_tranche_info(xwl_feedback); assert(dev->size == sizeof(dev_t)); memcpy(&devid, dev->data, sizeof(dev_t)); if (drmGetDeviceFromDevId(devid, 0, &xwl_feedback->tmp_tranche.drm_dev) != 0) ErrorF("linux_dmabuf_feedback.tranche_target_device: Failed to fetch DRM device\n"); } static void xwl_dmabuf_feedback_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, uint32_t flags) { struct xwl_dmabuf_feedback *xwl_feedback = data; xwl_check_reset_tranche_info(xwl_feedback); if (flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT) xwl_feedback->tmp_tranche.supports_scanout = TRUE; } static void xwl_dmabuf_feedback_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *indices) { struct xwl_dmabuf_feedback *xwl_feedback = data; struct xwl_device_formats *tranche = &xwl_feedback->tmp_tranche; uint16_t *index; xwl_check_reset_tranche_info(xwl_feedback); wl_array_for_each(index, indices) { if (*index >= xwl_feedback->format_table.len) { ErrorF("linux_dmabuf_feedback.tranche_formats: Index given to us by the compositor" " is too large to fit in the format table\n"); continue; } /* Look up this format/mod in the format table */ struct xwl_format_table_entry *entry = &xwl_feedback->format_table.entry[*index]; /* Add it to the in-progress tranche */ xwl_add_format_and_mod_to_list(&tranche->formats, &tranche->num_formats, entry->format, entry->modifier); } } static void xwl_append_to_tranche(struct xwl_device_formats *dst, struct xwl_device_formats *src) { struct xwl_format *format; for (int i = 0; i < src->num_formats; i++) { format = &src->formats[i]; for (int j = 0; j < format->num_modifiers; j++) xwl_add_format_and_mod_to_list(&dst->formats, &dst->num_formats, format->format, format->modifiers[j]); } } static void xwl_dmabuf_feedback_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) { struct xwl_dmabuf_feedback *xwl_feedback = data; struct xwl_device_formats *tranche; int appended = FALSE; /* * No need to call xwl_check_reset_tranche_info, the other events should have been * triggered first */ if (xwl_feedback->tmp_tranche.drm_dev == NULL) { xwl_device_formats_destroy(&xwl_feedback->tmp_tranche); goto out; } /* * First check if there is an existing tranche for this device+flags combo. We * will combine it with this tranche, since we can only send one modifier list * in DRI3 but the compositor may report multiple tranches per device (KDE * does this) */ for (int i = 0; i < xwl_feedback->dev_formats_len; i++) { tranche = &xwl_feedback->dev_formats[i]; if (tranche->supports_scanout == xwl_feedback->tmp_tranche.supports_scanout && drmDevicesEqual(tranche->drm_dev, xwl_feedback->tmp_tranche.drm_dev)) { appended = TRUE; /* Add all format/mods to this tranche */ xwl_append_to_tranche(tranche, &xwl_feedback->tmp_tranche); /* Now free our temp tranche's allocations */ xwl_device_formats_destroy(&xwl_feedback->tmp_tranche); break; } } if (!appended) { xwl_feedback->dev_formats_len++; xwl_feedback->dev_formats = XNFrealloc(xwl_feedback->dev_formats, sizeof(struct xwl_device_formats) * xwl_feedback->dev_formats_len); /* copy the temporary tranche into the official array */ memcpy(&xwl_feedback->dev_formats[xwl_feedback->dev_formats_len - 1], &xwl_feedback->tmp_tranche, sizeof(struct xwl_device_formats)); } out: /* reset the tranche */ memset(&xwl_feedback->tmp_tranche, 0, sizeof(struct xwl_device_formats)); } static void xwl_dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) { struct xwl_dmabuf_feedback *xwl_feedback = data; xwl_feedback->feedback_done = TRUE; xwl_feedback->unprocessed_feedback_pending = TRUE; } static void xwl_dmabuf_feedback_format_table(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, int32_t fd, uint32_t size) { struct xwl_dmabuf_feedback *xwl_feedback = data; /* Unmap the old table */ if (xwl_feedback->format_table.entry) { munmap(xwl_feedback->format_table.entry, xwl_feedback->format_table.len * sizeof(struct xwl_format_table_entry)); } assert(size % sizeof(struct xwl_format_table_entry) == 0); xwl_feedback->format_table.len = size / sizeof(struct xwl_format_table_entry); xwl_feedback->format_table.entry = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); if (xwl_feedback->format_table.entry == MAP_FAILED) { ErrorF("linux_dmabuf_feedback.format_table: Could not map the format" " table: Compositor bug or out of resources\n"); xwl_feedback->format_table.len = 0; } } static const struct zwp_linux_dmabuf_feedback_v1_listener xwl_dmabuf_feedback_listener = { .done = xwl_dmabuf_feedback_done, .format_table = xwl_dmabuf_feedback_format_table, .main_device = xwl_dmabuf_feedback_main_device, .tranche_done = xwl_dmabuf_feedback_tranche_done, .tranche_target_device = xwl_dmabuf_feedback_tranche_target_device, .tranche_formats = xwl_dmabuf_feedback_tranche_formats, .tranche_flags = xwl_dmabuf_feedback_tranche_flags, }; Bool xwl_screen_set_dmabuf_interface(struct xwl_screen *xwl_screen, uint32_t id, uint32_t version) { /* We either support versions 3 or 4. 4 is needed for dmabuf feedback */ int supported_version = version >= 4 ? 4 : 3; if (version < 3) return FALSE; xwl_screen->dmabuf = wl_registry_bind(xwl_screen->registry, id, &zwp_linux_dmabuf_v1_interface, supported_version); xwl_screen->dmabuf_protocol_version = supported_version; zwp_linux_dmabuf_v1_add_listener(xwl_screen->dmabuf, &xwl_dmabuf_listener, xwl_screen); /* If the compositor supports it, request the default feedback hints */ if (version >= 4) { xwl_screen->default_feedback.dmabuf_feedback = zwp_linux_dmabuf_v1_get_default_feedback(xwl_screen->dmabuf); if (!xwl_screen->default_feedback.dmabuf_feedback) return FALSE; zwp_linux_dmabuf_feedback_v1_add_listener(xwl_screen->default_feedback.dmabuf_feedback, &xwl_dmabuf_feedback_listener, &xwl_screen->default_feedback); } return TRUE; } static void xwl_window_dmabuf_feedback_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *dev) { struct xwl_window *xwl_window = data; xwl_dmabuf_feedback_main_device(&xwl_window->feedback, dmabuf_feedback, dev); } static void xwl_window_dmabuf_feedback_tranche_target_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *dev) { struct xwl_window *xwl_window = data; xwl_dmabuf_feedback_tranche_target_device(&xwl_window->feedback, dmabuf_feedback, dev); } static void xwl_window_dmabuf_feedback_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, uint32_t flags) { struct xwl_window *xwl_window = data; xwl_dmabuf_feedback_tranche_flags(&xwl_window->feedback, dmabuf_feedback, flags); } static void xwl_window_dmabuf_feedback_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *indices) { struct xwl_window *xwl_window = data; xwl_dmabuf_feedback_tranche_formats(&xwl_window->feedback, dmabuf_feedback, indices); } static void xwl_window_dmabuf_feedback_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) { struct xwl_window *xwl_window = data; xwl_dmabuf_feedback_tranche_done(&xwl_window->feedback, dmabuf_feedback); } static void xwl_window_dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) { struct xwl_window *xwl_window = data; uint32_t format = wl_drm_format_for_depth(xwl_window->surface_window->drawable.depth); xwl_dmabuf_feedback_done(&xwl_window->feedback, dmabuf_feedback); xwl_window->has_implicit_scanout_support = xwl_feedback_is_modifier_supported(&xwl_window->feedback, format, DRM_FORMAT_MOD_INVALID, TRUE); DebugF("XWAYLAND: Window 0x%x can%s get implicit scanout support\n", xwl_window->surface_window->drawable.id, xwl_window->has_implicit_scanout_support ? "" : "not"); /* If the linux-dmabuf v4 per-surface feedback changed, make sure the * window buffers get re-created with appropriate parameters. */ xwl_window_buffers_dispose(xwl_window, FALSE); xwl_window_realloc_pixmap(xwl_window); } static void xwl_window_dmabuf_feedback_format_table(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, int32_t fd, uint32_t size) { struct xwl_window *xwl_window = data; xwl_dmabuf_feedback_format_table(&xwl_window->feedback, dmabuf_feedback, fd, size); } static const struct zwp_linux_dmabuf_feedback_v1_listener xwl_window_dmabuf_feedback_listener = { .done = xwl_window_dmabuf_feedback_done, .format_table = xwl_window_dmabuf_feedback_format_table, .main_device = xwl_window_dmabuf_feedback_main_device, .tranche_done = xwl_window_dmabuf_feedback_tranche_done, .tranche_target_device = xwl_window_dmabuf_feedback_tranche_target_device, .tranche_formats = xwl_window_dmabuf_feedback_tranche_formats, .tranche_flags = xwl_window_dmabuf_feedback_tranche_flags, }; Bool xwl_dmabuf_setup_feedback_for_window(struct xwl_window *xwl_window) { struct xwl_screen *xwl_screen = xwl_window->xwl_screen; xwl_window->feedback.dmabuf_feedback = zwp_linux_dmabuf_v1_get_surface_feedback(xwl_screen->dmabuf, xwl_window->surface); if (!xwl_window->feedback.dmabuf_feedback) return FALSE; zwp_linux_dmabuf_feedback_v1_add_listener(xwl_window->feedback.dmabuf_feedback, &xwl_window_dmabuf_feedback_listener, xwl_window); return TRUE; }